From e39aaa44baebb4a4d7a613b1177c56c21bc22588 Mon Sep 17 00:00:00 2001 From: Samuel Chassot Date: Mon, 5 Feb 2024 11:31:26 +0100 Subject: [PATCH 01/78] add sbt project with plugin --- lexers/regex/ListUtils.scala | 551 ----------- lexers/regex/VerifiedLexer.scala | 1505 ------------------------------ lexers/regex/VerifiedRegex.scala | 1119 ---------------------- 3 files changed, 3175 deletions(-) delete mode 100644 lexers/regex/ListUtils.scala delete mode 100644 lexers/regex/VerifiedLexer.scala delete mode 100644 lexers/regex/VerifiedRegex.scala diff --git a/lexers/regex/ListUtils.scala b/lexers/regex/ListUtils.scala deleted file mode 100644 index 16ef68c2..00000000 --- a/lexers/regex/ListUtils.scala +++ /dev/null @@ -1,551 +0,0 @@ -/** Author: Samuel Chassot - */ - -import stainless.annotation._ -import stainless.collection._ -import stainless.equations._ -import stainless.lang._ -import stainless.proof.check -import scala.annotation.tailrec -import stainless.lang.StaticChecks._ - -object ListUtils { - def isPrefix[B](prefix: List[B], l: List[B]): Boolean = { - (prefix, l) match { - case (Nil(), _) => true - case (_, Nil()) => false - case (l1, l2) => if (l1.head == l2.head) isPrefix(l1.tail, l2.tail) else false - } - } ensuring (res => if (res) l.size >= prefix.size else true) - - def removeLast[B](l: List[B]): List[B] = { - require(!l.isEmpty) - val res: List[B] = l match { - case Cons(hd, Nil()) => Nil() - case Cons(hd, tl) => Cons(hd, removeLast(tl)) - } - res - } ensuring (res => res ++ List(l.last) == l) - - def reverseList[B](l: List[B]): List[B] = { - decreases(l) - l match { - case Cons(hd, tl) => reverseList(tl) ++ List(hd) - case Nil() => Nil() - } - } - - def getSuffix[B](l: List[B], p: List[B]): List[B] = { - require(l.size >= p.size) - require(isPrefix(p, l)) - p match { - case Cons(hd, tl) => getSuffix(l.tail, tl) - case Nil() => l - } - } ensuring (res => p ++ res == l) - - def getIndex[B](l: List[B], e: B): BigInt = { - require(l.contains(e)) - decreases(l) - l match { - case Cons(hd, tl) if hd == e => BigInt(0) - case Cons(hd, tl) if hd != e => 1 + getIndex(tl, e) - case Nil() => BigInt(-1) - } - } ensuring (res => res >= 0) - - def consecutiveSubseq[B](l1: List[B], lTot: List[B]): Boolean = { - lTot match { - case Cons(hd, tl) => consecutiveSubseqAtHead(l1, lTot) || consecutiveSubseq(l1, tl) - case Nil() => consecutiveSubseqAtHead(l1, lTot) - } - } - - def consecutiveSubseqAtHead[B](l1: List[B], lTot: List[B]): Boolean = { - (l1, lTot) match { - case (Nil(), _) => true - case (Cons(hd1, tl1), Cons(hdTot, tlTot)) if hd1 == hdTot => consecutiveSubseqAtHead(tl1, tlTot) - case _ => false - } - } - - @inlineOnce - @opaque - def lemmaConsecutiveSubseqThenSubseq[B](l1: List[B], l2: List[B]): Unit = { - require(consecutiveSubseq(l1, l2)) - decreases(l2) - (l1, l2) match { - case (Cons(hd1, tl1), Cons(hd2, tl2)) if consecutiveSubseqAtHead(l1, l2) => lemmaConsecutiveSubseqThenSubseq(tl1, tl2) - case (Cons(hd1, tl1), Cons(hd2, tl2)) => lemmaConsecutiveSubseqThenSubseq(l1, tl2) - case _ => () - } - - } ensuring (_ => ListSpecs.subseq(l1, l2)) - - @inlineOnce - @opaque - def lemmaContainsAndNotHdThenTlContains[B](l: List[B], e: B): Unit = { - require(l.contains(e)) - require(l.head != e) - - } ensuring (_ => l.tail.contains(e)) - - @inlineOnce - @opaque - def lemmaGetIndexBiggerAndHeadNotEqThenTailContains[B](l: List[B], e1: B, e2: B): Unit = { - require(l.contains(e1) && l.contains(e2)) - require(e1 != e2) - require(getIndex(l, e1) < getIndex(l, e2)) - decreases(l.size) - - l match { - case Cons(hd, tl) if hd == e1 => lemmaGetIndexBiggerAndHeadEqThenTailContains(l, e1, e2) - case Cons(hd, tl) if hd != e1 => { - assert(hd != e1) - - lemmaContainsAndNotHdThenTlContains(l, e1) - lemmaNotHeadSoGetIndexTailIsMinusOne(l, e1) - lemmaNotHeadSoGetIndexTailIsMinusOne(l, e2) - - lemmaGetIndexBiggerAndHeadNotEqThenTailContains(tl, e1, e2) - } - case Nil() => check(false) - } - assert(l.tail.contains(e2)) - - } ensuring (_ => l.tail.contains(e2)) - - @inlineOnce - @opaque - def lemmaSameIndexThenSameElement[B](l: List[B], e1: B, e2: B): Unit = { - require(l.contains(e1)) - require(l.contains(e2)) - require(getIndex(l, e1) == getIndex(l, e2)) - decreases(l) - - if (getIndex(l, e1) == 0) { - assert(l.head == e1) - assert(l.head == e2) - assert(e1 == e2) - } else { - lemmaSameIndexThenSameElement(l.tail, e1, e2) - } - } ensuring (_ => e1 == e2) - - @inlineOnce - @opaque - def lemmaGetIndexBiggerAndHeadEqThenTailContains[B](l: List[B], e1: B, e2: B): Unit = { - require(l.contains(e1) && l.contains(e2)) - require(e1 != e2) - require(l.head == e1) - require(getIndex(l, e1) < getIndex(l, e2)) - - } ensuring (_ => l.tail.contains(e2)) - - @inlineOnce - @opaque - def lemmaNotHeadSoGetIndexTailIsMinusOne[B](l: List[B], e: B): Unit = { - decreases(l) - require(l.contains(e)) - require(l.head != e) - - if (l.tail.head != e) { - lemmaNotHeadSoGetIndexTailIsMinusOne(l.tail, e) - } - } ensuring (_ => getIndex(l, e) == getIndex(l.tail, e) + 1) - - @inlineOnce - @opaque - def lemmaIsPrefixRefl[B](l1: List[B], l2: List[B]): Unit = { - decreases(l1) - require(l1 == l2) - l1 match { - case Cons(hd, tl) => lemmaIsPrefixRefl(tl, l2.tail) - case Nil() => () - } - } ensuring (_ => isPrefix(l1, l2)) - - @inlineOnce - @opaque - def lemmaConcatTwoListThenFirstIsPrefix[B](l1: List[B], l2: List[B]): Unit = { - l1 match { - case Cons(hd, tl) => lemmaConcatTwoListThenFirstIsPrefix(tl, l2) - case Nil() => () - } - } ensuring (_ => isPrefix(l1, l1 ++ l2)) - - @inlineOnce - @opaque - def lemmaLongerPrefixContainsHeadOfSuffixOfSmallerPref[B](p1: List[B], s1: List[B], p2: List[B], l: List[B]): Unit = { - require(isPrefix(p2, l)) - require(p1 ++ s1 == l) - require(!s1.isEmpty) - require(p1.size < p2.size) - decreases(p1) - - lemmaConcatTwoListThenFirstIsPrefix(p1, s1) - - p1 match { - case Cons(hd, tl) => lemmaLongerPrefixContainsHeadOfSuffixOfSmallerPref(tl, s1, p2.tail, l.tail) - case Nil() => () - } - } ensuring (_ => p2.contains(s1.head)) - - @inlineOnce - @opaque - def lemmaConcatAssociativity[B](l1: List[B], elmt: B, l2: List[B], tot: List[B]): Unit = { - require((l1 ++ List(elmt)) ++ l2 == tot) - decreases(l1) - assert(l1 ++ List(elmt) ++ l2 == tot) - l1 match { - case Cons(hd, tl) => lemmaConcatAssociativity(tl, elmt, l2, tot.tail) - case Nil() => () - } - } ensuring (_ => l1 ++ (List(elmt) ++ l2) == tot) - - @inlineOnce - @opaque - def lemmaTwoListsConcatAssociativity[B]( - l1: List[B], - l2: List[B], - l3: List[B] - ): Unit = { - decreases(l1) - l1 match { - case Cons(hd, tl) => { - lemmaTwoListsConcatAssociativity(tl, l2, l3) - } - case Nil() => () - } - - } ensuring (_ => (l1 ++ l2) ++ l3 == l1 ++ (l2 ++ l3)) - - @inlineOnce - @opaque - def lemmaRemoveLastConcatenatedPrefixStillPrefix[B](l: List[B], elmt: B, tot: List[B]): Unit = { - require(isPrefix(l ++ List(elmt), tot)) - decreases(l) - l match { - case Cons(hd, tl) => lemmaRemoveLastConcatenatedPrefixStillPrefix(tl, elmt, tot.tail) - case Nil() => () - } - } ensuring (_ => isPrefix(l, tot)) - - @inlineOnce - @opaque - def lemmaRemoveLastPrefixStillPrefix[B](p: List[B], l: List[B]): Unit = { - require(!l.isEmpty) - require(isPrefix(p, l)) - require(p.size < l.size) - decreases(p) - p match { - case Cons(hd, tl) => lemmaRemoveLastPrefixStillPrefix(tl, l.tail) - case Nil() => () - } - - } ensuring (_ => isPrefix(p, removeLast(l))) - - @inlineOnce - @opaque - def lemmaPrefixStaysPrefixWhenAddingToSuffix[B](p: List[B], l: List[B], suffix: List[B]): Unit = { - decreases(p) - require(isPrefix(p, l)) - p match { - case Cons(hd, tl) => lemmaPrefixStaysPrefixWhenAddingToSuffix(tl, l.tail, suffix) - case Nil() => () - } - } ensuring (_ => isPrefix(p, l ++ suffix)) - - @inlineOnce - @opaque - def lemmaRemoveLastPrefixDecreasesSize[B](l: List[B]): Unit = { - require(l.size > 0) - } ensuring (_ => removeLast(l).size < l.size) - - @inlineOnce - @opaque - def lemmaIsPrefixSameLengthThenSameList[B](p1: List[B], p2: List[B], l: List[B]): Unit = { - require(isPrefix(p1, l)) - require(isPrefix(p2, l)) - require(p1.size == p2.size) - - p1 match { - case Cons(hd, tl) => lemmaIsPrefixSameLengthThenSameList(tl, p2.tail, l.tail) - case Nil() => () - } - - } ensuring (_ => p1 == p2) - - @inlineOnce - @opaque - def lemmaRemoveLastFromBothSidePreservesEq[B](p: List[B], s: List[B], l: List[B]): Unit = { - require(p ++ s == l) - require(!s.isEmpty) - decreases(p) - p match { - case Cons(hd, tl) => lemmaRemoveLastFromBothSidePreservesEq(tl, s, l.tail) - case Nil() => () - } - } ensuring (_ => p ++ removeLast(s) == removeLast(l)) - - @inlineOnce - @opaque - def lemmaRemoveLastFromLMakesItPrefix[B](l: List[B]): Unit = { - require(!l.isEmpty) - decreases(l.size) - l match { - case Cons(hd, Nil()) => () - case Cons(hd, tl) => lemmaRemoveLastFromLMakesItPrefix(tl) - } - - } ensuring (_ => isPrefix(removeLast(l), l)) - - @inlineOnce - @opaque - def lemmaSamePrefixThenSameSuffix[B](p1: List[B], s1: List[B], p2: List[B], s2: List[B], l: List[B]): Unit = { - require(isPrefix(p1, l)) - require(isPrefix(p2, l)) - require(p1 ++ s1 == l) - require(p2 ++ s2 == l) - require(p1 == p2) - - p1 match { - case Cons(hd, tl) => lemmaSamePrefixThenSameSuffix(tl, s1, p2.tail, s2, l.tail) - case Nil() => () - } - } ensuring (_ => s1 == s2) - - @inlineOnce - @opaque - def lemmaIsPrefixThenSmallerEqSize[B](p: List[B], l: List[B]): Unit = { - require(isPrefix(p, l)) - (p, l) match { - case (Nil(), _) => () - case (_, Nil()) => () - case (l1, l2) => lemmaIsPrefixThenSmallerEqSize(l1.tail, l2.tail) - } - } ensuring (_ => p.size <= l.size) - - @inlineOnce - @opaque - def lemmaAddHeadSuffixToPrefixStillPrefix[B](p: List[B], l: List[B]): Unit = { - require(isPrefix(p, l)) - require(p.size < l.size) - p match { - case Cons(hd, tl) => lemmaAddHeadSuffixToPrefixStillPrefix(tl, l.tail) - case Nil() => () - } - } ensuring (_ => isPrefix(p ++ List(getSuffix(l, p).head), l)) - - @inlineOnce - @opaque - def lemmaGetSuffixOnListWithItSelfIsEmpty[B](l: List[B]): Unit = { - decreases(l.size) - lemmaIsPrefixRefl(l, l) - l match { - case Cons(hd, tl) => lemmaGetSuffixOnListWithItSelfIsEmpty(tl) - case Nil() => () - } - } ensuring (_ => getSuffix(l, l).isEmpty) - - @inlineOnce - @opaque - def lemmaMoveElementToOtherListKeepsConcatEq[B](s1: List[B], hd2: B, tl2: List[B], tot: List[B]): Unit = { - require(s1 ++ Cons(hd2, tl2) == tot) - - s1 match { - case Cons(hd1, tl1) => lemmaMoveElementToOtherListKeepsConcatEq(tl1, hd2, tl2, tot.tail) - case Nil() => () - } - - } ensuring (_ => (s1 ++ List(hd2)) ++ tl2 == tot) - - @inlineOnce - @opaque - def lemmaPrefixFromSameListAndStrictlySmallerThenPrefixFromEachOther[B](s1: List[B], s2: List[B], l: List[B]): Unit = { - require(isPrefix(s1, l)) - require(isPrefix(s2, l)) - require(s2.size <= s1.size) - decreases(s2) - - s2 match { - case Cons(hd, tl) => lemmaPrefixFromSameListAndStrictlySmallerThenPrefixFromEachOther(s1.tail, tl, l.tail) - case Nil() => - } - } ensuring (_ => isPrefix(s2, s1)) - - @inlineOnce - @opaque - def concatWithoutDuplicates[B](baseList: List[B], newList: List[B]): List[B] = { - require(ListOps.noDuplicate(baseList)) - decreases(newList) - newList match { - case Cons(hd, tl) if baseList.contains(hd) => concatWithoutDuplicates(baseList, tl) - case Cons(hd, tl) if !baseList.contains(hd) => concatWithoutDuplicates(Cons(hd, baseList), tl) - case Nil() => baseList - } - } ensuring (res => ListOps.noDuplicate(res) && (baseList ++ newList).content == res.content) - - @inlineOnce - @opaque - def removeDuplicates[B](list: List[B], acc: List[B] = Nil[B]()): List[B] = { - require(ListOps.noDuplicate(acc)) - decreases(list.size) - list match { - case Cons(hd, tl) if acc.contains(hd) => removeDuplicates(tl, acc) - case Cons(hd, tl) => removeDuplicates(tl, Cons(hd, acc)) - case Nil() => acc - } - } ensuring (res => ListOps.noDuplicate(res) && res.content == (list ++ acc).content) - - @inlineOnce - @opaque - def lemmaSubseqRefl[B](l: List[B]): Unit = { - decreases(l.size) - l match { - case Nil() => () - case Cons(hd, tl) => lemmaSubseqRefl(tl) - } - } ensuring (_ => ListSpecs.subseq(l, l)) - - @inlineOnce - @opaque - def lemmaTailIsSubseqOfList[B](elmt: B, l: List[B]): Unit = { - l match { - case Nil() => () - case Cons(hd, tl) if hd == elmt => { - lemmaSubseqRefl(l) - ListSpecs.subseqTail(l, l) - assert(ListSpecs.subseq(tl, l)) - } - case Cons(hd, tl) if hd != elmt => lemmaSubseqRefl(l) - } - } ensuring (_ => ListSpecs.subseq(l, Cons(elmt, l))) - - @inlineOnce - @opaque - def lemmaSubSeqTransitive[B](l1: List[B], l2: List[B], l3: List[B]): Unit = { - require(ListSpecs.subseq(l1, l2)) - require(ListSpecs.subseq(l2, l3)) - decreases(l1.size, l2.size, l3.size) - - (l1, l2, l3) match { - case (Cons(hd1, tl1), Cons(hd2, tl2), Cons(hd3, tl3)) if hd2 != hd3 => { - lemmaSubSeqTransitive(l1, l2, tl3) - } - case (Cons(hd1, tl1), Cons(hd2, tl2), Cons(hd3, tl3)) if hd2 == hd3 => { - if (ListSpecs.subseq(tl2, tl3)) { - if (hd1 == hd2) { - if (ListSpecs.subseq(tl1, tl2)) { - lemmaSubSeqTransitive(tl1, tl2, tl3) - } else { - lemmaSubSeqTransitive(l1, tl2, tl3) - } - } else { - lemmaSubSeqTransitive(l1, tl2, tl3) - } - } else { - if (hd1 == hd2) { - if (ListSpecs.subseq(tl1, l2)) { - lemmaSubSeqTransitive(tl1, l2, tl3) - } else { - lemmaSubSeqTransitive(l1, l2, tl3) - } - } else { - lemmaSubSeqTransitive(l1, l2, tl3) - } - } - - } - case _ => () - } - - } ensuring (_ => ListSpecs.subseq(l1, l3)) - - @inlineOnce - @opaque - def lemmaConcatThenFirstSubseqOfTot[B](l1: List[B], l2: List[B]): Unit = { - decreases(l1) - l1 match { - case Cons(hd, tl) => lemmaConcatThenFirstSubseqOfTot(tl, l2) - case Nil() => () - } - } ensuring (_ => ListSpecs.subseq(l1, l1 ++ l2)) - - @inlineOnce - @opaque - def lemmaConcatThenSecondSubseqOfTot[B](l1: List[B], l2: List[B]): Unit = { - decreases(l1) - l1 match { - case Cons(hd, tl) => lemmaConcatThenSecondSubseqOfTot(tl, l2) - case Nil() => lemmaSubseqRefl(l2) - } - } ensuring (_ => ListSpecs.subseq(l2, l1 ++ l2)) - - @inlineOnce - @opaque - def lemmaConcatTwoListsWhichNotContainThenTotNotContain[B](l1: List[B], l2: List[B], b: B): Unit = { - require(!l1.contains(b)) - require(!l2.contains(b)) - - l1 match { - case Cons(hd, tl) if hd == b => check(false) - case Cons(hd, tl) => lemmaConcatTwoListsWhichNotContainThenTotNotContain(tl, l2, b) - case Nil() => () - } - } ensuring (_ => !(l1 ++ l2).contains(b)) - - @inlineOnce - @opaque - def lemmaForallContainsThenForEqualLists[B](l1: List[B], l2: List[B], l1Bis: List[B], l2Bis: List[B]): Unit = { - require(l1.forall(b => l2.contains(b))) - require(l1 == l1Bis) - require(l2 == l2Bis) - - } ensuring (_ => l1Bis.forall(b => l2Bis.contains(b))) - - @inlineOnce - @opaque - def lemmaForallContainsAndNoDuplicateThenSmallerList[B](l: List[B], lIn: List[B]): Unit = { - require(lIn.forall(e => l.contains(e))) - require(ListOps.noDuplicate(lIn)) - decreases(lIn.size) - - lIn match { - case Cons(hd, tl) => { - - ListSpecs.forallContainsSubset(lIn, l) - assert(lIn.content.subsetOf(l.content)) - assert(!tl.contains(hd)) - val newList = l - hd - assert(newList.content == l.content - hd) - ListSpecs.subsetContains(tl, newList) - lemmaForallContainsAndNoDuplicateThenSmallerList(newList, tl) - assert(tl.size <= newList.size) - assert(tl.size + 1 == lIn.size) - assert(l.contains(hd)) - assert(newList.content == l.content -- Set(hd)) - lemmaRemoveElmtContainedSizeSmaller(l, hd) - assert(l.size > newList.size) - } - case Nil() => () - } - } ensuring (_ => lIn.size <= l.size) - - @inlineOnce - @opaque - def lemmaRemoveElmtContainedSizeSmaller[B](l: List[B], e: B): Unit = { - require(l.contains(e)) - decreases(l) - l match { - case Cons(hd, tl) if hd == e => { - assert(l - e == tl - e) - if (tl.contains(e)) { - lemmaRemoveElmtContainedSizeSmaller(tl, e) - } - } - case Cons(hd, tl) => lemmaRemoveElmtContainedSizeSmaller(tl, e) - case Nil() => check(false) - } - } ensuring (_ => (l - e).size < l.size) -} diff --git a/lexers/regex/VerifiedLexer.scala b/lexers/regex/VerifiedLexer.scala deleted file mode 100644 index 728b1788..00000000 --- a/lexers/regex/VerifiedLexer.scala +++ /dev/null @@ -1,1505 +0,0 @@ -/** Author: Samuel Chassot - */ - -import stainless.annotation._ -import stainless.collection._ -import stainless.equations._ -import stainless.lang._ -import stainless.proof.check -import scala.annotation.tailrec -import stainless.lang.StaticChecks._ - -object Main { - import VerifiedLexer._ - import VerifiedRegex._ - import VerifiedRegexMatcher._ - import stainless.io.StdOut.println - import stainless.io.State - import VerifiedLexer._ - @extern - def main(args: Array[String]): Unit = { - - val regexSep = Concat(ElementMatch(' '), Star(ElementMatch(' '))) - val ruleSep = Rule(regexSep, "sep", true) - val sepToken = Token(List(' '), "sep", true) - - // DFA which recognises any repetition of at least one "ab" string - val regexAb = Concat(Concat(ElementMatch('a'), ElementMatch('b')), Star(Concat(ElementMatch('a'), ElementMatch('b')))) - val ruleAb = Rule(regexAb, "ab", false) - - // DFA which recognises any repetition of at least one "c" string - val regexC = Concat(ElementMatch('c'), Star(ElementMatch('c'))) - val ruleC = Rule(regexC, "c", false) - - val rules = List(ruleAb, ruleC, ruleSep) - - // Tokens -> Characters -> Tokens - val t1 = Token(List('a', 'b'), "ab", false) - val t2 = Token(List('c', 'c'), "c", false) - val input: List[Token[Char]] = List(t1, t2, t1, t2, t1, t1) - - val state = State(BigInt(1)) - - val output: List[Char] = Lexer.printWithSeparatorTokenWhenNeeded(rules, input, sepToken) - - val lexed = Lexer.lex(rules, output) - - println("Token list input:")(state) - println(input.foldLeft("")((s: String, t: Token[Char]) => s + t.toString + "\n"))(state) - - println("Token list printed when separator token when needed: " + output.foldLeft("")((s: String, c: Char) => s + c.toString))(state) - - println("After lexing again:")(state) - println(lexed._1.foldLeft("")((s: String, t: Token[Char]) => s + t.toString + "\n"))(state) - - println("tokens -> print -> tokens modulo separator tokens equality: " + (lexed._1.filter(!_.isSeparator) == input))(state) - println("--------------------------------------------------------------------------------")(state) - - // Characters -> Tokens -> Characters - val inputChar = List('a', 'b', 'c', 'a', 'b', 'a', 'b', 'c', 'c') - val tokenised = Lexer.lex(rules, inputChar) - - println("Input string is: " + inputChar.foldLeft("")((s: String, c: Char) => s + c.toString))(state) - println( - "Resulting list of tokens: " + tokenised._1.foldLeft("")((s: String, t: Token[Char]) => - s + t.characters.foldLeft("")((s: String, c: Char) => s + c.toString) + " " - ) - )( - state - ) - println("Non-tokenised suffix: " + tokenised._2.foldLeft("")((s: String, c: Char) => s + c.toString))(state) - } - -} -object VerifiedLexer { - import VerifiedRegex._ - import VerifiedRegexMatcher._ - - case class Token[C](characters: List[C], tag: String, isSeparator: Boolean) - case class Rule[C](regex: Regex[C], tag: String, isSeparator: Boolean) - - object Lexer { - - @inline - def ruleValid[C](r: Rule[C]): Boolean = { - validRegex(r.regex) && !nullable(r.regex) && r.tag != "" - } - def noDuplicateTag[C](rules: List[Rule[C]], acc: List[String] = Nil()): Boolean = { - decreases(rules) - rules match { - case Nil() => true - case Cons(hd, tl) => !acc.contains(hd.tag) && noDuplicateTag(tl, Cons(hd.tag, acc)) - } - } - def rulesValid[C](rs: List[Rule[C]]): Boolean = { - rs match { - case Cons(hd, tl) => ruleValid(hd) && rulesValid(tl) - case Nil() => true - } - } - - def rulesProduceIndivualToken[C](rs: List[Rule[C]], t: Token[C]): Boolean = { - require(!rs.isEmpty) - require(rulesInvariant(rs)) - val (producedTs, suffix) = lex(rs, print(List(t))) - producedTs.size == 1 && producedTs.head == t && suffix.isEmpty - } - - def rulesProduceEachTokenIndividually[C](rs: List[Rule[C]], ts: List[Token[C]]): Boolean = { - decreases(ts) - require(!rs.isEmpty) - require(rulesInvariant(rs)) - ts match { - case Cons(hd, tl) => rulesProduceIndivualToken(rs, hd) && rulesProduceEachTokenIndividually(rs, tl) - case Nil() => true - } - } - - def sepAndNonSepRulesDisjointChars[C](rules: List[Rule[C]], rulesRec: List[Rule[C]]): Boolean = { - rulesRec match { - case Cons(hd, tl) => ruleDisjointCharsFromAllFromOtherType(hd, rules) && sepAndNonSepRulesDisjointChars(rules, tl) - case Nil() => true - } - } - - def ruleDisjointCharsFromAllFromOtherType[C](r: Rule[C], rules: List[Rule[C]]): Boolean = { - decreases(rules) - rules match { - case Cons(hd, tl) if hd.isSeparator != r.isSeparator => rulesUseDisjointChars(r, hd) && ruleDisjointCharsFromAllFromOtherType(r, tl) - case Cons(hd, tl) => ruleDisjointCharsFromAllFromOtherType(r, tl) - case Nil() => true - } - - } - def rulesUseDisjointChars[C](r1: Rule[C], r2: Rule[C]): Boolean = { - usedCharacters(r2.regex).forall(c => !usedCharacters(r1.regex).contains(c)) && - usedCharacters(r1.regex).forall(c => !usedCharacters(r2.regex).contains(c)) - } - - @inline - def rulesInvariant[C](rules: List[Rule[C]]): Boolean = - rulesValid(rules) && noDuplicateTag(rules, Nil()) - - /** Main function of the lexer - * - * It lexes the input list of characters using the set of rules - * - * It returns the produced list of Tokens and the remaining untokenised characters (normally empty) - * - * @param rules - * @param input - */ - def lex[C]( - rules: List[Rule[C]], - input: List[C] - ): (List[Token[C]], List[C]) = { - decreases(input.size) - require(!rules.isEmpty) - require(rulesInvariant(rules)) - val ret: (List[Token[C]], List[C]) = maxPrefix(rules, input) match { - case Some((token, suffix)) => { - val (followingTokens, nextSuffix) = lex(rules, suffix) - assert(token.characters ++ suffix == input) - (Cons(token, followingTokens), nextSuffix) - } - case None() => (Nil(), input) - } - ret - } ensuring (res => - if (res._1.size > 0) res._2.size < input.size && !res._1.isEmpty - else res._2 == input - ) - - /** Prints back the tokens to a list of characters of the type C - * - * @param l - */ - def print[C](l: List[Token[C]]): List[C] = { - decreases(l) - l match { - case Cons(hd, tl) => hd.characters ++ print(tl) - case Nil() => Nil[C]() - } - } - - /** Prints back the tokens to a list of characters of the type C, by adding a separatorToken between all of them, and after the last - * - * @param l - * @param separatorToken - */ - def printWithSeparatorToken[C](l: List[Token[C]], separatorToken: Token[C]): List[C] = { - require(separatorToken.isSeparator) - decreases(l) - l match { - case Cons(hd, tl) => hd.characters ++ separatorToken.characters ++ printWithSeparatorToken(tl, separatorToken) - case Nil() => Nil[C]() - } - } - - /** Prints back the tokens to a list of characters of the type C, by adding a separatorToken between tokens when the maxPrefix would return - * another token if printed back to back. - * - * @param l - * @param separatorToken - */ - def printWithSeparatorTokenWhenNeeded[C](rules: List[Rule[C]], l: List[Token[C]], separatorToken: Token[C]): List[C] = { - require(!rules.isEmpty) - require(rulesInvariant(rules)) - require(rulesProduceEachTokenIndividually(rules, l)) - require(rulesProduceIndivualToken(rules, separatorToken)) - require(separatorToken.isSeparator) - require(l.forall(!_.isSeparator)) - require(sepAndNonSepRulesDisjointChars(rules, rules)) - decreases(l) - - l match { - case Cons(hd, tl) => { - val suffix = printWithSeparatorTokenWhenNeeded(rules, tl, separatorToken) - val maxPrefWithoutSep = maxPrefix(rules, hd.characters ++ suffix) - maxPrefWithoutSep match { - case Some((t, s)) if t == hd => hd.characters ++ suffix - case Some((t, s)) if t != hd => hd.characters ++ separatorToken.characters ++ suffix - case None() => { - lemmaLexIsDefinedWithStrThenLexWithSuffixIsDefined(rules, hd.characters, suffix) - check(false) - Nil[C]() - } - } - } - case Nil() => Nil[C]() - } - } - - /** Finds the biggest prefix matching any rule in the input list of characters If nothing matched a rule, returns None Else, returns the matched - * prefix as Token and the remaining suffix - * - * @param rulesArg - * @param input - */ - def maxPrefix[C]( - rulesArg: List[Rule[C]], - input: List[C] - ): Option[(Token[C], List[C])] = { - require(rulesValid(rulesArg)) - require(!rulesArg.isEmpty) - decreases(rulesArg.size) - - ListUtils.lemmaIsPrefixRefl(input, input) - val ret: Option[(Token[C], List[C])] = rulesArg match { - case Cons(hd, Nil()) => maxPrefixOneRule(hd, input) - case Cons(hd, tl) => { - val currentRulePref = maxPrefixOneRule(hd, input) - val othersPrefix = maxPrefix(tl, input) - (currentRulePref, othersPrefix) match { - case (None(), None()) => None() - case (c, None()) => c - case (None(), o) => o - case (Some(c), Some(o)) if c._1.characters.size >= o._1.characters.size => Some(c) - case (Some(c), Some(o)) if c._1.characters.size < o._1.characters.size => Some(o) - } - } - } - ret - } ensuring (res => res.isEmpty || res.isDefined && (res.get._2.size < input.size && res.get._1.characters ++ res.get._2 == input)) - - /** Finds the biggest prefix matching any rule in the input list of characters If nothing matched a rule, returns None Else, returns the matched - * prefix and the remaining suffix - * - * @param rule - * @param input - */ - def maxPrefixOneRule[C]( - rule: Rule[C], - input: List[C] - ): Option[(Token[C], List[C])] = { - require(ruleValid(rule)) - - val (longestPrefix, suffix) = findLongestMatch(rule.regex, input) - if (longestPrefix.isEmpty) { - None[(Token[C], List[C])]() - } else { - longestMatchIsAcceptedByMatchOrIsEmpty(rule.regex, input) - Some[(Token[C], List[C])]((Token(longestPrefix, rule.tag, rule.isSeparator), suffix)) - } - - } ensuring (res => - res.isEmpty || matchR( - rule.regex, - res.get._1.characters - ) && res.get._1.characters ++ res.get._2 == input && res.get._2.size < input.size && res.get._1.tag == rule.tag && res.get._1.isSeparator == rule.isSeparator - ) - - // Proofs -------------------------------------------------------------------------------------------------------------------------------- - - // Correctness --------------------------------------------------------------------------------------------------------------------------- - - // The lexer is sound, i.e., if it produces a Tokenisation, it is valid w.r.t. the biggest prefix property - def theoremLexSoundFirstChar[C]( - rules: List[Rule[C]], - input: List[C], - suffix: List[C], - tokens: List[Token[C]], - r: Rule[C], - otherR: Rule[C], - otherP: List[C] - ): Unit = { - require(!rules.isEmpty) - require(rulesInvariant(rules)) - require(rules.contains(r)) - require(rules.contains(otherR)) - require(lex(rules, input) == (tokens, suffix)) - - require(tokens.isEmpty || tokens.head.characters.size <= otherP.size) - require(tokens.isEmpty || tokens.head.tag == r.tag) - require(tokens.isEmpty || tokens.head.isSeparator == r.isSeparator) - require(ListUtils.isPrefix(otherP, input)) - require(r != otherR) - require({ - lemmaRuleInListAndRulesValidThenRuleIsValid(r, rules) - tokens.isEmpty || matchR(r.regex, tokens.head.characters) - }) - - lemmaRuleInListAndRulesValidThenRuleIsValid(otherR, rules) - if (ListUtils.getIndex(rules, r) > ListUtils.getIndex(rules, otherR)) { - - tokens match { - case Nil() => { - lemmaMaxPrefixReturnsNoneThenAnyRuleReturnsNone(otherR, rules, input) - lemmaMaxPrefOneRuleReturnsNoneThenNoPrefMaxRegex(otherR, otherP, input) - } - case Cons(hd, tl) => { - val (tok, suf) = maxPrefix(rules, input).get - assert(hd == tok) - ListUtils.lemmaConcatTwoListThenFirstIsPrefix(hd.characters, suf) - ListUtils.lemmaSamePrefixThenSameSuffix(hd.characters, suf, hd.characters, ListUtils.getSuffix(input, hd.characters), input) - if (otherP.size == hd.characters.size) { - ListUtils.lemmaIsPrefixSameLengthThenSameList(hd.characters, otherP, input) - lemmaMaxPrefNoSmallerRuleMatches(rules, r, hd.characters, input, otherR) - } else { - lemmaMaxPrefixTagSoFindMaxPrefOneRuleWithThisRule(rules, hd.characters, input, suf, r) - lemmaMaxPrefixOutputsMaxPrefix(rules, r, hd.characters, input, otherP, otherR) - } - } - } - } else { - if (ListUtils.getIndex(rules, r) == ListUtils.getIndex(rules, otherR)) { - ListUtils.lemmaSameIndexThenSameElement(rules, r, otherR) - check(false) - } - - tokens match { - case Nil() => { - lemmaMaxPrefixReturnsNoneThenAnyRuleReturnsNone(otherR, rules, input) - lemmaMaxPrefOneRuleReturnsNoneThenNoPrefMaxRegex(otherR, otherP, input) - } - case Cons(hd, tl) => { - val (tok, suf) = maxPrefix(rules, input).get - ListUtils.lemmaConcatTwoListThenFirstIsPrefix(hd.characters, suf) - ListUtils.lemmaSamePrefixThenSameSuffix(hd.characters, suf, hd.characters, ListUtils.getSuffix(input, hd.characters), input) - if (otherP.size > hd.characters.size) { - lemmaMaxPrefixTagSoFindMaxPrefOneRuleWithThisRule(rules, hd.characters, input, suf, r) - lemmaMaxPrefixOutputsMaxPrefix(rules, r, hd.characters, input, otherP, otherR) - } - - } - } - } - - } ensuring (_ => - if (ListUtils.getIndex(rules, otherR) < ListUtils.getIndex(rules, r)) !matchR(otherR.regex, otherP) - else tokens.size > 0 && otherP.size <= tokens.head.characters.size || !matchR(otherR.regex, otherP) - ) - - // Invertability ------------------------------------------------------------------------------------------------------------------------- - - def theoremInvertabilityFromTokensSepTokenWhenNeeded[C](rules: List[Rule[C]], tokens: List[Token[C]], separatorToken: Token[C]): Unit = { - require(!rules.isEmpty) - require(rulesInvariant(rules)) - require(rulesProduceEachTokenIndividually(rules, tokens)) - require(rulesProduceIndivualToken(rules, separatorToken)) - require(separatorToken.isSeparator) - require(tokens.forall(!_.isSeparator)) - require({ - lemmaMaxPrefReturnTokenSoItsTagBelongsToARule(rules, separatorToken.characters, separatorToken) - getRuleFromTag(rules, separatorToken.tag).get.isSeparator - }) - require(sepAndNonSepRulesDisjointChars(rules, rules)) - decreases(tokens) - - tokens match { - case Cons(hd, tl) => { - val input = printWithSeparatorTokenWhenNeeded(rules, tokens, separatorToken) - val suffixWithSep = separatorToken.characters ++ printWithSeparatorTokenWhenNeeded(rules, tl, separatorToken) - ListUtils.lemmaTwoListsConcatAssociativity( - hd.characters, - separatorToken.characters, - printWithSeparatorTokenWhenNeeded(rules, tl, separatorToken) - ) - val suffixWithoutSep = printWithSeparatorTokenWhenNeeded(rules, tl, separatorToken) - assert(input == hd.characters ++ suffixWithSep || input == hd.characters ++ suffixWithoutSep) - - if (input == hd.characters ++ suffixWithSep) { - val suffixAfterSep = printWithSeparatorTokenWhenNeeded(rules, tl, separatorToken) - lemmaPrintWithSepTokenWhenNeededThenMaxPrefReturnsHead(rules, tokens, separatorToken) - ListUtils.lemmaConcatTwoListThenFirstIsPrefix(hd.characters, suffixWithSep) - ListUtils.lemmaSamePrefixThenSameSuffix(hd.characters, suffixWithSep, hd.characters, maxPrefix(rules, input).get._2, input) - - val nextToken = tl.head - val sepTokenOpt = maxPrefix(rules, suffixWithSep) - if (tl.isEmpty) { - assert(input == hd.characters ++ separatorToken.characters) - check(false) - } - lemmaRulesProduceEachTokenIndividuallyThenForAnyToken(rules, tokens, nextToken) - check(rulesProduceIndivualToken(rules, nextToken)) - lemmaMaxPrefReturnTokenSoItsTagBelongsToARule(rules, separatorToken.characters, separatorToken) - val separatorRule = getRuleFromTag(rules, separatorToken.tag).get - lemmaMaxPrefReturnTokenSoItsTagBelongsToARule(rules, tl.head.characters, nextToken) - val nextTokenRule = getRuleFromTag(rules, nextToken.tag).get - - if (!usedCharacters(nextTokenRule.regex).contains(nextToken.characters.head)) { - lemmaRegexCannotMatchAStringContainingACharItDoesNotContain(nextTokenRule.regex, nextToken.characters, nextToken.characters.head) - check(false) - } - if (usedCharacters(separatorRule.regex).contains(suffixAfterSep.head)) { - lemmaSepRuleNotContainsCharContainedInANonSepRule(rules, rules, nextTokenRule, separatorRule, suffixAfterSep.head) - check(false) - } - lemmaMaxPrefWithOtherTypeUsedCharAtStartOfSuffixReturnSame(rules, separatorToken, separatorRule, suffixAfterSep, nextTokenRule) - - theoremInvertabilityFromTokensSepTokenWhenNeeded(rules, tl, separatorToken) - } else { - lemmaPrintWithSepTokenWhenNeededThenMaxPrefReturnsHead(rules, tokens, separatorToken) - theoremInvertabilityFromTokensSepTokenWhenNeeded(rules, tl, separatorToken) - ListUtils.lemmaConcatTwoListThenFirstIsPrefix(hd.characters, suffixWithoutSep) - ListUtils.lemmaSamePrefixThenSameSuffix(hd.characters, suffixWithoutSep, hd.characters, maxPrefix(rules, input).get._2, input) - } - } - case Nil() => () - } - } ensuring (_ => lex(rules, printWithSeparatorTokenWhenNeeded(rules, tokens, separatorToken))._1.filter(!_.isSeparator) == tokens) - - def theoremInvertFromTokensSepTokenBetweenEach[C](rules: List[Rule[C]], tokens: List[Token[C]], separatorToken: Token[C]): Unit = { - require(!rules.isEmpty) - require(rulesInvariant(rules)) - require(rulesProduceEachTokenIndividually(rules, tokens)) - require(rulesProduceIndivualToken(rules, separatorToken)) - require(separatorToken.isSeparator) - require(tokens.forall(!_.isSeparator)) - require({ - lemmaMaxPrefReturnTokenSoItsTagBelongsToARule(rules, separatorToken.characters, separatorToken) - getRuleFromTag(rules, separatorToken.tag).get.isSeparator - }) - require(sepAndNonSepRulesDisjointChars(rules, rules)) - decreases(tokens.size) - - tokens match { - case Nil() => () - // case Cons(hd, Nil()) => () - case Cons(hd, Nil()) => { - ListSpecs.forallContained(tokens, (t: Token[C]) => !t.isSeparator, hd) - assert(!hd.isSeparator) - val input = printWithSeparatorToken(tokens, separatorToken) - assert(input == hd.characters ++ separatorToken.characters) - ListUtils.lemmaGetSuffixOnListWithItSelfIsEmpty(hd.characters) - lemmaRulesProduceEachTokenIndividuallyThenForAnyToken(rules, tokens, hd) - lemmaMaxPrefReturnTokenSoItsTagBelongsToARule(rules, hd.characters, hd) - val rule = getRuleFromTag(rules, hd.tag).get - assert(!rule.isSeparator) - lemmaMaxPrefReturnTokenSoItsTagBelongsToARule(rules, separatorToken.characters, separatorToken) - val separatorRule = getRuleFromTag(rules, separatorToken.tag).get - lemmaMaxPrefixTagSoFindMaxPrefOneRuleWithThisRule(rules, hd.characters, hd.characters, Nil(), rule) - - if (!usedCharacters(separatorRule.regex).contains(separatorToken.characters.head)) { - lemmaRegexCannotMatchAStringContainingACharItDoesNotContain( - separatorRule.regex, - separatorToken.characters, - separatorToken.characters.head - ) - check(false) - } - assert(usedCharacters(separatorRule.regex).contains(separatorToken.characters.head)) - - lemmaNonSepRuleNotContainsCharContainedInASepRule(rules, rules, rule, separatorRule, separatorToken.characters.head) - - lemmaMaxPrefWithOtherTypeUsedCharAtStartOfSuffixReturnSame(rules, hd, rule, separatorToken.characters, separatorRule) - - val suffix = maxPrefix(rules, input).get._2 - - // needed - val ret: (List[Token[C]], List[C]) = maxPrefix(rules, input) match { - case Some((t, s)) => { - assert(s == suffix) - assert(t == hd) - val (followingTokens, nextSuffix) = lex(rules, s) - assert(nextSuffix.isEmpty) - assert(t.characters ++ s == input) - (Cons(t, followingTokens), nextSuffix) - } - case None() => { - check(false) - (Nil(), input) - } - } - - } - case Cons(hd, Cons(nextT, tl)) => { - ListSpecs.forallContained(tokens, (t: Token[C]) => !t.isSeparator, hd) - ListSpecs.forallContained(tokens, (t: Token[C]) => !t.isSeparator, nextT) - assert(!hd.isSeparator) - assert(!nextT.isSeparator) - val input = printWithSeparatorToken(tokens, separatorToken) - val suffixAfterSeparator = printWithSeparatorToken(Cons(nextT, tl), separatorToken) - val suffix = separatorToken.characters ++ suffixAfterSeparator - assert(suffixAfterSeparator == nextT.characters ++ separatorToken.characters ++ printWithSeparatorToken(tl, separatorToken)) - assert(input == hd.characters ++ separatorToken.characters ++ suffixAfterSeparator) - ListUtils.lemmaTwoListsConcatAssociativity(hd.characters, separatorToken.characters, suffixAfterSeparator) - assert(input == hd.characters ++ suffix) - lemmaRulesProduceEachTokenIndividuallyThenForAnyToken(rules, tokens, hd) - lemmaMaxPrefReturnTokenSoItsTagBelongsToARule(rules, hd.characters, hd) - val rule = getRuleFromTag(rules, hd.tag).get - assert(!rule.isSeparator) - lemmaMaxPrefixTagSoFindMaxPrefOneRuleWithThisRule(rules, hd.characters, hd.characters, Nil(), rule) - - lemmaMaxPrefReturnTokenSoItsTagBelongsToARule(rules, separatorToken.characters, separatorToken) - val separatorRule = getRuleFromTag(rules, separatorToken.tag).get - - if (!usedCharacters(separatorRule.regex).contains(separatorToken.characters.head)) { - lemmaRegexCannotMatchAStringContainingACharItDoesNotContain( - separatorRule.regex, - separatorToken.characters, - separatorToken.characters.head - ) - check(false) - } - - assert(suffixAfterSeparator == nextT.characters ++ separatorToken.characters ++ printWithSeparatorToken(tl, separatorToken)) - lemmaNonSepRuleNotContainsCharContainedInASepRule(rules, rules, rule, separatorRule, separatorToken.characters.head) - - lemmaMaxPrefWithOtherTypeUsedCharAtStartOfSuffixReturnSame(rules, hd, rule, suffix, separatorRule) - - lemmaRulesProduceEachTokenIndividuallyThenForAnyToken(rules, tokens, nextT) - assert(rulesProduceIndivualToken(rules, nextT)) - lemmaMaxPrefReturnTokenSoItsTagBelongsToARule(rules, nextT.characters, nextT) - val nextTRule = getRuleFromTag(rules, nextT.tag).get - assert(!nextTRule.isSeparator) - lemmaMaxPrefixTagSoFindMaxPrefOneRuleWithThisRule(rules, nextT.characters, nextT.characters, Nil(), nextTRule) - - if (!usedCharacters(nextTRule.regex).contains(nextT.characters.head)) { - lemmaRegexCannotMatchAStringContainingACharItDoesNotContain(nextTRule.regex, nextT.characters, nextT.characters.head) - check(false) - } - - lemmaSepRuleNotContainsCharContainedInANonSepRule(rules, rules, nextTRule, separatorRule, nextT.characters.head) - - lemmaMaxPrefWithOtherTypeUsedCharAtStartOfSuffixReturnSame(rules, separatorToken, separatorRule, suffixAfterSeparator, nextTRule) - - theoremInvertFromTokensSepTokenBetweenEach(rules, Cons(nextT, tl), separatorToken) - - } - } - - } ensuring (_ => lex(rules, printWithSeparatorToken(tokens, separatorToken))._1.filter(!_.isSeparator) == tokens) - - def theoremInvertFromString[C](rules: List[Rule[C]], input: List[C]): Unit = { - require(!rules.isEmpty) - require(rulesInvariant(rules)) - decreases(input.size) - - val (tokens, suffix) = lex(rules, input) - if (suffix.isEmpty) { - tokens match { - case Cons(hd, Nil()) => () - case Cons(hd, tl) => theoremInvertFromString(rules, maxPrefix(rules, input).get._2) - case Nil() => () - } - } else { - tokens match { - case Cons(hd, Nil()) => assert(print(tokens) ++ suffix == input) - case Cons(hd, tl) => { - theoremInvertFromString(rules, maxPrefix(rules, input).get._2) - lemmaRemovingFirstTokensCharactersPreservesLexSuffix(rules, input, tokens, suffix) - - assert(input == maxPrefix(rules, input).get._1.characters ++ maxPrefix(rules, input).get._2) - assert(input == maxPrefix(rules, input).get._1.characters ++ (print(tl) ++ suffix)) - ListUtils.lemmaTwoListsConcatAssociativity( - maxPrefix(rules, input).get._1.characters, - print(tl), - suffix - ) - } - case Nil() => assert(print(tokens) ++ suffix == input) - } - } - } ensuring (_ => { - val (tokens, suffix) = lex(rules, input) - print(tokens) ++ suffix == input - }) - - // Functions ----------------------------------------------------------------------------------------------------------------------------- - - def getRuleFromTag[C](rules: List[Rule[C]], tag: String): Option[Rule[C]] = { - require(rulesInvariant(rules)) - rules match { - case Cons(hd, tl) if hd.tag == tag => Some(hd) - case Cons(hd, tl) if hd.tag != tag => { - lemmaInvariantOnRulesThenOnTail(hd, tl) - getRuleFromTag(tl, tag) - } - case Nil() => None[Rule[C]]() - } - } ensuring (res => res.isEmpty || rules.contains(res.get) && res.get.tag == tag) - - // Lemmas -------------------------------------------------------------------------------------------------------------------------------- - - def lemmaPrintWithSepTokenWhenNeededThenMaxPrefReturnsHead[C](rules: List[Rule[C]], tokens: List[Token[C]], separatorToken: Token[C]): Unit = { - require(!rules.isEmpty) - require(rulesInvariant(rules)) - require(rulesProduceEachTokenIndividually(rules, tokens)) - require(rulesProduceIndivualToken(rules, separatorToken)) - require(separatorToken.isSeparator) - require(tokens.forall(!_.isSeparator)) - require(sepAndNonSepRulesDisjointChars(rules, rules)) - - tokens match { - case Cons(hd, tl) => { - lemmaMaxPrefReturnTokenSoItsTagBelongsToARule(rules, separatorToken.characters, separatorToken) - val separatorRule = getRuleFromTag(rules, separatorToken.tag).get - - lemmaMaxPrefReturnTokenSoItsTagBelongsToARule(rules, hd.characters, hd) - val rule = getRuleFromTag(rules, hd.tag).get - - val suffix = printWithSeparatorTokenWhenNeeded(rules, tl, separatorToken) - val maxPrefWithoutSep = maxPrefix(rules, hd.characters ++ suffix) - maxPrefWithoutSep match { - case Some((t, s)) if t == hd => () - case Some((t, s)) if t != hd => { - ListUtils.lemmaTwoListsConcatAssociativity(hd.characters, separatorToken.characters, suffix) - val resSuffix = separatorToken.characters ++ suffix - if (!usedCharacters(separatorRule.regex).contains(separatorToken.characters.head)) { - lemmaRegexCannotMatchAStringContainingACharItDoesNotContain( - separatorRule.regex, - separatorToken.characters, - separatorToken.characters.head - ) - check(false) - } - lemmaNonSepRuleNotContainsCharContainedInASepRule(rules, rules, rule, separatorRule, separatorToken.characters.head) - - check(maxPrefix(rules, hd.characters).isDefined) - check(maxPrefix(rules, hd.characters).get._1 == hd) - lemmaMaxPrefWithOtherTypeUsedCharAtStartOfSuffixReturnSame(rules, hd, rule, resSuffix, separatorRule) - } - case None() => { - lemmaLexIsDefinedWithStrThenLexWithSuffixIsDefined(rules, hd.characters, suffix) - check(false) - } - } - } - case Nil() => () - } - - } ensuring (_ => - tokens.isEmpty || - (!tokens.isEmpty && - maxPrefix(rules, printWithSeparatorTokenWhenNeeded(rules, tokens, separatorToken)).isDefined && - maxPrefix(rules, printWithSeparatorTokenWhenNeeded(rules, tokens, separatorToken)).get._1 == tokens.head) - ) - - def lemmaMaxPrefWithOtherTypeUsedCharAtStartOfSuffixReturnSame[C]( - rules: List[Rule[C]], - token: Token[C], - rule: Rule[C], - suffix: List[C], - anOtherTypeRule: Rule[C] - ): Unit = { - require(!rules.isEmpty) - require(rulesInvariant(rules)) - require(rules.contains(rule)) - require(rules.contains(anOtherTypeRule)) - require(anOtherTypeRule.isSeparator != rule.isSeparator) - require(maxPrefix(rules, token.characters).isDefined) - require(maxPrefix(rules, token.characters).get._1 == token) - require(maxPrefix(rules, token.characters).get._2.isEmpty) - require(token.tag == rule.tag) - require(token.isSeparator == rule.isSeparator) - require({ - lemmaRuleInListAndRulesValidThenRuleIsValid(rule, rules) - matchR(rule.regex, token.characters) - }) - require(!suffix.isEmpty) - require(!usedCharacters(rule.regex).contains(suffix.head)) - require(usedCharacters(anOtherTypeRule.regex).contains(suffix.head)) - require(sepAndNonSepRulesDisjointChars(rules, rules)) - - val input = token.characters ++ suffix - ListUtils.lemmaConcatTwoListThenFirstIsPrefix(token.characters, suffix) - val tokenOpt = maxPrefix(rules, input) - lemmaLexIsDefinedWithStrThenLexWithSuffixIsDefined(rules, token.characters, suffix) - val foundToken = tokenOpt.get._1 - val foundSuffix = tokenOpt.get._2 - lemmaMaxPrefReturnTokenSoItsTagBelongsToARule(rules, input, foundToken) - val foundRule = getRuleFromTag(rules, foundToken.tag).get - ListUtils.lemmaConcatTwoListThenFirstIsPrefix(foundToken.characters, foundSuffix) - assert(ListUtils.isPrefix(foundToken.characters, input)) - assert(foundRule.tag == foundToken.tag) - assert(matchR(foundRule.regex, foundToken.characters)) - assert(foundRule.isSeparator == foundToken.isSeparator) - - lemmaMaxPrefixTagSoFindMaxPrefOneRuleWithThisRule(rules, foundToken.characters, input, foundSuffix, foundRule) - ListUtils.lemmaSamePrefixThenSameSuffix( - foundToken.characters, - foundSuffix, - foundToken.characters, - ListUtils.getSuffix(input, foundToken.characters), - input - ) - assert(ListUtils.getSuffix(input, foundToken.characters) == foundSuffix) - assert(maxPrefixOneRule(foundRule, input) == Some((foundToken, ListUtils.getSuffix(input, foundToken.characters)))) - - if (!usedCharacters(rule.regex).contains(foundToken.characters.head)) { - lemmaRegexCannotMatchAStringContainingACharItDoesNotContain(rule.regex, token.characters, foundToken.characters.head) - check(false) - } - if (rule.isSeparator) { - if (!foundRule.isSeparator) { - assert(token.characters.contains(foundToken.characters.head)) - assert(usedCharacters(rule.regex).contains(foundToken.characters.head)) - lemmaNonSepRuleNotContainsCharContainedInASepRule(rules, rules, foundRule, rule, foundToken.characters.head) - lemmaRegexCannotMatchAStringContainingACharItDoesNotContain(foundRule.regex, foundToken.characters, foundToken.characters.head) - check(false) - } - } else { - if (foundRule.isSeparator) { - assert(token.characters.contains(foundToken.characters.head)) - assert(usedCharacters(rule.regex).contains(foundToken.characters.head)) - lemmaSepRuleNotContainsCharContainedInANonSepRule(rules, rules, rule, foundRule, foundToken.characters.head) - lemmaRegexCannotMatchAStringContainingACharItDoesNotContain(foundRule.regex, foundToken.characters, foundToken.characters.head) - check(false) - } - } - - if (foundToken.characters.size > token.characters.size) { - ListUtils.lemmaLongerPrefixContainsHeadOfSuffixOfSmallerPref(token.characters, suffix, foundToken.characters, input) - if (rule.isSeparator) { - lemmaSepRuleNotContainsCharContainedInANonSepRule(rules, rules, anOtherTypeRule, foundRule, suffix.head) - lemmaRegexCannotMatchAStringContainingACharItDoesNotContain(foundRule.regex, foundToken.characters, suffix.head) - check(false) - } else { - lemmaNonSepRuleNotContainsCharContainedInASepRule(rules, rules, foundRule, anOtherTypeRule, suffix.head) - lemmaRegexCannotMatchAStringContainingACharItDoesNotContain(foundRule.regex, foundToken.characters, suffix.head) - check(false) - } - } - if (foundToken.characters.size < token.characters.size) { - lemmaMaxPrefixOutputsMaxPrefix(rules, foundRule, foundToken.characters, input, token.characters, rule) - check(false) - } - ListUtils.lemmaIsPrefixSameLengthThenSameList(foundToken.characters, token.characters, input) - - assert(foundToken.characters == token.characters) - - if (foundToken.tag != token.tag) { - assert(foundRule != rule) - val foundRuleIndex = ListUtils.getIndex(rules, foundRule) - val ruleIndex = ListUtils.getIndex(rules, rule) - if (foundRuleIndex < ruleIndex) { - ListUtils.lemmaGetSuffixOnListWithItSelfIsEmpty(token.characters) - assert(ListUtils.getSuffix(token.characters, token.characters).isEmpty) - lemmaMaxPrefNoSmallerRuleMatches(rules, rule, token.characters, token.characters, foundRule) - check(false) - } - if (ruleIndex < foundRuleIndex) { - lemmaMaxPrefNoSmallerRuleMatches(rules, foundRule, token.characters, input, rule) - check(false) - } - - ListUtils.lemmaSameIndexThenSameElement(rules, foundRule, rule) - check(false) - } - assert(foundToken.tag == token.tag) - assert(foundToken.tag == rule.tag) - - lemmaMaxPrefixTagSoFindMaxPrefOneRuleWithThisRule(rules, token.characters, input, ListUtils.getSuffix(input, token.characters), rule) - ListUtils.lemmaSamePrefixThenSameSuffix( - token.characters, - ListUtils.getSuffix(input, token.characters), - foundToken.characters, - foundSuffix, - input - ) - ListUtils.lemmaSamePrefixThenSameSuffix(token.characters, suffix, foundToken.characters, foundSuffix, input) - - } ensuring (_ => maxPrefix(rules, token.characters ++ suffix) == Some((token, suffix))) - - def lemmaLexIsDefinedWithStrThenLexWithSuffixIsDefined[C](rules: List[Rule[C]], input: List[C], suffix: List[C]): Unit = { - require(!rules.isEmpty) - require(rulesInvariant(rules)) - require(!lex(rules, input)._1.isEmpty) - - val (tokens, _) = lex(rules, input) - val firstT = tokens.head - ListUtils.lemmaConcatTwoListThenFirstIsPrefix(firstT.characters, maxPrefix(rules, input).get._2) - ListUtils.lemmaPrefixStaysPrefixWhenAddingToSuffix(firstT.characters, input, suffix) - lemmaMaxPrefReturnTokenSoItsTagBelongsToARule(rules, input, firstT) - val rule: Rule[C] = getRuleFromTag(rules, firstT.tag).get - assert(matchR(rule.regex, firstT.characters)) - - if (maxPrefix(rules, input ++ suffix).isEmpty) { - lemmaMaxPrefixReturnsNoneThenAnyRuleReturnsNone(rule, rules, input ++ suffix) - lemmaMaxPrefOneRuleReturnsNoneThenNoPrefMaxRegex(rule, firstT.characters, input ++ suffix) - check(false) - } - - } ensuring (_ => maxPrefix(rules, input ++ suffix).isDefined) - - def lemmaMaxPrefReturnTokenSoItsTagBelongsToARule[C](rules: List[Rule[C]], input: List[C], token: Token[C]): Unit = { - require(rulesInvariant(rules)) - require(!rules.isEmpty) - require(maxPrefix(rules, input).isDefined && maxPrefix(rules, input).get._1 == token) - decreases(rules) - - rules match { - case Cons(hd, tl) => { - if (maxPrefixOneRule(hd, input).isDefined && maxPrefixOneRule(hd, input).get._1 == token) { - assert(hd.tag == token.tag) - assert(matchR(hd.regex, token.characters)) - } else { - if (!tl.isEmpty) { - lemmaInvariantOnRulesThenOnTail(hd, tl) - lemmaMaxPrefReturnTokenSoItsTagBelongsToARule(tl, input, token) - lemmaGetRuleFromTagInListThenSameListWhenAddingARuleDiffTag(tl, hd, token.tag) - } else { - check(false) - } - } - } - case Nil() => () - } - } ensuring (_ => - getRuleFromTag(rules, token.tag).isDefined && matchR(getRuleFromTag(rules, token.tag).get.regex, token.characters) && - token.isSeparator == getRuleFromTag(rules, token.tag).get.isSeparator - ) - - def lemmaGetRuleFromTagInListThenSameListWhenAddingARuleDiffTag[C](rules: List[Rule[C]], newHd: Rule[C], tag: String): Unit = { - require(rulesInvariant(Cons(newHd, rules))) - require({ - lemmaInvariantOnRulesThenOnTail(newHd, rules) - getRuleFromTag(rules, tag).isDefined - }) - - lemmaInvariantOnRulesThenOnTail(newHd, rules) - lemmaNoDuplicateAndTagInAccThenRuleCannotHaveSame(rules, getRuleFromTag(rules, tag).get, newHd.tag, List(newHd.tag)) - - } ensuring (_ => getRuleFromTag(rules, tag).get == getRuleFromTag(Cons(newHd, rules), tag).get) - - def lemmaRemovingFirstTokensCharactersPreservesLexSuffix[C]( - rules: List[Rule[C]], - input: List[C], - producedTokens: List[Token[C]], - suffix: List[C] - ): Unit = { - require(rulesInvariant(rules)) - require(!rules.isEmpty) - require(producedTokens.size > 0) - require(lex(rules, input) == (producedTokens, suffix)) - } ensuring (_ => lex(rules, maxPrefix(rules, input).get._2) == (producedTokens.tail, suffix)) - - def lemmaMaxPrefNoneThenNoRuleMatches[C](rules: List[Rule[C]], r: Rule[C], p: List[C], input: List[C]): Unit = { - require(ListUtils.isPrefix(p, input)) - require(!rules.isEmpty) - require(rulesInvariant(rules)) - require(rules.contains(r)) - require(maxPrefix(rules, input) == None[(Token[C], List[C])]()) - - lemmaRuleInListAndRulesValidThenRuleIsValid(r, rules) - - lemmaMaxPrefixReturnsNoneThenAnyRuleReturnsNone(r, rules, input) - lemmaMaxPrefOneRuleReturnsNoneThenNoPrefMaxRegex(r, p, input) - - } ensuring (_ => !matchR(r.regex, p)) - - def lemmaMaxPrefNoSmallerRuleMatches[C]( - rules: List[Rule[C]], - r: Rule[C], - p: List[C], - input: List[C], - rBis: Rule[C] - ): Unit = { - require(ListUtils.isPrefix(p, input)) - require(!rules.isEmpty) - require(rulesInvariant(rules)) - require(rules.contains(r)) - require(rules.contains(rBis)) - require(maxPrefix(rules, input) == Some(Token(p, r.tag, r.isSeparator), ListUtils.getSuffix(input, p))) - require(ListUtils.getIndex(rules, rBis) < ListUtils.getIndex(rules, r)) - require(ruleValid(r)) - require(matchR(r.regex, p)) - decreases(rules) - - assert(ListUtils.getIndex(rules, rBis) < ListUtils.getIndex(rules, r)) - - lemmaRuleInListAndRulesValidThenRuleIsValid(rBis, rules) - lemmaRuleInListAndRulesValidThenRuleIsValid(r, rules) - rules match { - case Cons(hd, tl) if hd == rBis => { - ListUtils.lemmaGetIndexBiggerAndHeadEqThenTailContains(rules, rBis, r) - lemmaNoDuplicateTagAndDiffIndexThenNoTwoRulesEq(rules, rBis, r) - - val tokenSuffOpt = maxPrefixOneRule(rBis, input) - if (tokenSuffOpt.isEmpty) { - lemmaMaxPrefOneRuleReturnsNoneThenNoPrefMaxRegex(rBis, p, input) - } else { - val (token, suff) = tokenSuffOpt.get - if (token.characters.size > p.size) { - lemmaRuleReturnsPrefixSmallerEqualThanGlobalMaxPref( - rules, - p, - Token(p, r.tag, r.isSeparator), - input, - ListUtils.getSuffix(input, p), - token.characters, - ListUtils.getSuffix(input, token.characters), - rBis, - token - ) - check(false) - check(!matchR(rBis.regex, p)) - } else { - if (token.characters.size < p.size) { - ListUtils.lemmaConcatTwoListThenFirstIsPrefix(token.characters, suff) - lemmaMaxPrefixOneRuleOutputsMaxPrefix(rBis, token.characters, token, input, suff, p) - check(!matchR(rBis.regex, p)) - } else { - lemmaNoDuplicateTagAndDiffIndexThenNoTwoRulesTagsEq(rules, rBis, r) - check(Some(token, suff) != Some(Token(p, r.tag, r.isSeparator), ListUtils.getSuffix(input, p))) - check(!matchR(rBis.regex, p)) - } - } - } - } - case Cons(hd, tl) if hd != rBis => { - assert(tl.contains(r)) - assert(tl.contains(rBis)) - - val tokenSuffOpt = maxPrefixOneRule(hd, input) - val tokenSuffTailOpt = maxPrefix(tl, input) - - lemmaInvariantOnRulesThenOnTail(hd, tl) - lemmaNoDuplicateTagAndDiffIndexThenNoTwoRulesTagsEq(rules, hd, r) - lemmaMaxPrefNoSmallerRuleMatches(tl, r, p, input, rBis) - } - case Nil() => check(false) - } - } ensuring (_ => !matchR(rBis.regex, p)) - - /** Lemma which proves that indeed the getMaxPrefix indeed returns the maximal prefix that matches any rules - * - * @param rules - * @param r - * @param p - * @param input - * @param pBis - * @param rBis - */ - def lemmaMaxPrefixOutputsMaxPrefix[C]( - rules: List[Rule[C]], - r: Rule[C], - p: List[C], - input: List[C], - pBis: List[C], - rBis: Rule[C] - ): Unit = { - require(ListUtils.isPrefix(p, input)) - require(ListUtils.isPrefix(pBis, input)) - - require(!rules.isEmpty) - require(rulesInvariant(rules)) - require(rules.contains(r)) - require(rules.contains(rBis)) - - require({ - lemmaRuleInListAndRulesValidThenRuleIsValid(r, rules) - matchR(r.regex, p) - }) - require({ - ListUtils.lemmaIsPrefixRefl(input, input) - maxPrefixOneRule(r, input) == Some(Token(p, r.tag, r.isSeparator), ListUtils.getSuffix(input, p)) - }) - - require(pBis.size > p.size) - - require(maxPrefix(rules, input) == Some(Token(p, r.tag, r.isSeparator), ListUtils.getSuffix(input, p))) - - // For preconditions - lemmaRuleInListAndRulesValidThenRuleIsValid(r, rules) - lemmaRuleInListAndRulesValidThenRuleIsValid(rBis, rules) - ListUtils.lemmaIsPrefixRefl(input, input) - - // Main lemma - lemmaMaxPrefixOutputsMaxPrefixInner(rules, r, p, input, pBis, rBis) - - } ensuring (_ => !matchR(rBis.regex, pBis)) - - def lemmaMaxPrefixOutputsMaxPrefixInner[C]( - rules: List[Rule[C]], - r: Rule[C], - p: List[C], - input: List[C], - pBis: List[C], - rBis: Rule[C] - ): Unit = { - require(ListUtils.isPrefix(p, input)) - require(ListUtils.isPrefix(pBis, input)) - - require(!rules.isEmpty) - require(rulesInvariant(rules)) - require(rules.contains(r)) - require(rules.contains(rBis)) - - require(validRegex(r.regex)) - require(matchR(r.regex, p)) - require(ruleValid(r)) - require({ - ListUtils.lemmaIsPrefixRefl(input, input) - maxPrefixOneRule(r, input) == Some(Token(p, r.tag, r.isSeparator), ListUtils.getSuffix(input, p)) - }) - - require(pBis.size > p.size) - - require(ruleValid(rBis)) - require(maxPrefix(rules, input) == Some(Token(p, r.tag, r.isSeparator), ListUtils.getSuffix(input, p))) - - assert(validRegex(r.regex)) - - ListUtils.lemmaIsPrefixThenSmallerEqSize(pBis, input) - lemmaRuleInListAndRulesValidThenRuleIsValid(rBis, rules) - - val bisTokenSuff = maxPrefixOneRule(rBis, input) // == Some(Token(pBis, rBis.tag), ListUtils.getSuffix(input, pBis)) - if (bisTokenSuff.isEmpty) { - lemmaMaxPrefOneRuleReturnsNoneThenNoPrefMaxRegex(rBis, pBis, input) - check(!matchR(rBis.regex, pBis)) - } else { - val tBis = bisTokenSuff.get._1 - val suffixBis = bisTokenSuff.get._2 - ListUtils.lemmaConcatTwoListThenFirstIsPrefix(tBis.characters, suffixBis) - if (tBis.characters == pBis) { - lemmaRuleReturnsPrefixSmallerEqualThanGlobalMaxPref( - rules, - p, - Token(p, r.tag, r.isSeparator), - input, - ListUtils.getSuffix(input, p), - pBis, - suffixBis, - rBis, - tBis - ) - check(!matchR(rBis.regex, pBis)) - } else { - if (tBis.characters.size < pBis.size) { - assert(ListUtils.isPrefix(tBis.characters, input)) - lemmaMaxPrefixOneRuleOutputsMaxPrefix(rBis, tBis.characters, tBis, input, suffixBis, pBis) - check(!matchR(rBis.regex, pBis)) - } else { - if (pBis.size == tBis.characters.size) { - ListUtils.lemmaIsPrefixSameLengthThenSameList(pBis, tBis.characters, input) - check(false) - } - assert(pBis.size < tBis.characters.size) - lemmaRuleReturnsPrefixSmallerEqualThanGlobalMaxPref( - rules, - p, - Token(p, r.tag, r.isSeparator), - input, - ListUtils.getSuffix(input, p), - tBis.characters, - suffixBis, - rBis, - tBis - ) - assert(tBis.characters.size <= p.size) - check(false) - check(!matchR(rBis.regex, pBis)) - - } - } - - } - - } ensuring (_ => !matchR(rBis.regex, pBis)) - - def lemmaMaxPrefixTagSoFindMaxPrefOneRuleWithThisRule[C]( - rules: List[Rule[C]], - p: List[C], - input: List[C], - suffix: List[C], - r: Rule[C] - ): Unit = { - require(!rules.isEmpty) - require(rulesInvariant(rules)) - require(rules.contains(r)) - require(input == p ++ suffix) - require(maxPrefix(rules, input) == Some(Token(p, r.tag, r.isSeparator), suffix)) - require({ - lemmaRuleInListAndRulesValidThenRuleIsValid(r, rules) - matchR(r.regex, p) - }) - decreases(rules.size) - - rules match { - case Cons(hd, tl) if hd == r => { - lemmaInvariantOnRulesThenOnTail(hd, tl) - if (tl.isEmpty) { - check(maxPrefixOneRule(r, input) == Some(Token(p, r.tag, r.isSeparator), suffix)) - } else { - lemmaNoDuplTagThenTailRulesCannotProduceHeadTagInTok(hd, tl, input) - assert(maxPrefix(tl, input).isEmpty || maxPrefix(tl, input).get._1.tag != r.tag) - check(maxPrefixOneRule(r, input) == Some(Token(p, r.tag, r.isSeparator), suffix)) - } - } - case Cons(hd, tl) if hd != r => { - lemmaInvariantOnRulesThenOnTail(hd, tl) - val otherTokSufOpt = maxPrefixOneRule(hd, input) - if (otherTokSufOpt.isEmpty) { - lemmaMaxPrefixTagSoFindMaxPrefOneRuleWithThisRule(tl, p, input, suffix, r) - } else { - assert(otherTokSufOpt.get._1.tag == hd.tag) - if (otherTokSufOpt.get._1.tag == r.tag) { - assert(hd.tag == r.tag) - lemmaNoDuplicateTagAndDiffIndexThenNoTwoRulesTagsEq(rules, hd, r) - check(false) - } else { - assert(otherTokSufOpt.get._1.tag != r.tag) - assert(maxPrefixOneRule(hd, input) != Some(Token(p, r.tag, r.isSeparator), suffix)) - assert(maxPrefix(tl, input) == Some(Token(p, r.tag, r.isSeparator), suffix)) - lemmaMaxPrefixTagSoFindMaxPrefOneRuleWithThisRule(tl, p, input, suffix, r) - } - - } - } - case Nil() => check(false) - - } - - } ensuring (_ => maxPrefixOneRule(r, input) == Some(Token(p, r.tag, r.isSeparator), suffix)) - - def lemmaNoDuplTagThenTailRulesCannotProduceHeadTagInTok[C](rHead: Rule[C], rTail: List[Rule[C]], input: List[C]): Unit = { - require(!rTail.isEmpty) - require(rulesInvariant(Cons(rHead, rTail))) - decreases(rTail) - - rTail match { - case Cons(hd, tl) => { - lemmaNoDuplicateCanReorder(rHead, hd, tl) - - lemmaNoDuplicateTagAndDiffIndexThenNoTwoRulesTagsEq(Cons(rHead, rTail), rHead, hd) - if (!tl.isEmpty) { - lemmaNoDupTagThenAlsoWithSubListAcc(List(hd.tag), Nil(), Cons(rHead, tl)) - lemmaNoDuplTagThenTailRulesCannotProduceHeadTagInTok(rHead, tl, input) - } - } - case Nil() => check(false) - } - - } ensuring (_ => maxPrefix(rTail, input).isEmpty || maxPrefix(rTail, input).get._1.tag != rHead.tag) - - def lemmaRuleReturnsPrefixSmallerEqualThanGlobalMaxPref[C]( - rules: List[Rule[C]], - p: List[C], - t: Token[C], - input: List[C], - suffix: List[C], - pBis: List[C], - suffixBis: List[C], - rBis: Rule[C], - tBis: Token[C] - ): Unit = { - decreases(rules) - require(p ++ suffix == input) - require(ListUtils.isPrefix(p, input)) - require(ListUtils.isPrefix(pBis, input)) - - require(!rules.isEmpty) - require(rulesInvariant(rules)) - require(rules.contains(rBis)) - require(maxPrefix(rules, input) == Some(t, suffix)) - require(ruleValid(rBis)) - require({ - ListUtils.lemmaIsPrefixRefl(input, input) - maxPrefixOneRule(rBis, input) == Some(tBis, suffixBis) - }) - require(tBis.tag == rBis.tag) - require(tBis.characters == pBis) - require(pBis ++ suffixBis == input) - - rules match { - case Cons(hd, Nil()) => () - case Cons(hd, tl) => { - if (hd == rBis) { - check(pBis.size <= p.size) - } else { - if (maxPrefixOneRule(hd, input) == Some(t, suffix)) { - val othersPrefix = maxPrefix(tl, input) - if (othersPrefix.isEmpty) { - lemmaMaxPrefixReturnsNoneThenAnyRuleReturnsNone(rBis, tl, input) - check(false) - } - val tokSuff = othersPrefix.get - val oPref = tokSuff._1.characters - val suff = tokSuff._2 - ListUtils.lemmaConcatTwoListThenFirstIsPrefix(oPref, suff) - lemmaInvariantOnRulesThenOnTail(hd, tl) - lemmaRuleReturnsPrefixSmallerEqualThanGlobalMaxPref(tl, oPref, tokSuff._1, input, suff, pBis, suffixBis, rBis, tBis) - check(pBis.size <= p.size) - } else { - lemmaInvariantOnRulesThenOnTail(hd, tl) - lemmaRuleReturnsPrefixSmallerEqualThanGlobalMaxPref(tl, p, t, input, suffix, pBis, suffixBis, rBis, tBis) - } - } - } - case Nil() => check(false) - } - - } ensuring (_ => pBis.size <= p.size) - - def lemmaMaxPrefixReturnsNoneThenAnyRuleReturnsNone[C]( - r: Rule[C], - rules: List[Rule[C]], - input: List[C] - ): Unit = { - require(!rules.isEmpty) - require(rulesValid(rules)) - require(rules.contains(r)) - - require(maxPrefix(rules, input).isEmpty) - decreases(rules) - - lemmaRuleInListAndRulesValidThenRuleIsValid(r, rules) - - rules match { - case Cons(hd, tl) if r == hd => () - case Cons(hd, tl) if r != hd => { - - lemmaMaxPrefixReturnsNoneThenAnyRuleReturnsNone(r, tl, input) - } - case Nil() => () - } - - } ensuring (_ => maxPrefixOneRule(r, input).isEmpty) - - def lemmaMaxPrefixOneRuleOutputsMaxPrefix[C]( - r: Rule[C], - p: List[C], - t: Token[C], - input: List[C], - suffix: List[C], - pBis: List[C] - ): Unit = { - decreases(input.size) - - require(p ++ suffix == input) - require(ListUtils.isPrefix(p, input)) - require(ListUtils.isPrefix(pBis, input)) - require(pBis.size <= input.size) - require(pBis.size > p.size) - - require(ruleValid(r)) - require(validRegex(r.regex)) - require(matchR(r.regex, p)) - require(t.characters == p) - require({ - ListUtils.lemmaIsPrefixRefl(input, input) - maxPrefixOneRule(r, input) == Some(t, suffix) - }) - - ListUtils.lemmaIsPrefixRefl(input, input) - - longestMatchNoBiggerStringMatch(r.regex, input, p, pBis) - } ensuring (_ => !matchR(r.regex, pBis)) - - def lemmaMaxPrefOneRuleReturnsNoneThenNoPrefMaxRegex[C]( - r: Rule[C], - p: List[C], - input: List[C] - ): Unit = { - require(ListUtils.isPrefix(p, input)) - require(ruleValid(r)) - require(maxPrefixOneRule(r, input) == None[(Token[C], List[C])]()) - - longestMatchNoBiggerStringMatch(r.regex, input, Nil(), p) - - } ensuring (_ => !matchR(r.regex, p)) - - def lemmaRuleInListAndRulesValidThenRuleIsValid[C](r: Rule[C], rules: List[Rule[C]]): Unit = { - require(rules.contains(r)) - require(rulesValid(rules)) - rules match { - case Cons(hd, tl) if (hd == r) => assert(ruleValid(r)) - case Cons(hd, tl) if (hd != r) => { - assert(tl.contains(r)) - lemmaRuleInListAndRulesValidThenRuleIsValid(r, tl) - } - case Nil() => assert(false) - } - } ensuring (_ => ruleValid(r)) - - def lemmaInvariantOnRulesThenOnTail[C](r: Rule[C], rules: List[Rule[C]]): Unit = { - require(rulesInvariant(Cons(r, rules))) - assert(rulesValid(Cons(r, rules)) && noDuplicateTag(Cons(r, rules), Nil())) - assert(rulesValid(rules)) - assert(noDuplicateTag(rules, List(r.tag))) - - lemmaNoDupTagThenAlsoWithSubListAcc(List(r.tag), Nil(), rules) - assert(noDuplicateTag(rules, Nil())) - - } ensuring (_ => rulesInvariant(rules)) - - def lemmaNoDuplicateCanReorder[C](e1: Rule[C], e2: Rule[C], l: List[Rule[C]]): Unit = { - require(noDuplicateTag(Cons(e1, Cons(e2, l)), List())) - - assert(noDuplicateTag(Cons(e1, Cons(e2, l)), List()) == noDuplicateTag(Cons(e2, l), List(e1.tag))) - assert(noDuplicateTag(Cons(e2, l), List(e1.tag)) == noDuplicateTag(l, List(e2.tag, e1.tag))) - assert(List(e2.tag, e1.tag).toSet == List(e1.tag, e2.tag).toSet) - lemmaNoDuplicateSameWithAccWithSameContent(l, List(e2.tag, e1.tag), List(e1.tag, e2.tag)) - assert(noDuplicateTag(l, List(e2.tag, e1.tag)) == noDuplicateTag(l, List(e1.tag, e2.tag))) // TODO - } ensuring (_ => noDuplicateTag(Cons(e2, Cons(e1, l)), List())) - - def lemmaNoDuplicateSameWithAccWithSameContent[C](l: List[Rule[C]], acc: List[String], newAcc: List[String]): Unit = { - require(noDuplicateTag(l, acc)) - require(acc.content == newAcc.content) - decreases(l) - - l match { - case Cons(hd, tl) => { - ListSpecs.subsetContains(acc, newAcc) - ListSpecs.subsetContains(newAcc, acc) - assert(acc.contains(hd.tag) == newAcc.contains(hd.tag)) - lemmaNoDuplicateSameWithAccWithSameContent(tl, Cons(hd.tag, acc), Cons(hd.tag, newAcc)) - } - case Nil() => () - } - - } ensuring (_ => noDuplicateTag(l, newAcc)) - - def lemmaNoDupTagThenAlsoWithSubListAcc[C](acc: List[String], newAcc: List[String], rules: List[Rule[C]]): Unit = { - require(ListSpecs.subseq(newAcc, acc)) - require(noDuplicateTag(rules, acc)) - - rules match { - case Cons(hd, tl) => { - lemmaNoDupTagThenAlsoWithSubListAcc(Cons(hd.tag, acc), Cons(hd.tag, newAcc), tl) - ListSpecs.subseqNotContains(newAcc, acc, hd.tag) - } - case Nil() => () - } - - } ensuring (_ => noDuplicateTag(rules, newAcc)) - - def lemmaNoDuplicateTagAndDiffIndexThenNoTwoRulesEq[C](rules: List[Rule[C]], r1: Rule[C], r2: Rule[C]): Unit = { - require(rules.contains(r1)) - require(rules.contains(r2)) - require(noDuplicateTag(rules)) - require(ListUtils.getIndex(rules, r1) < ListUtils.getIndex(rules, r2)) - - } ensuring (_ => r1 != r2) - - def lemmaNoDuplicateTagAndDiffIndexThenNoTwoRulesTagsEq[C](rules: List[Rule[C]], r1: Rule[C], r2: Rule[C]): Unit = { - require(rules.contains(r1)) - require(rules.contains(r2)) - require(noDuplicateTag(rules)) - require(ListUtils.getIndex(rules, r1) < ListUtils.getIndex(rules, r2)) - decreases(rules) - - if (rules.head == r1) { - lemmaNoDuplicateAndTagInAccThenRuleCannotHaveSame(rules.tail, r2, r1.tag, List(r1.tag)) - assert(noDuplicateTag(rules) == noDuplicateTag(rules.tail, List(r1.tag))) - } else { - lemmaNoDupTagThenAlsoWithSubListAcc(List(rules.head.tag), Nil(), rules.tail) - ListUtils.lemmaGetIndexBiggerAndHeadNotEqThenTailContains(rules, r1, r2) - lemmaNoDuplicateTagAndDiffIndexThenNoTwoRulesTagsEq(rules.tail, r1, r2) - } - - } ensuring (_ => r1.tag != r2.tag) - - def lemmaNoDuplicateAndTagInAccThenRuleCannotHaveSame[C](rules: List[Rule[C]], r: Rule[C], tag: String, acc: List[String]): Unit = { - require(acc.contains(tag)) - require(noDuplicateTag(rules, acc)) - require(rules.contains(r)) - - rules match { - case Nil() => check(false) - case Cons(hd, tl) if hd == r => () - case Cons(hd, tl) if hd != r => lemmaNoDuplicateAndTagInAccThenRuleCannotHaveSame(tl, r, tag, Cons(hd.tag, acc)) - } - } ensuring (_ => r.tag != tag) - - def lemmaNonSepRuleNotContainsCharContainedInASepRule[C]( - rules: List[Rule[C]], - rulesRec: List[Rule[C]], - rNSep: Rule[C], - rSep: Rule[C], - c: C - ): Unit = { - require(rulesInvariant(rules)) - require(rules.contains(rSep)) - require(rulesRec.contains(rNSep)) - require(rules.contains(rNSep)) - require(!rNSep.isSeparator) - require(rSep.isSeparator) - require(usedCharacters(rSep.regex).contains(c)) - require(sepAndNonSepRulesDisjointChars(rules, rulesRec)) - - rulesRec match { - case Cons(hd, tl) if hd == rNSep => lemmaNonSepRuleNotContainsCharContainedInASepRuleInner(rules, rNSep, rSep, c) - case Cons(hd, tl) => lemmaNonSepRuleNotContainsCharContainedInASepRule(rules, tl, rNSep, rSep, c) - case Nil() => () - } - - } ensuring (_ => !usedCharacters(rNSep.regex).contains(c)) - - def lemmaNonSepRuleNotContainsCharContainedInASepRuleInner[C](rules: List[Rule[C]], rNSep: Rule[C], rSep: Rule[C], c: C): Unit = { - require(rulesInvariant(rules)) - require(rules.contains(rSep)) - require(usedCharacters(rSep.regex).contains(c)) - require(!rNSep.isSeparator) - require(rSep.isSeparator) - require(ruleDisjointCharsFromAllFromOtherType(rNSep, rules)) - decreases(rules) - - rules match { - case Cons(hd, tl) if hd == rSep => - ListSpecs.forallContained(usedCharacters(rSep.regex), (x: C) => !usedCharacters(rNSep.regex).contains(x), c) - - case Cons(hd, tl) => { - lemmaInvariantOnRulesThenOnTail(hd, tl) - lemmaNonSepRuleNotContainsCharContainedInASepRuleInner(tl, rNSep, rSep, c) - } - case Nil() => () - } - - } ensuring (_ => !usedCharacters(rNSep.regex).contains(c)) - - def lemmaSepRuleNotContainsCharContainedInANonSepRule[C]( - rules: List[Rule[C]], - rulesRec: List[Rule[C]], - rNSep: Rule[C], - rSep: Rule[C], - c: C - ): Unit = { - require(rulesInvariant(rules)) - require(rules.contains(rSep)) - require(rulesRec.contains(rNSep)) - require(rules.contains(rNSep)) - require(!rNSep.isSeparator) - require(rSep.isSeparator) - require(usedCharacters(rNSep.regex).contains(c)) - require(sepAndNonSepRulesDisjointChars(rules, rulesRec)) - - rulesRec match { - case Cons(hd, tl) if hd == rNSep => lemmaSepRuleNotContainsCharContainedInANonSepRuleInner(rules, rNSep, rSep, c) - case Cons(hd, tl) => lemmaSepRuleNotContainsCharContainedInANonSepRule(rules, tl, rNSep, rSep, c) - case Nil() => () - } - - } ensuring (_ => !usedCharacters(rSep.regex).contains(c)) - - def lemmaSepRuleNotContainsCharContainedInANonSepRuleInner[C](rules: List[Rule[C]], rNSep: Rule[C], rSep: Rule[C], c: C): Unit = { - require(rulesInvariant(rules)) - require(rules.contains(rSep)) - require(usedCharacters(rNSep.regex).contains(c)) - require(!rNSep.isSeparator) - require(rSep.isSeparator) - require(ruleDisjointCharsFromAllFromOtherType(rNSep, rules)) - decreases(rules) - - rules match { - case Cons(hd, tl) if hd == rSep => - ListSpecs.forallContained(usedCharacters(rNSep.regex), (x: C) => !usedCharacters(rSep.regex).contains(x), c) - - case Cons(hd, tl) => { - lemmaInvariantOnRulesThenOnTail(hd, tl) - lemmaSepRuleNotContainsCharContainedInANonSepRuleInner(tl, rNSep, rSep, c) - } - case Nil() => () - } - - } ensuring (_ => !usedCharacters(rSep.regex).contains(c)) - - def lemmaRulesProduceEachTokenIndividuallyThenForAnyToken[C](rules: List[Rule[C]], tokens: List[Token[C]], t: Token[C]): Unit = { - require(!rules.isEmpty) - require(rulesInvariant(rules)) - require(tokens.contains(t)) - require(rulesProduceEachTokenIndividually(rules, tokens)) - decreases(tokens) - - tokens match { - case Cons(hd, tl) if hd == t => () - case Cons(hd, tl) => lemmaRulesProduceEachTokenIndividuallyThenForAnyToken(rules, tl, t) - case Nil() => () - } - } ensuring (_ => rulesProduceIndivualToken(rules, t)) - - } - -} diff --git a/lexers/regex/VerifiedRegex.scala b/lexers/regex/VerifiedRegex.scala deleted file mode 100644 index 3d8328c6..00000000 --- a/lexers/regex/VerifiedRegex.scala +++ /dev/null @@ -1,1119 +0,0 @@ -/** Author: Samuel Chassot - */ - -import stainless.equations._ -import stainless.lang._ -import stainless.collection._ -import stainless.annotation._ -import stainless.proof._ -import scala.runtime.Statics - -object VerifiedRegex { - abstract sealed class Regex[C] {} - def validRegex[C](r: Regex[C]): Boolean = r match { - case ElementMatch(c) => true - case Star(r) => !nullable(r) && !isEmptyLang(r) && validRegex(r) - case Union(rOne, rTwo) => validRegex(rOne) && validRegex(rTwo) - case Concat(rOne, rTwo) => validRegex(rOne) && validRegex(rTwo) - case EmptyExpr() => true - case EmptyLang() => true - } - - def regexDepth[C](r: Regex[C]): BigInt ={ - decreases(r) - r match { - case ElementMatch(c) => BigInt(1) - case Star(r) => BigInt(1) + regexDepth(r) - case Union(rOne, rTwo) => BigInt(1) + Utils.maxBigInt(regexDepth(rOne), regexDepth(rTwo)) - case Concat(rOne, rTwo) => BigInt(1) + Utils.maxBigInt(regexDepth(rOne), regexDepth(rTwo)) - case EmptyExpr() => BigInt(1) - case EmptyLang() => BigInt(1) - } -} ensuring (res => - res > 0 && (r match { - case Union(rOne, rTwo) => res > regexDepth(rOne) && res > regexDepth(rTwo) - case Concat(rOne, rTwo) => res > regexDepth(rOne) && res > regexDepth(rTwo) - case Star(r) => res > regexDepth(r) - case _ => res == BigInt(1) - }) - ) - - case class ElementMatch[C](c: C) extends Regex[C] - case class Star[C](reg: Regex[C]) extends Regex[C] - case class Union[C](regOne: Regex[C], regTwo: Regex[C]) extends Regex[C] - case class Concat[C](regOne: Regex[C], regTwo: Regex[C]) extends Regex[C] - - /** Regex that accepts only the empty string: represents the language {""} - */ - case class EmptyExpr[C]() extends Regex[C] - - /** Regex that accepts nothing: represents the empty language - */ - case class EmptyLang[C]() extends Regex[C] - - def usedCharacters[C](r: Regex[C]): List[C] = { - r match { - case EmptyExpr() => Nil[C]() - case EmptyLang() => Nil[C]() - case ElementMatch(c) => List(c) - case Star(r) => usedCharacters(r) - case Union(rOne, rTwo) => usedCharacters(rOne) ++ usedCharacters(rTwo) - case Concat(rOne, rTwo) => usedCharacters(rOne) ++ usedCharacters(rTwo) - } - } - - def firstChars[C](r: Regex[C]): List[C] = { - r match { - case EmptyExpr() => Nil[C]() - case EmptyLang() => Nil[C]() - case ElementMatch(c) => List(c) - case Star(r) => firstChars(r) - case Union(rOne, rTwo) => firstChars(rOne) ++ firstChars(rTwo) - case Concat(rOne, rTwo) if nullable(rOne) => firstChars(rOne) ++ firstChars(rTwo) - case Concat(rOne, rTwo) if !nullable(rOne) => firstChars(rOne) - } - } - - def nullable[C](r: Regex[C]): Boolean = { - r match { - case EmptyExpr() => true - case EmptyLang() => false - case ElementMatch(c) => false - case Star(r) => true - case Union(rOne, rTwo) => nullable(rOne) || nullable(rTwo) - case Concat(rOne, rTwo) => nullable(rOne) && nullable(rTwo) - } - } - - @inline - def isEmptyExpr[C](r: Regex[C]): Boolean = { - r match { - case EmptyExpr() => true - case _ => false - } - } - @inline - def isEmptyLang[C](r: Regex[C]): Boolean = { - r match { - case EmptyLang() => true - case _ => false - } - } - @inline - def isElementMatch[C](r: Regex[C]): Boolean = { - r match { - case ElementMatch(_) => true - case _ => false - } - } - @inline - def elementMatchIsChar[C](r: Regex[C], c: C): Boolean = { - require(isElementMatch(r)) - r match { - case ElementMatch(cc) => c == cc - } - } - @inline - def isStar[C](r: Regex[C]): Boolean = { - r match { - case Star(_) => true - case _ => false - } - } - @inline - def isUnion[C](r: Regex[C]): Boolean = { - r match { - case Union(_, _) => true - case _ => false - } - } - @inline - def unionInnersEquals[C](r: Regex[C], r1: Regex[C], r2: Regex[C]): Boolean = { - require(isUnion(r)) - r match { - case Union(rOne, rTwo) => r1 == rOne && r2 == rTwo - } - } - - @inline - def isConcat[C](r: Regex[C]): Boolean = { - r match { - case Concat(_, _) => true - case _ => false - } - } -} - -object VerifiedRegexMatcher { - import RegularExpression._ - import ListUtils._ - - def derivativeStep[C](r: Regex[C], a: C): Regex[C] = { - require(validRegex(r)) - decreases(r) - val res: Regex[C] = r match { - case EmptyExpr() => EmptyLang() - case EmptyLang() => EmptyLang() - case ElementMatch(c) => if (a == c) EmptyExpr() else EmptyLang() - case Union(rOne, rTwo) => Union(derivativeStep(rOne, a), derivativeStep(rTwo, a)) - case Star(rInner) => Concat(derivativeStep(rInner, a), Star(rInner)) - case Concat(rOne, rTwo) => { - if (nullable(rOne)) Union(Concat(derivativeStep(rOne, a), rTwo), derivativeStep(rTwo, a)) - else Union(Concat(derivativeStep(rOne, a), rTwo), EmptyLang()) - } - } - res - } ensuring (res => validRegex(res)) - - def derivative[C](r: Regex[C], input: List[C]): Regex[C] = { - require(validRegex(r)) - input match { - case Cons(hd, tl) => derivative(derivativeStep(r, hd), tl) - case Nil() => r - } - } ensuring (res => validRegex(res)) - - def matchR[C](r: Regex[C], input: List[C]): Boolean = { - require(validRegex(r)) - decreases(input.size) - if (input.isEmpty) nullable(r) else matchR(derivativeStep(r, input.head), input.tail) - } ensuring (res => - r match { - case EmptyExpr() => res == input.isEmpty - case EmptyLang() => !res - case ElementMatch(c) => - (res && !input.isEmpty && input.tail.isEmpty && input.head == c) || (!res && !(!input.isEmpty && input.tail.isEmpty && input.head == c)) - case _ => true - } - ) - - def matchRSpec[C](r: Regex[C], s: List[C]): Boolean = { - require(validRegex(r)) - decreases(s.size + regexDepth(r)) - r match { - case EmptyExpr() => s.isEmpty - case EmptyLang() => false - case ElementMatch(c) => s == List(c) - case Union(r1, r2) => matchRSpec(r1, s) || matchRSpec(r2, s) - case Star(rInner) => s.isEmpty || findConcatSeparation(rInner, Star(rInner), Nil(), s, s).isDefined - case Concat(r1, r2) => findConcatSeparation(r1, r2, Nil(), s, s).isDefined - } - } - - def mainMatchTheorem[C](r: Regex[C], s: List[C]): Unit = { - require(validRegex(r)) - decreases(s.size + regexDepth(r)) - r match { - case EmptyExpr() => () - case EmptyLang() => () - case ElementMatch(c) => () - case Union(r1, r2) => { - if (matchR(r, s)) { - lemmaRegexUnionAcceptsThenOneOfTheTwoAccepts(r1, r2, s) - mainMatchTheorem(r1, s) - mainMatchTheorem(r2, s) - } else { - if (matchR(r1, s)) { - lemmaRegexAcceptsStringThenUnionWithAnotherAcceptsToo(r1, r2, s) - check(false) - } - if (matchR(r2, s)) { - lemmaRegexAcceptsStringThenUnionWithAnotherAcceptsToo(r2, r1, s) - lemmaReversedUnionAcceptsSameString(r2, r1, s) - check(false) - } - mainMatchTheorem(r1, s) - mainMatchTheorem(r2, s) - } - - } - case Star(rInner) => { - if (s.isEmpty) { - () - } else { - val cut = findConcatSeparation(rInner, Star(rInner), Nil(), s, s) - if (cut.isDefined) { - mainMatchTheorem(rInner, cut.get._1) - mainMatchTheorem(Star(rInner), cut.get._2) - if (!matchR(r, s)) { - lemmaFindSeparationIsDefinedThenConcatMatches(rInner, Star(rInner), cut.get._1, cut.get._2, s) - check(false) - } - } else { - if (matchR(r, s)) { - lemmaStarAppConcat(rInner, s) - lemmaConcatAcceptsStringThenFindSeparationIsDefined(rInner, Star(rInner), s) - check(false) - } - } - } - } - case Concat(r1, r2) => { - if (matchR(r, s)) { - lemmaConcatAcceptsStringThenFindSeparationIsDefined(r1, r2, s) - } else { - val cut = findConcatSeparation(r1, r2, Nil(), s, s) - if (cut.isDefined) { - lemmaFindSeparationIsDefinedThenConcatMatches(r1, r2, cut.get._1, cut.get._2, s) - check(false) - } - } - - } - } - } ensuring (matchR(r, s) == matchRSpec(r, s)) - - /** Enumerate all cuts in s and returns one that works, i.e., r1 matches s1 and r2 matches s2 Specifically, it is the right most one, i.e., s2 is the largest, if multiple exists Returns None is no valid cut exists - * - * @param r1 - * @param r2 - * @param s1 - * @param s2 - * @param s - */ - def findConcatSeparation[C](r1: Regex[C], r2: Regex[C], s1: List[C], s2: List[C], s: List[C]): Option[(List[C], List[C])] = { - require(validRegex(r1)) - require(validRegex(r2)) - require(s1 ++ s2 == s) - decreases(s2.size) - - val res: Option[(List[C], List[C])] = (s1, s2) match { - case (_, _) if matchR(r1, s1) && matchR(r2, s2) => Some((s1, s2)) - case (_, Nil()) => None() - case (_, Cons(hd2, tl2)) => { - lemmaMoveElementToOtherListKeepsConcatEq(s1, hd2, tl2, s) - assert(s1 ++ List(hd2) ++ tl2 == s) - findConcatSeparation(r1, r2, s1 ++ List(hd2), tl2, s) - } - } - res - - } ensuring (res => (res.isDefined && matchR(r1, res.get._1) && matchR(r2, res.get._2) && res.get._1 ++ res.get._2 == s) || !res.isDefined) - - def findLongestMatch[C](r: Regex[C], input: List[C]): (List[C], List[C]) = { - require(validRegex(r)) - findLongestMatchInner(r, Nil(), input) - } ensuring (res => res._1 ++ res._2 == input) - - def findLongestMatchInner[C](r: Regex[C], testedP: List[C], totalInput: List[C]): (List[C], List[C]) = { - require(validRegex(r)) - require(ListUtils.isPrefix(testedP, totalInput)) - decreases(totalInput.size - testedP.size) - - if (testedP == totalInput) { - if (nullable(r)) { - (testedP, Nil[C]()) - } else { - (Nil[C](), totalInput) - } - } else { - ListUtils.lemmaIsPrefixThenSmallerEqSize(testedP, totalInput) - if (testedP.size == totalInput.size) { - ListUtils.lemmaIsPrefixRefl(totalInput, totalInput) - ListUtils.lemmaIsPrefixSameLengthThenSameList(totalInput, testedP, totalInput) - check(false) - } - assert(testedP.size < totalInput.size) - val suffix = ListUtils.getSuffix(totalInput, testedP) - val newP = testedP ++ List(suffix.head) - lemmaAddHeadSuffixToPrefixStillPrefix(testedP, totalInput) - if (nullable(r)) { - val recursive = findLongestMatchInner(derivativeStep(r, suffix.head), newP, totalInput) - if (recursive._1.isEmpty) { - (testedP, ListUtils.getSuffix(totalInput, testedP)) - } else { - recursive - } - } else { - findLongestMatchInner(derivativeStep(r, suffix.head), newP, totalInput) - } - } - } ensuring (res => res._1 ++ res._2 == totalInput && (res._1.isEmpty || res._1.size >= testedP.size)) - - // Longest match theorems - def longestMatchIsAcceptedByMatchOrIsEmpty[C](r: Regex[C], input: List[C]): Unit = { - require(validRegex(r)) - longestMatchIsAcceptedByMatchOrIsEmptyRec(r, r, Nil(), input) - - } ensuring (findLongestMatchInner(r, Nil(), input)._1.isEmpty || matchR(r, findLongestMatchInner(r, Nil(), input)._1)) - - def longestMatchNoBiggerStringMatch[C](baseR: Regex[C], input: List[C], returnP: List[C], bigger: List[C]): Unit = { - require(validRegex(baseR)) - require(ListUtils.isPrefix(returnP, input)) - require(ListUtils.isPrefix(bigger, input)) - require(bigger.size >= returnP.size) - require(findLongestMatchInner(baseR, Nil(), input)._1 == returnP) - - if (bigger.size == returnP.size) { - ListUtils.lemmaIsPrefixSameLengthThenSameList(bigger, returnP, input) - } else { - if (matchR(baseR, bigger)) { - lemmaKnownAcceptedStringThenFromSmallPAtLeastThat(baseR, baseR, input, Nil(), bigger) - check(false) - } - } - - } ensuring (bigger == returnP || !matchR(baseR, bigger)) - - def lemmaIfMatchRThenLongestMatchFromThereReturnsAtLeastThis[C](baseR: Regex[C], r: Regex[C], input: List[C], testedP: List[C]): Unit = { - require(validRegex(baseR)) - require(validRegex(r)) - require(ListUtils.isPrefix(testedP, input)) - require(matchR(baseR, testedP)) - require(derivative(baseR, testedP) == r) - - lemmaMatchRIsSameAsWholeDerivativeAndNil(baseR, testedP) - assert(matchR(r, Nil())) - assert(nullable(r)) - - } ensuring (findLongestMatchInner(r, testedP, input)._1.size >= testedP.size) - - def lemmaKnownAcceptedStringThenFromSmallPAtLeastThat[C](baseR: Regex[C], r: Regex[C], input: List[C], testedP: List[C], knownP: List[C]): Unit = { - require(validRegex(baseR)) - require(validRegex(r)) - require(ListUtils.isPrefix(testedP, input)) - require(ListUtils.isPrefix(knownP, input)) - require(knownP.size >= testedP.size) - require(matchR(baseR, knownP)) - require(derivative(baseR, testedP) == r) - decreases(knownP.size - testedP.size) - - if (testedP.size == knownP.size) { - ListUtils.lemmaIsPrefixSameLengthThenSameList(testedP, knownP, input) - lemmaIfMatchRThenLongestMatchFromThereReturnsAtLeastThis(baseR, r, input, testedP) - check(findLongestMatchInner(r, testedP, input)._1.size >= knownP.size) - } else { - assert(testedP.size < input.size) - val suffix = ListUtils.getSuffix(input, testedP) - val newP = testedP ++ List(suffix.head) - lemmaAddHeadSuffixToPrefixStillPrefix(testedP, input) - - lemmaDerivativeOnLWithANewCharIsANewDerivativeStep(baseR, r, testedP, suffix.head) - lemmaKnownAcceptedStringThenFromSmallPAtLeastThat(baseR, derivativeStep(r, suffix.head), input, newP, knownP) - - check(findLongestMatchInner(r, testedP, input)._1.size >= knownP.size) - } - - } ensuring (findLongestMatchInner(r, testedP, input)._1.size >= knownP.size) - - def longestMatchIsAcceptedByMatchOrIsEmptyRec[C](baseR: Regex[C], r: Regex[C], testedP: List[C], input: List[C]): Unit = { - require(validRegex(baseR)) - require(ListUtils.isPrefix(testedP, input)) - require(derivative(baseR, testedP) == r) - decreases(input.size - testedP.size) - - if (findLongestMatchInner(r, testedP, input)._1.isEmpty) { - () - } else { - if (testedP == input) { - if (nullable(r)) { - lemmaMatchRIsSameAsWholeDerivativeAndNil(baseR, testedP) - } else { - () - } - } else { - ListUtils.lemmaIsPrefixThenSmallerEqSize(testedP, input) - if (testedP.size == input.size) { - ListUtils.lemmaIsPrefixRefl(input, input) - ListUtils.lemmaIsPrefixSameLengthThenSameList(input, testedP, input) - check(false) - } - assert(testedP.size < input.size) - val suffix = ListUtils.getSuffix(input, testedP) - val newP = testedP ++ List(suffix.head) - lemmaAddHeadSuffixToPrefixStillPrefix(testedP, input) - if (nullable(r)) { - val recursive = findLongestMatchInner(derivativeStep(r, suffix.head), newP, input) - if (recursive._1.isEmpty) { - lemmaMatchRIsSameAsWholeDerivativeAndNil(baseR, testedP) - } else { - lemmaDerivativeOnLWithANewCharIsANewDerivativeStep(baseR, r, testedP, suffix.head) - longestMatchIsAcceptedByMatchOrIsEmptyRec(baseR, derivativeStep(r, suffix.head), newP, input) - } - } else { - lemmaDerivativeOnLWithANewCharIsANewDerivativeStep(baseR, r, testedP, suffix.head) - longestMatchIsAcceptedByMatchOrIsEmptyRec(baseR, derivativeStep(r, suffix.head), newP, input) - } - } - } - - } ensuring (findLongestMatchInner(r, testedP, input)._1.isEmpty || matchR(baseR, findLongestMatchInner(r, testedP, input)._1)) - - def lemmaMatchRIsSameAsWholeDerivativeAndNil[C](r: Regex[C], input: List[C]): Unit = { - require(validRegex(r)) - input match { - case Cons(hd, tl) => lemmaMatchRIsSameAsWholeDerivativeAndNil(derivativeStep(r, hd), tl) - case Nil() => () - } - } ensuring (matchR(r, input) == matchR(derivative(r, input), Nil())) - - def lemmaDerivativeOnLWithANewCharIsANewDerivativeStep[C](baseR: Regex[C], r: Regex[C], input: List[C], c: C): Unit = { - require(validRegex(baseR)) - require(derivative(baseR, input) == r) - - input match { - case Cons(hd, tl) => lemmaDerivativeOnLWithANewCharIsANewDerivativeStep(derivativeStep(baseR, hd), r, tl, c) - case Nil() => () - } - - } ensuring (derivative(baseR, input ++ List(c)) == derivativeStep(r, c)) - - // Basic lemmas - def lemmaIfAcceptEmptyStringThenNullable[C](r: Regex[C], s: List[C]): Unit = { - require(validRegex(r)) - require(s.isEmpty) - require(matchR(r, s)) - } ensuring (nullable(r)) - - def lemmaRegexAcceptsStringThenDerivativeAcceptsTail[C](r: Regex[C], s: List[C]): Unit = { - require(validRegex(r)) - require(matchR(r, s)) - - } ensuring (if (s.isEmpty) nullable(r) else matchR(derivativeStep(r, s.head), s.tail)) - - // EmptyString Lemma - def lemmaRegexEmptyStringAcceptsTheEmptyString[C](r: EmptyExpr[C]): Unit = { - require(validRegex(r)) - } ensuring (matchR(r, List())) - - // Single Character Lemma - def lemmaElementRegexAcceptsItsCharacterAndOnlyIt[C]( - r: ElementMatch[C], - c: C, - d: C - ): Unit = { - require(validRegex(r) && r == ElementMatch(c)) - require(c != d) - } ensuring (matchR(r, List(c)) && !matchR(r, List(d))) - - def lemmaElementRegexDoesNotAcceptMultipleCharactersString[C]( - r: ElementMatch[C], - c: C, - s: List[C] - ): Unit = { - require(validRegex(r) && r == ElementMatch(c)) - require(!s.isEmpty) - } ensuring (!matchR(r, Cons(c, s))) - - // Union lemmas - def lemmaRegexAcceptsStringThenUnionWithAnotherAcceptsToo[C]( - r1: Regex[C], - r2: Regex[C], - s: List[C] - ): Unit = { - require(validRegex(r1) && validRegex(r2)) - require(matchR(r1, s)) - - s match { - case Cons(hd, tl) => { - lemmaRegexAcceptsStringThenUnionWithAnotherAcceptsToo(derivativeStep(r1, hd), derivativeStep(r2, hd), tl) - assert(matchR(Union(r1, r2), s)) - } - case Nil() => assert(matchR(Union(r1, r2), s)) - } - } ensuring (matchR(Union(r1, r2), s)) - - def lemmaRegexUnionAcceptsThenOneOfTheTwoAccepts[C](r1: Regex[C], r2: Regex[C], s: List[C]): Unit = { - require(validRegex(r1) && validRegex(r2)) - require(matchR(Union(r1, r2), s)) - - s match { - case Cons(hd, tl) => { - lemmaRegexUnionAcceptsThenOneOfTheTwoAccepts(derivativeStep(r1, hd), derivativeStep(r2, hd), tl) - } - case Nil() => - } - } ensuring (matchR(r1, s) || matchR(r2, s)) - - def lemmaReversedUnionAcceptsSameString[C]( - r1: Regex[C], - r2: Regex[C], - s: List[C] - ): Unit = { - require(validRegex(r1) && validRegex(r2)) - require(matchR(Union(r1, r2), s)) - - s match { - case Cons(hd, tl) => { - lemmaReversedUnionAcceptsSameString(derivativeStep(r1, hd), derivativeStep(r2, hd), tl) - assert(matchR(Union(r2, r1), s)) - } - case Nil() => assert(matchR(Union(r1, r2), s)) - } - } ensuring (matchR(Union(r2, r1), s)) - - // Concat lemmas - - def lemmaRegexConcatWithNullableAcceptsSameStr[C]( - r1: Regex[C], - r2: Regex[C], - s: List[C] - ): Unit = { - require(validRegex(r1)) - require(validRegex(r2)) - require(matchR(r1, s)) - require(nullable(r2)) - - val newR = Concat(r2, r1) - - s match { - case Cons(hd, tl) => { - assert(nullable(r2)) - assert( - derivativeStep(newR, hd) == Union(Concat(derivativeStep(r2, hd), r1), derivativeStep(r1, hd)) - ) - lemmaRegexAcceptsStringThenDerivativeAcceptsTail(r1, s) - lemmaRegexAcceptsStringThenUnionWithAnotherAcceptsToo( - derivativeStep(r1, hd), - Concat(derivativeStep(r2, hd), r1), - tl - ) - lemmaReversedUnionAcceptsSameString(derivativeStep(r1, hd), Concat(derivativeStep(r2, hd), r1), tl) - } - case Nil() => () - } - } ensuring (matchR(Concat(r2, r1), s)) - - def lemmaTwoRegexMatchThenConcatMatchesConcatString[C]( - r1: Regex[C], - r2: Regex[C], - s1: List[C], - s2: List[C] - ): Unit = { - require(validRegex(r1) && validRegex(r2)) - require(matchR(r1, s1)) - require(matchR(r2, s2)) - decreases(s1) - - s1 match { - case Cons(hd, tl) => { - lemmaTwoRegexMatchThenConcatMatchesConcatString(derivativeStep(r1, hd), r2, tl, s2) - assert(matchR(Concat(derivativeStep(r1, hd), r2), tl ++ s2)) - if (nullable(r1)) { - assert( - derivativeStep(Concat(r1, r2), hd) == Union(Concat(derivativeStep(r1, hd), r2), derivativeStep(r2, hd)) - ) - lemmaRegexAcceptsStringThenUnionWithAnotherAcceptsToo( - Concat(derivativeStep(r1, hd), r2), - derivativeStep(r2, hd), - tl ++ s2 - ) - } else { - assert(derivativeStep(Concat(r1, r2), hd) == Union(Concat(derivativeStep(r1, hd), r2), EmptyLang())) - lemmaRegexAcceptsStringThenUnionWithAnotherAcceptsToo( - Concat(derivativeStep(r1, hd), r2), - EmptyLang(), - tl ++ s2 - ) - assert(matchR(Concat(r1, r2), s1 ++ s2)) - } - } - case Nil() => - lemmaRegexConcatWithNullableAcceptsSameStr(r2, r1, s2) - - } - } ensuring (matchR(Concat(r1, r2), s1 ++ s2)) - - def lemmaFindSeparationIsDefinedThenConcatMatches[C](r1: Regex[C], r2: Regex[C], s1: List[C], s2: List[C], s: List[C]): Unit = { - require(validRegex(r1)) - require(validRegex(r2)) - require(s == s1 ++ s2) - require(findConcatSeparation(r1, r2, Nil(), s, s).isDefined) - require(findConcatSeparation(r1, r2, Nil(), s, s).get == (s1, s2)) - - lemmaTwoRegexMatchThenConcatMatchesConcatString(r1, r2, s1, s2) - - } ensuring (matchR(Concat(r1, r2), s1 ++ s2)) - - def lemmaR1MatchesS1AndR2MatchesS2ThenFindSeparationFindsAtLeastThem[C]( - r1: Regex[C], - r2: Regex[C], - s1: List[C], - s2: List[C], - s: List[C], - s1Rec: List[C], - s2Rec: List[C] - ): Unit = { - require(validRegex(r1)) - require(validRegex(r2)) - require(s1 ++ s2 == s) - require(ListUtils.isPrefix(s1Rec, s1)) - require(s1Rec ++ s2Rec == s) - require(matchR(r1, s1)) - require(matchR(r2, s2)) - decreases(s2Rec.size) - - (s1Rec, s2Rec) match { - case (_, _) if matchR(r1, s1Rec) && matchR(r2, s2Rec) => () - case (_, Nil()) => { - assert(s1Rec.size == s.size) - assert(s1Rec.size == s1.size) - assert(s1Rec == s1) - assert(s2Rec == s2) - assert(findConcatSeparation(r1, r2, s1Rec, s2Rec, s) == Some(s1Rec, s2Rec)) - } - case (_, Cons(hd2, tl2)) => { - ListUtils.lemmaConcatTwoListThenFirstIsPrefix(s1, s2) - ListUtils.lemmaConcatTwoListThenFirstIsPrefix(s1Rec, s2Rec) - if (s1Rec == s1) { - ListUtils.lemmaConcatTwoListThenFirstIsPrefix(s1, s2) - ListUtils.lemmaSamePrefixThenSameSuffix(s1, s2, s1Rec, s2Rec, s) - check(false) - } - lemmaMoveElementToOtherListKeepsConcatEq(s1Rec, hd2, tl2, s) - ListUtils.lemmaConcatTwoListThenFirstIsPrefix(s1Rec ++ List(hd2), tl2) - if (s1Rec.size == s1.size) { - ListUtils.lemmaIsPrefixSameLengthThenSameList(s1, s1Rec, s) - check(false) - } - - ListUtils.lemmaPrefixFromSameListAndStrictlySmallerThenPrefixFromEachOther(s1, s1Rec ++ List(hd2), s) - lemmaR1MatchesS1AndR2MatchesS2ThenFindSeparationFindsAtLeastThem(r1, r2, s1, s2, s, s1Rec ++ List(hd2), tl2) - } - } - - } ensuring (findConcatSeparation(r1, r2, s1Rec, s2Rec, s).isDefined) - - def lemmaConcatAcceptsStringThenFindSeparationIsDefined[C](r1: Regex[C], r2: Regex[C], s: List[C]): Unit = { - require(validRegex(r1)) - require(validRegex(r2)) - require(matchR(Concat(r1, r2), s)) - decreases(s) - - val r = Concat(r1, r2) - s match { - case Cons(hd, tl) => { - assert(matchR(derivativeStep(Concat(r1, r2), hd), tl)) - // if (nullable(rOne)) Union(Concat(derivativeStep(rOne, a), rTwo), derivativeStep(rTwo, a)) - // else Union(Concat(derivativeStep(rOne, a), rTwo), EmptyLang()) - if (nullable(r1)) { - assert(derivativeStep(r, hd) == Union(Concat(derivativeStep(r1, hd), r2), derivativeStep(r2, hd))) - lemmaRegexUnionAcceptsThenOneOfTheTwoAccepts(Concat(derivativeStep(r1, hd), r2), derivativeStep(r2, hd), tl) - if (matchR(Concat(derivativeStep(r1, hd), r2), tl)) { - lemmaConcatAcceptsStringThenFindSeparationIsDefined(derivativeStep(r1, hd), r2, tl) - assert(findConcatSeparation(derivativeStep(r1, hd), r2, Nil(), tl, tl).isDefined) - val (s1, s2) = findConcatSeparation(derivativeStep(r1, hd), r2, Nil(), tl, tl).get - lemmaR1MatchesS1AndR2MatchesS2ThenFindSeparationFindsAtLeastThem(r1, r2, Cons(hd, s1), s2, s, Nil(), s) - } else { - assert(matchR(derivativeStep(r2, hd), tl)) - } - } else { - assert(derivativeStep(r, hd) == Union(Concat(derivativeStep(r1, hd), r2), EmptyLang())) - lemmaRegexUnionAcceptsThenOneOfTheTwoAccepts(Concat(derivativeStep(r1, hd), r2), EmptyLang(), tl) - lemmaEmptyLangDerivativeIsAFixPoint(EmptyLang(), tl) - assert(matchR(Concat(derivativeStep(r1, hd), r2), tl)) - lemmaConcatAcceptsStringThenFindSeparationIsDefined(derivativeStep(r1, hd), r2, tl) - val (s1, s2) = findConcatSeparation(derivativeStep(r1, hd), r2, Nil(), tl, tl).get - lemmaR1MatchesS1AndR2MatchesS2ThenFindSeparationFindsAtLeastThem(r1, r2, Cons(hd, s1), s2, s, Nil(), s) - } - } - case Nil() => { - assert(nullable(r1) && nullable(r2)) - assert(findConcatSeparation(r1, r2, Nil(), Nil(), Nil()) == Some((Nil[C](), Nil[C]()))) - } - } - - } ensuring (findConcatSeparation(r1, r2, Nil(), s, s).isDefined) - - // Star lemmas - def lemmaStarAcceptsEmptyString[C](r: Star[C]): Unit = { - require(validRegex(r)) - } ensuring (matchR(r, List())) - - def lemmaStarApp[C](r: Regex[C], s1: List[C], s2: List[C]): Unit = { - require(validRegex(Star(r))) - require(matchR(r, s1)) - require(matchR(Star(r), s2)) - - s1 match { - case Cons(hd, tl) => { - assert(derivativeStep(Star(r), hd) == Concat(derivativeStep(r, hd), Star(r))) - lemmaTwoRegexMatchThenConcatMatchesConcatString(derivativeStep(r, hd), Star(r), tl, s2) - } - case Nil() => () - } - } ensuring (matchR(Star(r), s1 ++ s2)) - - def lemmaStarAppConcat[C](r: Regex[C], s: List[C]): Unit = { - require(validRegex(Star(r))) - require(matchR(Star(r), s)) - - s match { - case Cons(hd, tl) => { - assert(derivativeStep(Star(r), hd) == Concat(derivativeStep(r, hd), Star(r))) - val r1 = derivativeStep(r, hd) - val r2 = Star(r) - lemmaConcatAcceptsStringThenFindSeparationIsDefined(r1, r2, tl) - val cut = findConcatSeparation(r1, r2, Nil(), tl, tl) - lemmaTwoRegexMatchThenConcatMatchesConcatString(r, Star(r), Cons(hd, cut.get._1), cut.get._2) - } - case Nil() => () - } - } ensuring (s.isEmpty || matchR(Concat(r, Star(r)), s)) - - // usedCharacters lemmas --------------------------------------------------------------------------------------------------- - - def lemmaRegexCannotMatchAStringContainingACharItDoesNotContain[C](r: Regex[C], s: List[C], c: C): Unit = { - require(validRegex(r)) - require(s.contains(c)) - require(!usedCharacters(r).contains(c)) - decreases(s) - - s match { - case Cons(hd, tl) if hd == c => lemmaRegexCannotMatchAStringStartingWithACharItDoesNotContain(r, s, c) - case Cons(hd, tl) if hd != c => { - lemmaDerivativeStepDoesNotAddCharToUsedCharacters(r, hd, c) - lemmaRegexCannotMatchAStringContainingACharItDoesNotContain(derivativeStep(r, hd), tl, c) - } - case Nil() => check(false) - } - - } ensuring (!matchR(r, s)) - - def lemmaRegexCannotMatchAStringStartingWithACharItDoesNotContain[C](r: Regex[C], s: List[C], c: C): Unit = { - require(validRegex(r)) - require(s.contains(c)) - require(s.head == c) - require(!usedCharacters(r).contains(c)) - - if (matchR(r, s)) { - lemmaMatchRIsSameAsWholeDerivativeAndNil(r, s) - lemmaDerivativeAfterDerivativeStepIsNullableThenUsedCharsContainsHead(r, c, s.tail) - check(false) - } - - } ensuring (!matchR(r, s)) - - def lemmaRegexCannotMatchAStringStartingWithACharWhichIsNotInFirstChars[C](r: Regex[C], s: List[C], c: C): Unit = { - require(validRegex(r)) - require(s.contains(c)) - require(s.head == c) - require(!firstChars(r).contains(c)) - - if (matchR(r, s)) { - lemmaMatchRIsSameAsWholeDerivativeAndNil(r, s) - lemmaDerivAfterDerivStepIsNullableThenFirstCharsContainsHead(r, c, s.tail) - check(false) - } - - } ensuring (!matchR(r, s)) - - // not used - def lemmaRIsNotNullableDerivativeStepIsThenUsedCharContainsC[C](r: Regex[C], c: C): Unit = { - require(validRegex(r)) - require(!nullable(r)) - require(nullable(derivativeStep(r, c))) - decreases(r) - - r match { - case EmptyExpr() => check(false) - case EmptyLang() => () - case ElementMatch(a) => () - case Union(rOne, rTwo) => { - if (nullable(rOne)) { - check(false) - } - if (nullable(rTwo)) { - check(false) - } - if (nullable(derivativeStep(rOne, c))) { - lemmaRIsNotNullableDerivativeStepIsThenUsedCharContainsC(rOne, c) - } else { - assert(nullable(derivativeStep(rTwo, c))) - lemmaRIsNotNullableDerivativeStepIsThenUsedCharContainsC(rTwo, c) - } - } - case Star(rInner) => () - case Concat(rOne, rTwo) => { - if (nullable(rOne)) { - if (nullable(Concat(derivativeStep(rOne, c), rTwo))) { - lemmaRIsNotNullableDerivativeStepIsThenUsedCharContainsC(rOne, c) - } else { - assert(nullable(derivativeStep(rTwo, c))) - lemmaRIsNotNullableDerivativeStepIsThenUsedCharContainsC(rTwo, c) - - } - } else { - lemmaRIsNotNullableDerivativeStepIsThenUsedCharContainsC(rOne, c) - } - } - } - - } ensuring (usedCharacters(r).contains(c)) - - // DONE - def lemmaDerivativeAfterDerivativeStepIsNullableThenUsedCharsContainsHead[C](r: Regex[C], c: C, tl: List[C]): Unit = { - require(validRegex(r)) - require(nullable(derivative(derivativeStep(r, c), tl))) - decreases(r) - - r match { - case EmptyExpr() => { - assert(derivativeStep(r, c) == EmptyLang[C]()) - lemmaEmptyLangDerivativeIsAFixPoint(derivativeStep(r, c), tl) - check(false) - } - case EmptyLang() => { - assert(derivativeStep(r, c) == EmptyLang[C]()) - lemmaEmptyLangDerivativeIsAFixPoint(derivativeStep(r, c), tl) - check(false) - } - case ElementMatch(a) => { - if (c == a) { - assert(derivativeStep(r, c) == EmptyExpr[C]()) - if (tl.isEmpty) { - assert(usedCharacters(r).contains(c)) - assert(nullable(derivative(derivativeStep(r, c), tl))) - } else { - lemmaEmptyLangDerivativeIsAFixPoint(derivativeStep(derivativeStep(r, c), tl.head), tl.tail) - check(false) - } - } else { - assert(derivativeStep(r, c) == EmptyLang[C]()) - lemmaEmptyLangDerivativeIsAFixPoint(derivativeStep(r, c), tl) - check(false) - } - } - case Union(rOne, rTwo) => { - if (nullable(derivative(derivativeStep(rOne, c), tl))) { - lemmaDerivativeAfterDerivativeStepIsNullableThenUsedCharsContainsHead(rOne, c, tl) - } else if (nullable(derivative(derivativeStep(rTwo, c), tl))) { - lemmaDerivativeAfterDerivativeStepIsNullableThenUsedCharsContainsHead(rTwo, c, tl) - } else { - lemmaMatchRIsSameAsWholeDerivativeAndNil(r, Cons(c, tl)) - lemmaMatchRIsSameAsWholeDerivativeAndNil(rOne, Cons(c, tl)) - lemmaMatchRIsSameAsWholeDerivativeAndNil(rTwo, Cons(c, tl)) - lemmaRegexUnionAcceptsThenOneOfTheTwoAccepts(rOne, rTwo, Cons(c, tl)) - check(false) - } - } - case Star(rInner) => { - assert(derivativeStep(r, c) == Concat(derivativeStep(rInner, c), Star(rInner))) - if (nullable(derivative(derivativeStep(rInner, c), tl))) { - lemmaDerivativeAfterDerivativeStepIsNullableThenUsedCharsContainsHead(rInner, c, tl) - } else { - lemmaMatchRIsSameAsWholeDerivativeAndNil(derivativeStep(r, c), tl) - assert(matchR(derivativeStep(r, c), tl)) - lemmaConcatAcceptsStringThenFindSeparationIsDefined(derivativeStep(rInner, c), Star(rInner), tl) - val (s1, s2) = findConcatSeparation(derivativeStep(rInner, c), Star(rInner), Nil(), tl, tl).get - assert(s1 ++ s2 == tl) - assert(matchR(Star(rInner), s2)) - - assert(matchR(rInner, Cons(c, s1))) - assert(matchR(derivativeStep(rInner, c), s1)) - lemmaMatchRIsSameAsWholeDerivativeAndNil(derivativeStep(rInner, c), s1) - lemmaDerivativeAfterDerivativeStepIsNullableThenUsedCharsContainsHead(rInner, c, s1) - } - } - case Concat(rOne, rTwo) => { - // if (nullable(rOne)) Union(Concat(derivativeStep(rOne, a), rTwo), derivativeStep(rTwo, a)) - // else Union(Concat(derivativeStep(rOne, a), rTwo), EmptyLang()) - - if (nullable(rOne)) { - lemmaMatchRIsSameAsWholeDerivativeAndNil(Union(Concat(derivativeStep(rOne, c), rTwo), derivativeStep(rTwo, c)), tl) - lemmaRegexUnionAcceptsThenOneOfTheTwoAccepts(Concat(derivativeStep(rOne, c), rTwo), derivativeStep(rTwo, c), tl) - if (matchR(Concat(derivativeStep(rOne, c), rTwo), tl)) { - - lemmaConcatAcceptsStringThenFindSeparationIsDefined(derivativeStep(rOne, c), rTwo, tl) - val (s1, s2) = findConcatSeparation(derivativeStep(rOne, c), rTwo, Nil(), tl, tl).get - assert(s1 ++ s2 == tl) - assert(matchR(derivativeStep(rOne, c), s1)) - assert(matchR(rTwo, s2)) - assert(matchR(rOne, Cons(c, s1))) - lemmaMatchRIsSameAsWholeDerivativeAndNil(derivativeStep(rOne, c), s1) - lemmaDerivativeAfterDerivativeStepIsNullableThenUsedCharsContainsHead(rOne, c, s1) - } else { - lemmaMatchRIsSameAsWholeDerivativeAndNil(derivativeStep(rTwo, c), tl) - lemmaDerivativeAfterDerivativeStepIsNullableThenUsedCharsContainsHead(rTwo, c, tl) - } - } else { - lemmaMatchRIsSameAsWholeDerivativeAndNil(Union(Concat(derivativeStep(rOne, c), rTwo), EmptyLang()), tl) - lemmaRegexUnionAcceptsThenOneOfTheTwoAccepts(Concat(derivativeStep(rOne, c), rTwo), EmptyLang(), tl) - lemmaEmptyLangDerivativeIsAFixPoint(EmptyLang(), tl) - assert(matchR(Concat(derivativeStep(rOne, c), rTwo), tl)) - lemmaConcatAcceptsStringThenFindSeparationIsDefined(derivativeStep(rOne, c), rTwo, tl) - val (s1, s2) = findConcatSeparation(derivativeStep(rOne, c), rTwo, Nil(), tl, tl).get - assert(s1 ++ s2 == tl) - assert(matchR(derivativeStep(rOne, c), s1)) - assert(matchR(rTwo, s2)) - assert(matchR(rOne, Cons(c, s1))) - lemmaMatchRIsSameAsWholeDerivativeAndNil(derivativeStep(rOne, c), s1) - lemmaDerivativeAfterDerivativeStepIsNullableThenUsedCharsContainsHead(rOne, c, s1) - - } - } - } - - } ensuring (usedCharacters(r).contains(c)) - - def lemmaDerivativeStepDoesNotAddCharToUsedCharacters[C](r: Regex[C], c: C, cNot: C): Unit = { - decreases(r) - require(validRegex(r)) - require(!usedCharacters(r).contains(cNot)) - - r match { - case EmptyExpr() => () - case EmptyLang() => () - case ElementMatch(c) => () - case Union(rOne, rTwo) => { - lemmaDerivativeStepDoesNotAddCharToUsedCharacters(rOne, c, cNot) - lemmaDerivativeStepDoesNotAddCharToUsedCharacters(rTwo, c, cNot) - lemmaConcatTwoListsWhichNotContainThenTotNotContain(usedCharacters(derivativeStep(rOne, c)), usedCharacters(derivativeStep(rTwo, c)), cNot) - } - case Star(rInner) => { - lemmaDerivativeStepDoesNotAddCharToUsedCharacters(rInner, c, cNot) - } - case Concat(rOne, rTwo) => { - if (nullable(rOne)) { - lemmaDerivativeStepDoesNotAddCharToUsedCharacters(rOne, c, cNot) - lemmaDerivativeStepDoesNotAddCharToUsedCharacters(rTwo, c, cNot) - lemmaConcatTwoListsWhichNotContainThenTotNotContain(usedCharacters(derivativeStep(rOne, c)), usedCharacters(derivativeStep(rTwo, c)), cNot) - } else { - lemmaDerivativeStepDoesNotAddCharToUsedCharacters(rOne, c, cNot) - } - } - } - - } ensuring (!usedCharacters(derivativeStep(r, c)).contains(cNot)) - - def lemmaEmptyLangDerivativeIsAFixPoint[C](r: Regex[C], s: List[C]): Unit = { - require(r == EmptyLang[C]()) - s match { - case Cons(hd, tl) => lemmaEmptyLangDerivativeIsAFixPoint(derivativeStep(r, hd), tl) - case Nil() => () - } - - } ensuring (derivative(r, s) == r) - - def lemmaUsedCharsContainsAllFirstChars[C](r: Regex[C], c: C): Unit = { - require(validRegex(r)) - require(firstChars(r).contains(c)) - decreases(r) - r match { - case EmptyExpr() => () - case EmptyLang() => () - case ElementMatch(c) => () - case Star(r) => lemmaUsedCharsContainsAllFirstChars(r, c) - case Union(rOne, rTwo) => - if (firstChars(rOne).contains(c)) { - lemmaUsedCharsContainsAllFirstChars(rOne, c) - } else { - lemmaUsedCharsContainsAllFirstChars(rTwo, c) - } - - case Concat(rOne, rTwo) if nullable(rOne) => - if (firstChars(rOne).contains(c)) { - lemmaUsedCharsContainsAllFirstChars(rOne, c) - } else { - lemmaUsedCharsContainsAllFirstChars(rTwo, c) - } - - case Concat(rOne, rTwo) if !nullable(rOne) => lemmaUsedCharsContainsAllFirstChars(rOne, c) - } - - } ensuring (usedCharacters(r).contains(c)) - - def lemmaDerivAfterDerivStepIsNullableThenFirstCharsContainsHead[C](r: Regex[C], c: C, tl: List[C]): Unit = { - require(validRegex(r)) - require(nullable(derivative(derivativeStep(r, c), tl))) - - r match { - case EmptyExpr() => { - assert(derivativeStep(r, c) == EmptyLang[C]()) - lemmaEmptyLangDerivativeIsAFixPoint(derivativeStep(r, c), tl) - check(false) - } - case EmptyLang() => { - assert(derivativeStep(r, c) == EmptyLang[C]()) - lemmaEmptyLangDerivativeIsAFixPoint(derivativeStep(r, c), tl) - check(false) - } - case ElementMatch(a) => { - if (c == a) { - assert(derivativeStep(r, c) == EmptyExpr[C]()) - if (tl.isEmpty) { - assert(firstChars(r).contains(c)) - assert(nullable(derivative(derivativeStep(r, c), tl))) - } else { - lemmaEmptyLangDerivativeIsAFixPoint(derivativeStep(derivativeStep(r, c), tl.head), tl.tail) - check(false) - } - } else { - assert(derivativeStep(r, c) == EmptyLang[C]()) - lemmaEmptyLangDerivativeIsAFixPoint(derivativeStep(r, c), tl) - check(false) - } - } - case Union(rOne, rTwo) => { - if (nullable(derivative(derivativeStep(rOne, c), tl))) { - lemmaDerivAfterDerivStepIsNullableThenFirstCharsContainsHead(rOne, c, tl) - } else if (nullable(derivative(derivativeStep(rTwo, c), tl))) { - lemmaDerivAfterDerivStepIsNullableThenFirstCharsContainsHead(rTwo, c, tl) - } else { - lemmaMatchRIsSameAsWholeDerivativeAndNil(r, Cons(c, tl)) - lemmaMatchRIsSameAsWholeDerivativeAndNil(rOne, Cons(c, tl)) - lemmaMatchRIsSameAsWholeDerivativeAndNil(rTwo, Cons(c, tl)) - lemmaRegexUnionAcceptsThenOneOfTheTwoAccepts(rOne, rTwo, Cons(c, tl)) - check(false) - } - } - case Star(rInner) => { - assert(derivativeStep(r, c) == Concat(derivativeStep(rInner, c), Star(rInner))) - if (nullable(derivative(derivativeStep(rInner, c), tl))) { - lemmaDerivAfterDerivStepIsNullableThenFirstCharsContainsHead(rInner, c, tl) - } else { - lemmaMatchRIsSameAsWholeDerivativeAndNil(derivativeStep(r, c), tl) - assert(matchR(derivativeStep(r, c), tl)) - lemmaConcatAcceptsStringThenFindSeparationIsDefined(derivativeStep(rInner, c), Star(rInner), tl) - val (s1, s2) = findConcatSeparation(derivativeStep(rInner, c), Star(rInner), Nil(), tl, tl).get - assert(s1 ++ s2 == tl) - assert(matchR(Star(rInner), s2)) - - assert(matchR(rInner, Cons(c, s1))) - assert(matchR(derivativeStep(rInner, c), s1)) - lemmaMatchRIsSameAsWholeDerivativeAndNil(derivativeStep(rInner, c), s1) - lemmaDerivAfterDerivStepIsNullableThenFirstCharsContainsHead(rInner, c, s1) - } - } - case Concat(rOne, rTwo) => { - if (nullable(rOne)) { - lemmaMatchRIsSameAsWholeDerivativeAndNil(Union(Concat(derivativeStep(rOne, c), rTwo), derivativeStep(rTwo, c)), tl) - lemmaRegexUnionAcceptsThenOneOfTheTwoAccepts(Concat(derivativeStep(rOne, c), rTwo), derivativeStep(rTwo, c), tl) - if (matchR(Concat(derivativeStep(rOne, c), rTwo), tl)) { - - lemmaConcatAcceptsStringThenFindSeparationIsDefined(derivativeStep(rOne, c), rTwo, tl) - val (s1, s2) = findConcatSeparation(derivativeStep(rOne, c), rTwo, Nil(), tl, tl).get - assert(s1 ++ s2 == tl) - assert(matchR(derivativeStep(rOne, c), s1)) - assert(matchR(rTwo, s2)) - assert(matchR(rOne, Cons(c, s1))) - lemmaMatchRIsSameAsWholeDerivativeAndNil(derivativeStep(rOne, c), s1) - lemmaDerivAfterDerivStepIsNullableThenFirstCharsContainsHead(rOne, c, s1) - } else { - lemmaMatchRIsSameAsWholeDerivativeAndNil(derivativeStep(rTwo, c), tl) - lemmaDerivAfterDerivStepIsNullableThenFirstCharsContainsHead(rTwo, c, tl) - } - } else { - lemmaMatchRIsSameAsWholeDerivativeAndNil(Union(Concat(derivativeStep(rOne, c), rTwo), EmptyLang()), tl) - lemmaRegexUnionAcceptsThenOneOfTheTwoAccepts(Concat(derivativeStep(rOne, c), rTwo), EmptyLang(), tl) - lemmaEmptyLangDerivativeIsAFixPoint(EmptyLang(), tl) - assert(matchR(Concat(derivativeStep(rOne, c), rTwo), tl)) - lemmaConcatAcceptsStringThenFindSeparationIsDefined(derivativeStep(rOne, c), rTwo, tl) - val (s1, s2) = findConcatSeparation(derivativeStep(rOne, c), rTwo, Nil(), tl, tl).get - assert(s1 ++ s2 == tl) - assert(matchR(derivativeStep(rOne, c), s1)) - assert(matchR(rTwo, s2)) - assert(matchR(rOne, Cons(c, s1))) - lemmaMatchRIsSameAsWholeDerivativeAndNil(derivativeStep(rOne, c), s1) - lemmaDerivAfterDerivStepIsNullableThenFirstCharsContainsHead(rOne, c, s1) - - } - } - } - - } ensuring (firstChars(r).contains(c)) -} - -object Utils { - def maxBigInt(a: BigInt, b: BigInt): BigInt = if (a >= b) a else b -} From 8bd26d1c60ea78d4b54ae6abbf67601df0ec698a Mon Sep 17 00:00:00 2001 From: Samuel Chassot Date: Mon, 5 Feb 2024 13:41:29 +0100 Subject: [PATCH 02/78] move regex to an sbt project --- lexers/regex/verifiedlexer/.gitignore | 37 + lexers/regex/verifiedlexer/.scalafmt.conf | 3 + lexers/regex/verifiedlexer/README.md | 28 + lexers/regex/verifiedlexer/build.sbt | 9 + .../verifiedlexer/project/build.properties | 1 + .../regex/verifiedlexer/project/plugins.sbt | 1 + .../verifiedlexer/src/main/scala/Main.scala | 5 + .../main/scala/ch/epfl/lexer/ListUtils.scala | 556 ++++++ .../scala/ch/epfl/lexer/VerifiedLexer.scala | 1505 +++++++++++++++++ .../scala/ch/epfl/lexer/VerifiedRegex.scala | 1165 +++++++++++++ lexers/regex/verifiedlexer/stainless.conf | 15 + lexers/regex/verifiedlexer/verify.sh | 1 + 12 files changed, 3326 insertions(+) create mode 100644 lexers/regex/verifiedlexer/.gitignore create mode 100644 lexers/regex/verifiedlexer/.scalafmt.conf create mode 100644 lexers/regex/verifiedlexer/README.md create mode 100644 lexers/regex/verifiedlexer/build.sbt create mode 100644 lexers/regex/verifiedlexer/project/build.properties create mode 100644 lexers/regex/verifiedlexer/project/plugins.sbt create mode 100644 lexers/regex/verifiedlexer/src/main/scala/Main.scala create mode 100644 lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/ListUtils.scala create mode 100644 lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedLexer.scala create mode 100644 lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedRegex.scala create mode 100644 lexers/regex/verifiedlexer/stainless.conf create mode 100755 lexers/regex/verifiedlexer/verify.sh diff --git a/lexers/regex/verifiedlexer/.gitignore b/lexers/regex/verifiedlexer/.gitignore new file mode 100644 index 00000000..ff35967c --- /dev/null +++ b/lexers/regex/verifiedlexer/.gitignore @@ -0,0 +1,37 @@ +# macOS +.DS_Store + +# sbt specific +dist/* +target/ +lib_managed/ +src_managed/ +project/boot/ +project/plugins/project/ +project/local-plugins.sbt +.history +.ensime +.ensime_cache/ +.sbt-scripted/ +local.sbt + +# Bloop +.bsp + +# VS Code +.vscode/ + +# Metals +.bloop/ +.metals/ +metals.sbt + +# IDEA +.idea +.idea_modules +/.worksheet/ + + +# Stainless plugin +stainless/ +project/lib \ No newline at end of file diff --git a/lexers/regex/verifiedlexer/.scalafmt.conf b/lexers/regex/verifiedlexer/.scalafmt.conf new file mode 100644 index 00000000..03930b18 --- /dev/null +++ b/lexers/regex/verifiedlexer/.scalafmt.conf @@ -0,0 +1,3 @@ +version = "3.7.15" +runner.dialect = scala3 +maxColumn = 210 \ No newline at end of file diff --git a/lexers/regex/verifiedlexer/README.md b/lexers/regex/verifiedlexer/README.md new file mode 100644 index 00000000..6eb2cba0 --- /dev/null +++ b/lexers/regex/verifiedlexer/README.md @@ -0,0 +1,28 @@ +# Verified Lexer based on Regex derivatives + +## Setup + +To run this project, you need to install the Stainless sbt plugin. To do so, follow these steps: + +1. Download the following archive: [Stainless SBT plugin download]("https://github.com/epfl-lara/stainless/releases/download/v0.9.8.2/sbt-stainless.zip") +2. Unzip the archive, it should contain a `project` folder and a `stainless` folder. +3. copy the `project/lib` folder into the `project` of this sbt project. +4. copy the `stainless` folder into the root of this sbt project. + +You should obtain the following folder structure: + +``` +mutablemap +├── build.sbt +├── project +│ ├── build.properties +│ └── lib <-------- +│ └── sbt-stainless.jar <-------- +├── sbt-stainless.zip +├── src/ +└── stainless/ <-------- +``` + +Now the project is ready to run, both the main class and the benchmarks. + +If it does not work, please refer to [this manual]("https://epfl-lara.github.io/stainless/installation.html#usage-within-an-existing-project"). diff --git a/lexers/regex/verifiedlexer/build.sbt b/lexers/regex/verifiedlexer/build.sbt new file mode 100644 index 00000000..8c78250f --- /dev/null +++ b/lexers/regex/verifiedlexer/build.sbt @@ -0,0 +1,9 @@ +name := "VerifiedLexer" +version := "0.1.0-SNAPSHOT" +scalaVersion :="3.3.0" + +run / fork := true + +stainlessEnabled := false + +enablePlugins(StainlessPlugin, JmhPlugin) \ No newline at end of file diff --git a/lexers/regex/verifiedlexer/project/build.properties b/lexers/regex/verifiedlexer/project/build.properties new file mode 100644 index 00000000..abbbce5d --- /dev/null +++ b/lexers/regex/verifiedlexer/project/build.properties @@ -0,0 +1 @@ +sbt.version=1.9.8 diff --git a/lexers/regex/verifiedlexer/project/plugins.sbt b/lexers/regex/verifiedlexer/project/plugins.sbt new file mode 100644 index 00000000..42535658 --- /dev/null +++ b/lexers/regex/verifiedlexer/project/plugins.sbt @@ -0,0 +1 @@ +addSbtPlugin("pl.project13.scala" % "sbt-jmh" % "0.4.3") \ No newline at end of file diff --git a/lexers/regex/verifiedlexer/src/main/scala/Main.scala b/lexers/regex/verifiedlexer/src/main/scala/Main.scala new file mode 100644 index 00000000..bd01e94b --- /dev/null +++ b/lexers/regex/verifiedlexer/src/main/scala/Main.scala @@ -0,0 +1,5 @@ +@main def hello(): Unit = + println("Hello world!") + println(msg) + +def msg = "I was compiled by Scala 3. :)" diff --git a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/ListUtils.scala b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/ListUtils.scala new file mode 100644 index 00000000..1d5d816f --- /dev/null +++ b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/ListUtils.scala @@ -0,0 +1,556 @@ +/** Author: Samuel Chassot + */ + +import stainless.annotation._ +import stainless.collection._ +import stainless.equations._ +import stainless.lang._ +import stainless.proof.check +import scala.annotation.tailrec +import stainless.lang.StaticChecks._ + +object ListUtils { + def isPrefix[B](prefix: List[B], l: List[B]): Boolean = { + (prefix, l) match { + case (Nil(), _) => true + case (_, Nil()) => false + case (l1, l2) => if (l1.head == l2.head) isPrefix(l1.tail, l2.tail) else false + } + } ensuring (res => if (res) l.size >= prefix.size else true) + + def removeLast[B](l: List[B]): List[B] = { + require(!l.isEmpty) + val res: List[B] = l match { + case Cons(hd, Nil()) => Nil() + case Cons(hd, tl) => Cons(hd, removeLast(tl)) + } + res + } ensuring (res => res ++ List(l.last) == l) + + def reverseList[B](l: List[B]): List[B] = { + decreases(l) + l match { + case Cons(hd, tl) => reverseList(tl) ++ List(hd) + case Nil() => Nil() + } + } + + def getSuffix[B](l: List[B], p: List[B]): List[B] = { + require(l.size >= p.size) + require(isPrefix(p, l)) + decreases(l) + p match { + case Cons(hd, tl) => getSuffix(l.tail, tl) + case Nil() => l + } + } ensuring (res => p ++ res == l) + + def getIndex[B](l: List[B], e: B): BigInt = { + require(l.contains(e)) + decreases(l) + l match { + case Cons(hd, tl) if hd == e => BigInt(0) + case Cons(hd, tl) if hd != e => 1 + getIndex(tl, e) + case Nil() => BigInt(-1) + } + } ensuring (res => res >= 0) + + def consecutiveSubseq[B](l1: List[B], lTot: List[B]): Boolean = { + decreases(lTot) + lTot match { + case Cons(hd, tl) => consecutiveSubseqAtHead(l1, lTot) || consecutiveSubseq(l1, tl) + case Nil() => consecutiveSubseqAtHead(l1, lTot) + } + } + + def consecutiveSubseqAtHead[B](l1: List[B], lTot: List[B]): Boolean = { + decreases(l1) + (l1, lTot) match { + case (Nil(), _) => true + case (Cons(hd1, tl1), Cons(hdTot, tlTot)) if hd1 == hdTot => consecutiveSubseqAtHead(tl1, tlTot) + case _ => false + } + } + + @inlineOnce + @opaque + def lemmaConsecutiveSubseqThenSubseq[B](l1: List[B], l2: List[B]): Unit = { + require(consecutiveSubseq(l1, l2)) + decreases(l2) + (l1, l2) match { + case (Cons(hd1, tl1), Cons(hd2, tl2)) if consecutiveSubseqAtHead(l1, l2) => lemmaConsecutiveSubseqThenSubseq(tl1, tl2) + case (Cons(hd1, tl1), Cons(hd2, tl2)) => lemmaConsecutiveSubseqThenSubseq(l1, tl2) + case _ => () + } + + } ensuring (_ => ListSpecs.subseq(l1, l2)) + + @inlineOnce + @opaque + def lemmaContainsAndNotHdThenTlContains[B](l: List[B], e: B): Unit = { + require(l.contains(e)) + require(l.head != e) + + } ensuring (_ => l.tail.contains(e)) + + @inlineOnce + @opaque + def lemmaGetIndexBiggerAndHeadNotEqThenTailContains[B](l: List[B], e1: B, e2: B): Unit = { + require(l.contains(e1) && l.contains(e2)) + require(e1 != e2) + require(getIndex(l, e1) < getIndex(l, e2)) + decreases(l.size) + + l match { + case Cons(hd, tl) if hd == e1 => lemmaGetIndexBiggerAndHeadEqThenTailContains(l, e1, e2) + case Cons(hd, tl) if hd != e1 => { + assert(hd != e1) + + lemmaContainsAndNotHdThenTlContains(l, e1) + lemmaNotHeadSoGetIndexTailIsMinusOne(l, e1) + lemmaNotHeadSoGetIndexTailIsMinusOne(l, e2) + + lemmaGetIndexBiggerAndHeadNotEqThenTailContains(tl, e1, e2) + } + case Nil() => check(false) + } + assert(l.tail.contains(e2)) + + } ensuring (_ => l.tail.contains(e2)) + + @inlineOnce + @opaque + def lemmaSameIndexThenSameElement[B](l: List[B], e1: B, e2: B): Unit = { + require(l.contains(e1)) + require(l.contains(e2)) + require(getIndex(l, e1) == getIndex(l, e2)) + decreases(l) + + if (getIndex(l, e1) == 0) { + assert(l.head == e1) + assert(l.head == e2) + assert(e1 == e2) + } else { + lemmaSameIndexThenSameElement(l.tail, e1, e2) + } + } ensuring (_ => e1 == e2) + + @inlineOnce + @opaque + def lemmaGetIndexBiggerAndHeadEqThenTailContains[B](l: List[B], e1: B, e2: B): Unit = { + require(l.contains(e1) && l.contains(e2)) + require(e1 != e2) + require(l.head == e1) + require(getIndex(l, e1) < getIndex(l, e2)) + + } ensuring (_ => l.tail.contains(e2)) + + @inlineOnce + @opaque + def lemmaNotHeadSoGetIndexTailIsMinusOne[B](l: List[B], e: B): Unit = { + decreases(l) + require(l.contains(e)) + require(l.head != e) + + if (l.tail.head != e) { + lemmaNotHeadSoGetIndexTailIsMinusOne(l.tail, e) + } + } ensuring (_ => getIndex(l, e) == getIndex(l.tail, e) + 1) + + @inlineOnce + @opaque + def lemmaIsPrefixRefl[B](l1: List[B], l2: List[B]): Unit = { + decreases(l1) + require(l1 == l2) + l1 match { + case Cons(hd, tl) => lemmaIsPrefixRefl(tl, l2.tail) + case Nil() => () + } + } ensuring (_ => isPrefix(l1, l2)) + + @inlineOnce + @opaque + def lemmaConcatTwoListThenFirstIsPrefix[B](l1: List[B], l2: List[B]): Unit = { + l1 match { + case Cons(hd, tl) => lemmaConcatTwoListThenFirstIsPrefix(tl, l2) + case Nil() => () + } + } ensuring (_ => isPrefix(l1, l1 ++ l2)) + + @inlineOnce + @opaque + def lemmaLongerPrefixContainsHeadOfSuffixOfSmallerPref[B](p1: List[B], s1: List[B], p2: List[B], l: List[B]): Unit = { + require(isPrefix(p2, l)) + require(p1 ++ s1 == l) + require(!s1.isEmpty) + require(p1.size < p2.size) + decreases(p1) + + lemmaConcatTwoListThenFirstIsPrefix(p1, s1) + + p1 match { + case Cons(hd, tl) => lemmaLongerPrefixContainsHeadOfSuffixOfSmallerPref(tl, s1, p2.tail, l.tail) + case Nil() => () + } + } ensuring (_ => p2.contains(s1.head)) + + @inlineOnce + @opaque + def lemmaConcatAssociativity[B](l1: List[B], elmt: B, l2: List[B], tot: List[B]): Unit = { + require((l1 ++ List(elmt)) ++ l2 == tot) + decreases(l1) + assert(l1 ++ List(elmt) ++ l2 == tot) + l1 match { + case Cons(hd, tl) => lemmaConcatAssociativity(tl, elmt, l2, tot.tail) + case Nil() => () + } + } ensuring (_ => l1 ++ (List(elmt) ++ l2) == tot) + + @inlineOnce + @opaque + def lemmaTwoListsConcatAssociativity[B]( + l1: List[B], + l2: List[B], + l3: List[B] + ): Unit = { + decreases(l1) + l1 match { + case Cons(hd, tl) => { + lemmaTwoListsConcatAssociativity(tl, l2, l3) + } + case Nil() => () + } + + } ensuring (_ => (l1 ++ l2) ++ l3 == l1 ++ (l2 ++ l3)) + + @inlineOnce + @opaque + def lemmaRemoveLastConcatenatedPrefixStillPrefix[B](l: List[B], elmt: B, tot: List[B]): Unit = { + require(isPrefix(l ++ List(elmt), tot)) + decreases(l) + l match { + case Cons(hd, tl) => lemmaRemoveLastConcatenatedPrefixStillPrefix(tl, elmt, tot.tail) + case Nil() => () + } + } ensuring (_ => isPrefix(l, tot)) + + @inlineOnce + @opaque + def lemmaRemoveLastPrefixStillPrefix[B](p: List[B], l: List[B]): Unit = { + require(!l.isEmpty) + require(isPrefix(p, l)) + require(p.size < l.size) + decreases(p) + p match { + case Cons(hd, tl) => lemmaRemoveLastPrefixStillPrefix(tl, l.tail) + case Nil() => () + } + + } ensuring (_ => isPrefix(p, removeLast(l))) + + @inlineOnce + @opaque + def lemmaPrefixStaysPrefixWhenAddingToSuffix[B](p: List[B], l: List[B], suffix: List[B]): Unit = { + decreases(p) + require(isPrefix(p, l)) + p match { + case Cons(hd, tl) => lemmaPrefixStaysPrefixWhenAddingToSuffix(tl, l.tail, suffix) + case Nil() => () + } + } ensuring (_ => isPrefix(p, l ++ suffix)) + + @inlineOnce + @opaque + def lemmaRemoveLastPrefixDecreasesSize[B](l: List[B]): Unit = { + require(l.size > 0) + } ensuring (_ => removeLast(l).size < l.size) + + @inlineOnce + @opaque + def lemmaIsPrefixSameLengthThenSameList[B](p1: List[B], p2: List[B], l: List[B]): Unit = { + require(isPrefix(p1, l)) + require(isPrefix(p2, l)) + require(p1.size == p2.size) + decreases(p1) + p1 match { + case Cons(hd, tl) => lemmaIsPrefixSameLengthThenSameList(tl, p2.tail, l.tail) + case Nil() => () + } + + } ensuring (_ => p1 == p2) + + @inlineOnce + @opaque + def lemmaRemoveLastFromBothSidePreservesEq[B](p: List[B], s: List[B], l: List[B]): Unit = { + require(p ++ s == l) + require(!s.isEmpty) + decreases(p) + p match { + case Cons(hd, tl) => lemmaRemoveLastFromBothSidePreservesEq(tl, s, l.tail) + case Nil() => () + } + } ensuring (_ => p ++ removeLast(s) == removeLast(l)) + + @inlineOnce + @opaque + def lemmaRemoveLastFromLMakesItPrefix[B](l: List[B]): Unit = { + require(!l.isEmpty) + decreases(l.size) + l match { + case Cons(hd, Nil()) => () + case Cons(hd, tl) => lemmaRemoveLastFromLMakesItPrefix(tl) + } + + } ensuring (_ => isPrefix(removeLast(l), l)) + + @inlineOnce + @opaque + def lemmaSamePrefixThenSameSuffix[B](p1: List[B], s1: List[B], p2: List[B], s2: List[B], l: List[B]): Unit = { + require(isPrefix(p1, l)) + require(isPrefix(p2, l)) + require(p1 ++ s1 == l) + require(p2 ++ s2 == l) + require(p1 == p2) + decreases(p1) + p1 match { + case Cons(hd, tl) => lemmaSamePrefixThenSameSuffix(tl, s1, p2.tail, s2, l.tail) + case Nil() => () + } + } ensuring (_ => s1 == s2) + + @inlineOnce + @opaque + def lemmaIsPrefixThenSmallerEqSize[B](p: List[B], l: List[B]): Unit = { + require(isPrefix(p, l)) + decreases(p) + (p, l) match { + case (Nil(), _) => () + case (_, Nil()) => () + case (l1, l2) => lemmaIsPrefixThenSmallerEqSize(l1.tail, l2.tail) + } + } ensuring (_ => p.size <= l.size) + + @inlineOnce + @opaque + def lemmaAddHeadSuffixToPrefixStillPrefix[B](p: List[B], l: List[B]): Unit = { + require(isPrefix(p, l)) + require(p.size < l.size) + decreases(p) + p match { + case Cons(hd, tl) => lemmaAddHeadSuffixToPrefixStillPrefix(tl, l.tail) + case Nil() => () + } + } ensuring (_ => isPrefix(p ++ List(getSuffix(l, p).head), l)) + + @inlineOnce + @opaque + def lemmaGetSuffixOnListWithItSelfIsEmpty[B](l: List[B]): Unit = { + decreases(l.size) + lemmaIsPrefixRefl(l, l) + l match { + case Cons(hd, tl) => lemmaGetSuffixOnListWithItSelfIsEmpty(tl) + case Nil() => () + } + } ensuring (_ => getSuffix(l, l).isEmpty) + + @inlineOnce + @opaque + def lemmaMoveElementToOtherListKeepsConcatEq[B](s1: List[B], hd2: B, tl2: List[B], tot: List[B]): Unit = { + require(s1 ++ Cons(hd2, tl2) == tot) + decreases(s1) + s1 match { + case Cons(hd1, tl1) => lemmaMoveElementToOtherListKeepsConcatEq(tl1, hd2, tl2, tot.tail) + case Nil() => () + } + + } ensuring (_ => (s1 ++ List(hd2)) ++ tl2 == tot) + + @inlineOnce + @opaque + def lemmaPrefixFromSameListAndStrictlySmallerThenPrefixFromEachOther[B](s1: List[B], s2: List[B], l: List[B]): Unit = { + require(isPrefix(s1, l)) + require(isPrefix(s2, l)) + require(s2.size <= s1.size) + decreases(s2) + + s2 match { + case Cons(hd, tl) => lemmaPrefixFromSameListAndStrictlySmallerThenPrefixFromEachOther(s1.tail, tl, l.tail) + case Nil() => + } + } ensuring (_ => isPrefix(s2, s1)) + + @inlineOnce + @opaque + def concatWithoutDuplicates[B](baseList: List[B], newList: List[B]): List[B] = { + require(ListOps.noDuplicate(baseList)) + decreases(newList) + newList match { + case Cons(hd, tl) if baseList.contains(hd) => concatWithoutDuplicates(baseList, tl) + case Cons(hd, tl) if !baseList.contains(hd) => concatWithoutDuplicates(Cons(hd, baseList), tl) + case Nil() => baseList + } + } ensuring (res => ListOps.noDuplicate(res) && (baseList ++ newList).content == res.content) + + @inlineOnce + @opaque + def removeDuplicates[B](list: List[B], acc: List[B] = Nil[B]()): List[B] = { + require(ListOps.noDuplicate(acc)) + decreases(list.size) + list match { + case Cons(hd, tl) if acc.contains(hd) => removeDuplicates(tl, acc) + case Cons(hd, tl) => removeDuplicates(tl, Cons(hd, acc)) + case Nil() => acc + } + } ensuring (res => ListOps.noDuplicate(res) && res.content == (list ++ acc).content) + + @inlineOnce + @opaque + def lemmaSubseqRefl[B](l: List[B]): Unit = { + decreases(l.size) + l match { + case Nil() => () + case Cons(hd, tl) => lemmaSubseqRefl(tl) + } + } ensuring (_ => ListSpecs.subseq(l, l)) + + @inlineOnce + @opaque + def lemmaTailIsSubseqOfList[B](elmt: B, l: List[B]): Unit = { + l match { + case Nil() => () + case Cons(hd, tl) if hd == elmt => { + lemmaSubseqRefl(l) + ListSpecs.subseqTail(l, l) + assert(ListSpecs.subseq(tl, l)) + } + case Cons(hd, tl) if hd != elmt => lemmaSubseqRefl(l) + } + } ensuring (_ => ListSpecs.subseq(l, Cons(elmt, l))) + + @inlineOnce + @opaque + def lemmaSubSeqTransitive[B](l1: List[B], l2: List[B], l3: List[B]): Unit = { + require(ListSpecs.subseq(l1, l2)) + require(ListSpecs.subseq(l2, l3)) + decreases(l1.size, l2.size, l3.size) + + (l1, l2, l3) match { + case (Cons(hd1, tl1), Cons(hd2, tl2), Cons(hd3, tl3)) if hd2 != hd3 => { + lemmaSubSeqTransitive(l1, l2, tl3) + } + case (Cons(hd1, tl1), Cons(hd2, tl2), Cons(hd3, tl3)) if hd2 == hd3 => { + if (ListSpecs.subseq(tl2, tl3)) { + if (hd1 == hd2) { + if (ListSpecs.subseq(tl1, tl2)) { + lemmaSubSeqTransitive(tl1, tl2, tl3) + } else { + lemmaSubSeqTransitive(l1, tl2, tl3) + } + } else { + lemmaSubSeqTransitive(l1, tl2, tl3) + } + } else { + if (hd1 == hd2) { + if (ListSpecs.subseq(tl1, l2)) { + lemmaSubSeqTransitive(tl1, l2, tl3) + } else { + lemmaSubSeqTransitive(l1, l2, tl3) + } + } else { + lemmaSubSeqTransitive(l1, l2, tl3) + } + } + + } + case _ => () + } + + } ensuring (_ => ListSpecs.subseq(l1, l3)) + + @inlineOnce + @opaque + def lemmaConcatThenFirstSubseqOfTot[B](l1: List[B], l2: List[B]): Unit = { + decreases(l1) + l1 match { + case Cons(hd, tl) => lemmaConcatThenFirstSubseqOfTot(tl, l2) + case Nil() => () + } + } ensuring (_ => ListSpecs.subseq(l1, l1 ++ l2)) + + @inlineOnce + @opaque + def lemmaConcatThenSecondSubseqOfTot[B](l1: List[B], l2: List[B]): Unit = { + decreases(l1) + l1 match { + case Cons(hd, tl) => lemmaConcatThenSecondSubseqOfTot(tl, l2) + case Nil() => lemmaSubseqRefl(l2) + } + } ensuring (_ => ListSpecs.subseq(l2, l1 ++ l2)) + + @inlineOnce + @opaque + def lemmaConcatTwoListsWhichNotContainThenTotNotContain[B](l1: List[B], l2: List[B], b: B): Unit = { + require(!l1.contains(b)) + require(!l2.contains(b)) + + l1 match { + case Cons(hd, tl) if hd == b => check(false) + case Cons(hd, tl) => lemmaConcatTwoListsWhichNotContainThenTotNotContain(tl, l2, b) + case Nil() => () + } + } ensuring (_ => !(l1 ++ l2).contains(b)) + + @inlineOnce + @opaque + def lemmaForallContainsThenForEqualLists[B](l1: List[B], l2: List[B], l1Bis: List[B], l2Bis: List[B]): Unit = { + require(l1.forall(b => l2.contains(b))) + require(l1 == l1Bis) + require(l2 == l2Bis) + + } ensuring (_ => l1Bis.forall(b => l2Bis.contains(b))) + + @inlineOnce + @opaque + def lemmaForallContainsAndNoDuplicateThenSmallerList[B](l: List[B], lIn: List[B]): Unit = { + require(lIn.forall(e => l.contains(e))) + require(ListOps.noDuplicate(lIn)) + decreases(lIn.size) + + lIn match { + case Cons(hd, tl) => { + + ListSpecs.forallContainsSubset(lIn, l) + assert(lIn.content.subsetOf(l.content)) + assert(!tl.contains(hd)) + val newList = l - hd + assert(newList.content == l.content - hd) + ListSpecs.subsetContains(tl, newList) + lemmaForallContainsAndNoDuplicateThenSmallerList(newList, tl) + assert(tl.size <= newList.size) + assert(tl.size + 1 == lIn.size) + assert(l.contains(hd)) + assert(newList.content == l.content -- Set(hd)) + lemmaRemoveElmtContainedSizeSmaller(l, hd) + assert(l.size > newList.size) + } + case Nil() => () + } + } ensuring (_ => lIn.size <= l.size) + + @inlineOnce + @opaque + def lemmaRemoveElmtContainedSizeSmaller[B](l: List[B], e: B): Unit = { + require(l.contains(e)) + decreases(l) + l match { + case Cons(hd, tl) if hd == e => { + assert(l - e == tl - e) + if (tl.contains(e)) { + lemmaRemoveElmtContainedSizeSmaller(tl, e) + } + } + case Cons(hd, tl) => lemmaRemoveElmtContainedSizeSmaller(tl, e) + case Nil() => check(false) + } + } ensuring (_ => (l - e).size < l.size) +} diff --git a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedLexer.scala b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedLexer.scala new file mode 100644 index 00000000..728b1788 --- /dev/null +++ b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedLexer.scala @@ -0,0 +1,1505 @@ +/** Author: Samuel Chassot + */ + +import stainless.annotation._ +import stainless.collection._ +import stainless.equations._ +import stainless.lang._ +import stainless.proof.check +import scala.annotation.tailrec +import stainless.lang.StaticChecks._ + +object Main { + import VerifiedLexer._ + import VerifiedRegex._ + import VerifiedRegexMatcher._ + import stainless.io.StdOut.println + import stainless.io.State + import VerifiedLexer._ + @extern + def main(args: Array[String]): Unit = { + + val regexSep = Concat(ElementMatch(' '), Star(ElementMatch(' '))) + val ruleSep = Rule(regexSep, "sep", true) + val sepToken = Token(List(' '), "sep", true) + + // DFA which recognises any repetition of at least one "ab" string + val regexAb = Concat(Concat(ElementMatch('a'), ElementMatch('b')), Star(Concat(ElementMatch('a'), ElementMatch('b')))) + val ruleAb = Rule(regexAb, "ab", false) + + // DFA which recognises any repetition of at least one "c" string + val regexC = Concat(ElementMatch('c'), Star(ElementMatch('c'))) + val ruleC = Rule(regexC, "c", false) + + val rules = List(ruleAb, ruleC, ruleSep) + + // Tokens -> Characters -> Tokens + val t1 = Token(List('a', 'b'), "ab", false) + val t2 = Token(List('c', 'c'), "c", false) + val input: List[Token[Char]] = List(t1, t2, t1, t2, t1, t1) + + val state = State(BigInt(1)) + + val output: List[Char] = Lexer.printWithSeparatorTokenWhenNeeded(rules, input, sepToken) + + val lexed = Lexer.lex(rules, output) + + println("Token list input:")(state) + println(input.foldLeft("")((s: String, t: Token[Char]) => s + t.toString + "\n"))(state) + + println("Token list printed when separator token when needed: " + output.foldLeft("")((s: String, c: Char) => s + c.toString))(state) + + println("After lexing again:")(state) + println(lexed._1.foldLeft("")((s: String, t: Token[Char]) => s + t.toString + "\n"))(state) + + println("tokens -> print -> tokens modulo separator tokens equality: " + (lexed._1.filter(!_.isSeparator) == input))(state) + println("--------------------------------------------------------------------------------")(state) + + // Characters -> Tokens -> Characters + val inputChar = List('a', 'b', 'c', 'a', 'b', 'a', 'b', 'c', 'c') + val tokenised = Lexer.lex(rules, inputChar) + + println("Input string is: " + inputChar.foldLeft("")((s: String, c: Char) => s + c.toString))(state) + println( + "Resulting list of tokens: " + tokenised._1.foldLeft("")((s: String, t: Token[Char]) => + s + t.characters.foldLeft("")((s: String, c: Char) => s + c.toString) + " " + ) + )( + state + ) + println("Non-tokenised suffix: " + tokenised._2.foldLeft("")((s: String, c: Char) => s + c.toString))(state) + } + +} +object VerifiedLexer { + import VerifiedRegex._ + import VerifiedRegexMatcher._ + + case class Token[C](characters: List[C], tag: String, isSeparator: Boolean) + case class Rule[C](regex: Regex[C], tag: String, isSeparator: Boolean) + + object Lexer { + + @inline + def ruleValid[C](r: Rule[C]): Boolean = { + validRegex(r.regex) && !nullable(r.regex) && r.tag != "" + } + def noDuplicateTag[C](rules: List[Rule[C]], acc: List[String] = Nil()): Boolean = { + decreases(rules) + rules match { + case Nil() => true + case Cons(hd, tl) => !acc.contains(hd.tag) && noDuplicateTag(tl, Cons(hd.tag, acc)) + } + } + def rulesValid[C](rs: List[Rule[C]]): Boolean = { + rs match { + case Cons(hd, tl) => ruleValid(hd) && rulesValid(tl) + case Nil() => true + } + } + + def rulesProduceIndivualToken[C](rs: List[Rule[C]], t: Token[C]): Boolean = { + require(!rs.isEmpty) + require(rulesInvariant(rs)) + val (producedTs, suffix) = lex(rs, print(List(t))) + producedTs.size == 1 && producedTs.head == t && suffix.isEmpty + } + + def rulesProduceEachTokenIndividually[C](rs: List[Rule[C]], ts: List[Token[C]]): Boolean = { + decreases(ts) + require(!rs.isEmpty) + require(rulesInvariant(rs)) + ts match { + case Cons(hd, tl) => rulesProduceIndivualToken(rs, hd) && rulesProduceEachTokenIndividually(rs, tl) + case Nil() => true + } + } + + def sepAndNonSepRulesDisjointChars[C](rules: List[Rule[C]], rulesRec: List[Rule[C]]): Boolean = { + rulesRec match { + case Cons(hd, tl) => ruleDisjointCharsFromAllFromOtherType(hd, rules) && sepAndNonSepRulesDisjointChars(rules, tl) + case Nil() => true + } + } + + def ruleDisjointCharsFromAllFromOtherType[C](r: Rule[C], rules: List[Rule[C]]): Boolean = { + decreases(rules) + rules match { + case Cons(hd, tl) if hd.isSeparator != r.isSeparator => rulesUseDisjointChars(r, hd) && ruleDisjointCharsFromAllFromOtherType(r, tl) + case Cons(hd, tl) => ruleDisjointCharsFromAllFromOtherType(r, tl) + case Nil() => true + } + + } + def rulesUseDisjointChars[C](r1: Rule[C], r2: Rule[C]): Boolean = { + usedCharacters(r2.regex).forall(c => !usedCharacters(r1.regex).contains(c)) && + usedCharacters(r1.regex).forall(c => !usedCharacters(r2.regex).contains(c)) + } + + @inline + def rulesInvariant[C](rules: List[Rule[C]]): Boolean = + rulesValid(rules) && noDuplicateTag(rules, Nil()) + + /** Main function of the lexer + * + * It lexes the input list of characters using the set of rules + * + * It returns the produced list of Tokens and the remaining untokenised characters (normally empty) + * + * @param rules + * @param input + */ + def lex[C]( + rules: List[Rule[C]], + input: List[C] + ): (List[Token[C]], List[C]) = { + decreases(input.size) + require(!rules.isEmpty) + require(rulesInvariant(rules)) + val ret: (List[Token[C]], List[C]) = maxPrefix(rules, input) match { + case Some((token, suffix)) => { + val (followingTokens, nextSuffix) = lex(rules, suffix) + assert(token.characters ++ suffix == input) + (Cons(token, followingTokens), nextSuffix) + } + case None() => (Nil(), input) + } + ret + } ensuring (res => + if (res._1.size > 0) res._2.size < input.size && !res._1.isEmpty + else res._2 == input + ) + + /** Prints back the tokens to a list of characters of the type C + * + * @param l + */ + def print[C](l: List[Token[C]]): List[C] = { + decreases(l) + l match { + case Cons(hd, tl) => hd.characters ++ print(tl) + case Nil() => Nil[C]() + } + } + + /** Prints back the tokens to a list of characters of the type C, by adding a separatorToken between all of them, and after the last + * + * @param l + * @param separatorToken + */ + def printWithSeparatorToken[C](l: List[Token[C]], separatorToken: Token[C]): List[C] = { + require(separatorToken.isSeparator) + decreases(l) + l match { + case Cons(hd, tl) => hd.characters ++ separatorToken.characters ++ printWithSeparatorToken(tl, separatorToken) + case Nil() => Nil[C]() + } + } + + /** Prints back the tokens to a list of characters of the type C, by adding a separatorToken between tokens when the maxPrefix would return + * another token if printed back to back. + * + * @param l + * @param separatorToken + */ + def printWithSeparatorTokenWhenNeeded[C](rules: List[Rule[C]], l: List[Token[C]], separatorToken: Token[C]): List[C] = { + require(!rules.isEmpty) + require(rulesInvariant(rules)) + require(rulesProduceEachTokenIndividually(rules, l)) + require(rulesProduceIndivualToken(rules, separatorToken)) + require(separatorToken.isSeparator) + require(l.forall(!_.isSeparator)) + require(sepAndNonSepRulesDisjointChars(rules, rules)) + decreases(l) + + l match { + case Cons(hd, tl) => { + val suffix = printWithSeparatorTokenWhenNeeded(rules, tl, separatorToken) + val maxPrefWithoutSep = maxPrefix(rules, hd.characters ++ suffix) + maxPrefWithoutSep match { + case Some((t, s)) if t == hd => hd.characters ++ suffix + case Some((t, s)) if t != hd => hd.characters ++ separatorToken.characters ++ suffix + case None() => { + lemmaLexIsDefinedWithStrThenLexWithSuffixIsDefined(rules, hd.characters, suffix) + check(false) + Nil[C]() + } + } + } + case Nil() => Nil[C]() + } + } + + /** Finds the biggest prefix matching any rule in the input list of characters If nothing matched a rule, returns None Else, returns the matched + * prefix as Token and the remaining suffix + * + * @param rulesArg + * @param input + */ + def maxPrefix[C]( + rulesArg: List[Rule[C]], + input: List[C] + ): Option[(Token[C], List[C])] = { + require(rulesValid(rulesArg)) + require(!rulesArg.isEmpty) + decreases(rulesArg.size) + + ListUtils.lemmaIsPrefixRefl(input, input) + val ret: Option[(Token[C], List[C])] = rulesArg match { + case Cons(hd, Nil()) => maxPrefixOneRule(hd, input) + case Cons(hd, tl) => { + val currentRulePref = maxPrefixOneRule(hd, input) + val othersPrefix = maxPrefix(tl, input) + (currentRulePref, othersPrefix) match { + case (None(), None()) => None() + case (c, None()) => c + case (None(), o) => o + case (Some(c), Some(o)) if c._1.characters.size >= o._1.characters.size => Some(c) + case (Some(c), Some(o)) if c._1.characters.size < o._1.characters.size => Some(o) + } + } + } + ret + } ensuring (res => res.isEmpty || res.isDefined && (res.get._2.size < input.size && res.get._1.characters ++ res.get._2 == input)) + + /** Finds the biggest prefix matching any rule in the input list of characters If nothing matched a rule, returns None Else, returns the matched + * prefix and the remaining suffix + * + * @param rule + * @param input + */ + def maxPrefixOneRule[C]( + rule: Rule[C], + input: List[C] + ): Option[(Token[C], List[C])] = { + require(ruleValid(rule)) + + val (longestPrefix, suffix) = findLongestMatch(rule.regex, input) + if (longestPrefix.isEmpty) { + None[(Token[C], List[C])]() + } else { + longestMatchIsAcceptedByMatchOrIsEmpty(rule.regex, input) + Some[(Token[C], List[C])]((Token(longestPrefix, rule.tag, rule.isSeparator), suffix)) + } + + } ensuring (res => + res.isEmpty || matchR( + rule.regex, + res.get._1.characters + ) && res.get._1.characters ++ res.get._2 == input && res.get._2.size < input.size && res.get._1.tag == rule.tag && res.get._1.isSeparator == rule.isSeparator + ) + + // Proofs -------------------------------------------------------------------------------------------------------------------------------- + + // Correctness --------------------------------------------------------------------------------------------------------------------------- + + // The lexer is sound, i.e., if it produces a Tokenisation, it is valid w.r.t. the biggest prefix property + def theoremLexSoundFirstChar[C]( + rules: List[Rule[C]], + input: List[C], + suffix: List[C], + tokens: List[Token[C]], + r: Rule[C], + otherR: Rule[C], + otherP: List[C] + ): Unit = { + require(!rules.isEmpty) + require(rulesInvariant(rules)) + require(rules.contains(r)) + require(rules.contains(otherR)) + require(lex(rules, input) == (tokens, suffix)) + + require(tokens.isEmpty || tokens.head.characters.size <= otherP.size) + require(tokens.isEmpty || tokens.head.tag == r.tag) + require(tokens.isEmpty || tokens.head.isSeparator == r.isSeparator) + require(ListUtils.isPrefix(otherP, input)) + require(r != otherR) + require({ + lemmaRuleInListAndRulesValidThenRuleIsValid(r, rules) + tokens.isEmpty || matchR(r.regex, tokens.head.characters) + }) + + lemmaRuleInListAndRulesValidThenRuleIsValid(otherR, rules) + if (ListUtils.getIndex(rules, r) > ListUtils.getIndex(rules, otherR)) { + + tokens match { + case Nil() => { + lemmaMaxPrefixReturnsNoneThenAnyRuleReturnsNone(otherR, rules, input) + lemmaMaxPrefOneRuleReturnsNoneThenNoPrefMaxRegex(otherR, otherP, input) + } + case Cons(hd, tl) => { + val (tok, suf) = maxPrefix(rules, input).get + assert(hd == tok) + ListUtils.lemmaConcatTwoListThenFirstIsPrefix(hd.characters, suf) + ListUtils.lemmaSamePrefixThenSameSuffix(hd.characters, suf, hd.characters, ListUtils.getSuffix(input, hd.characters), input) + if (otherP.size == hd.characters.size) { + ListUtils.lemmaIsPrefixSameLengthThenSameList(hd.characters, otherP, input) + lemmaMaxPrefNoSmallerRuleMatches(rules, r, hd.characters, input, otherR) + } else { + lemmaMaxPrefixTagSoFindMaxPrefOneRuleWithThisRule(rules, hd.characters, input, suf, r) + lemmaMaxPrefixOutputsMaxPrefix(rules, r, hd.characters, input, otherP, otherR) + } + } + } + } else { + if (ListUtils.getIndex(rules, r) == ListUtils.getIndex(rules, otherR)) { + ListUtils.lemmaSameIndexThenSameElement(rules, r, otherR) + check(false) + } + + tokens match { + case Nil() => { + lemmaMaxPrefixReturnsNoneThenAnyRuleReturnsNone(otherR, rules, input) + lemmaMaxPrefOneRuleReturnsNoneThenNoPrefMaxRegex(otherR, otherP, input) + } + case Cons(hd, tl) => { + val (tok, suf) = maxPrefix(rules, input).get + ListUtils.lemmaConcatTwoListThenFirstIsPrefix(hd.characters, suf) + ListUtils.lemmaSamePrefixThenSameSuffix(hd.characters, suf, hd.characters, ListUtils.getSuffix(input, hd.characters), input) + if (otherP.size > hd.characters.size) { + lemmaMaxPrefixTagSoFindMaxPrefOneRuleWithThisRule(rules, hd.characters, input, suf, r) + lemmaMaxPrefixOutputsMaxPrefix(rules, r, hd.characters, input, otherP, otherR) + } + + } + } + } + + } ensuring (_ => + if (ListUtils.getIndex(rules, otherR) < ListUtils.getIndex(rules, r)) !matchR(otherR.regex, otherP) + else tokens.size > 0 && otherP.size <= tokens.head.characters.size || !matchR(otherR.regex, otherP) + ) + + // Invertability ------------------------------------------------------------------------------------------------------------------------- + + def theoremInvertabilityFromTokensSepTokenWhenNeeded[C](rules: List[Rule[C]], tokens: List[Token[C]], separatorToken: Token[C]): Unit = { + require(!rules.isEmpty) + require(rulesInvariant(rules)) + require(rulesProduceEachTokenIndividually(rules, tokens)) + require(rulesProduceIndivualToken(rules, separatorToken)) + require(separatorToken.isSeparator) + require(tokens.forall(!_.isSeparator)) + require({ + lemmaMaxPrefReturnTokenSoItsTagBelongsToARule(rules, separatorToken.characters, separatorToken) + getRuleFromTag(rules, separatorToken.tag).get.isSeparator + }) + require(sepAndNonSepRulesDisjointChars(rules, rules)) + decreases(tokens) + + tokens match { + case Cons(hd, tl) => { + val input = printWithSeparatorTokenWhenNeeded(rules, tokens, separatorToken) + val suffixWithSep = separatorToken.characters ++ printWithSeparatorTokenWhenNeeded(rules, tl, separatorToken) + ListUtils.lemmaTwoListsConcatAssociativity( + hd.characters, + separatorToken.characters, + printWithSeparatorTokenWhenNeeded(rules, tl, separatorToken) + ) + val suffixWithoutSep = printWithSeparatorTokenWhenNeeded(rules, tl, separatorToken) + assert(input == hd.characters ++ suffixWithSep || input == hd.characters ++ suffixWithoutSep) + + if (input == hd.characters ++ suffixWithSep) { + val suffixAfterSep = printWithSeparatorTokenWhenNeeded(rules, tl, separatorToken) + lemmaPrintWithSepTokenWhenNeededThenMaxPrefReturnsHead(rules, tokens, separatorToken) + ListUtils.lemmaConcatTwoListThenFirstIsPrefix(hd.characters, suffixWithSep) + ListUtils.lemmaSamePrefixThenSameSuffix(hd.characters, suffixWithSep, hd.characters, maxPrefix(rules, input).get._2, input) + + val nextToken = tl.head + val sepTokenOpt = maxPrefix(rules, suffixWithSep) + if (tl.isEmpty) { + assert(input == hd.characters ++ separatorToken.characters) + check(false) + } + lemmaRulesProduceEachTokenIndividuallyThenForAnyToken(rules, tokens, nextToken) + check(rulesProduceIndivualToken(rules, nextToken)) + lemmaMaxPrefReturnTokenSoItsTagBelongsToARule(rules, separatorToken.characters, separatorToken) + val separatorRule = getRuleFromTag(rules, separatorToken.tag).get + lemmaMaxPrefReturnTokenSoItsTagBelongsToARule(rules, tl.head.characters, nextToken) + val nextTokenRule = getRuleFromTag(rules, nextToken.tag).get + + if (!usedCharacters(nextTokenRule.regex).contains(nextToken.characters.head)) { + lemmaRegexCannotMatchAStringContainingACharItDoesNotContain(nextTokenRule.regex, nextToken.characters, nextToken.characters.head) + check(false) + } + if (usedCharacters(separatorRule.regex).contains(suffixAfterSep.head)) { + lemmaSepRuleNotContainsCharContainedInANonSepRule(rules, rules, nextTokenRule, separatorRule, suffixAfterSep.head) + check(false) + } + lemmaMaxPrefWithOtherTypeUsedCharAtStartOfSuffixReturnSame(rules, separatorToken, separatorRule, suffixAfterSep, nextTokenRule) + + theoremInvertabilityFromTokensSepTokenWhenNeeded(rules, tl, separatorToken) + } else { + lemmaPrintWithSepTokenWhenNeededThenMaxPrefReturnsHead(rules, tokens, separatorToken) + theoremInvertabilityFromTokensSepTokenWhenNeeded(rules, tl, separatorToken) + ListUtils.lemmaConcatTwoListThenFirstIsPrefix(hd.characters, suffixWithoutSep) + ListUtils.lemmaSamePrefixThenSameSuffix(hd.characters, suffixWithoutSep, hd.characters, maxPrefix(rules, input).get._2, input) + } + } + case Nil() => () + } + } ensuring (_ => lex(rules, printWithSeparatorTokenWhenNeeded(rules, tokens, separatorToken))._1.filter(!_.isSeparator) == tokens) + + def theoremInvertFromTokensSepTokenBetweenEach[C](rules: List[Rule[C]], tokens: List[Token[C]], separatorToken: Token[C]): Unit = { + require(!rules.isEmpty) + require(rulesInvariant(rules)) + require(rulesProduceEachTokenIndividually(rules, tokens)) + require(rulesProduceIndivualToken(rules, separatorToken)) + require(separatorToken.isSeparator) + require(tokens.forall(!_.isSeparator)) + require({ + lemmaMaxPrefReturnTokenSoItsTagBelongsToARule(rules, separatorToken.characters, separatorToken) + getRuleFromTag(rules, separatorToken.tag).get.isSeparator + }) + require(sepAndNonSepRulesDisjointChars(rules, rules)) + decreases(tokens.size) + + tokens match { + case Nil() => () + // case Cons(hd, Nil()) => () + case Cons(hd, Nil()) => { + ListSpecs.forallContained(tokens, (t: Token[C]) => !t.isSeparator, hd) + assert(!hd.isSeparator) + val input = printWithSeparatorToken(tokens, separatorToken) + assert(input == hd.characters ++ separatorToken.characters) + ListUtils.lemmaGetSuffixOnListWithItSelfIsEmpty(hd.characters) + lemmaRulesProduceEachTokenIndividuallyThenForAnyToken(rules, tokens, hd) + lemmaMaxPrefReturnTokenSoItsTagBelongsToARule(rules, hd.characters, hd) + val rule = getRuleFromTag(rules, hd.tag).get + assert(!rule.isSeparator) + lemmaMaxPrefReturnTokenSoItsTagBelongsToARule(rules, separatorToken.characters, separatorToken) + val separatorRule = getRuleFromTag(rules, separatorToken.tag).get + lemmaMaxPrefixTagSoFindMaxPrefOneRuleWithThisRule(rules, hd.characters, hd.characters, Nil(), rule) + + if (!usedCharacters(separatorRule.regex).contains(separatorToken.characters.head)) { + lemmaRegexCannotMatchAStringContainingACharItDoesNotContain( + separatorRule.regex, + separatorToken.characters, + separatorToken.characters.head + ) + check(false) + } + assert(usedCharacters(separatorRule.regex).contains(separatorToken.characters.head)) + + lemmaNonSepRuleNotContainsCharContainedInASepRule(rules, rules, rule, separatorRule, separatorToken.characters.head) + + lemmaMaxPrefWithOtherTypeUsedCharAtStartOfSuffixReturnSame(rules, hd, rule, separatorToken.characters, separatorRule) + + val suffix = maxPrefix(rules, input).get._2 + + // needed + val ret: (List[Token[C]], List[C]) = maxPrefix(rules, input) match { + case Some((t, s)) => { + assert(s == suffix) + assert(t == hd) + val (followingTokens, nextSuffix) = lex(rules, s) + assert(nextSuffix.isEmpty) + assert(t.characters ++ s == input) + (Cons(t, followingTokens), nextSuffix) + } + case None() => { + check(false) + (Nil(), input) + } + } + + } + case Cons(hd, Cons(nextT, tl)) => { + ListSpecs.forallContained(tokens, (t: Token[C]) => !t.isSeparator, hd) + ListSpecs.forallContained(tokens, (t: Token[C]) => !t.isSeparator, nextT) + assert(!hd.isSeparator) + assert(!nextT.isSeparator) + val input = printWithSeparatorToken(tokens, separatorToken) + val suffixAfterSeparator = printWithSeparatorToken(Cons(nextT, tl), separatorToken) + val suffix = separatorToken.characters ++ suffixAfterSeparator + assert(suffixAfterSeparator == nextT.characters ++ separatorToken.characters ++ printWithSeparatorToken(tl, separatorToken)) + assert(input == hd.characters ++ separatorToken.characters ++ suffixAfterSeparator) + ListUtils.lemmaTwoListsConcatAssociativity(hd.characters, separatorToken.characters, suffixAfterSeparator) + assert(input == hd.characters ++ suffix) + lemmaRulesProduceEachTokenIndividuallyThenForAnyToken(rules, tokens, hd) + lemmaMaxPrefReturnTokenSoItsTagBelongsToARule(rules, hd.characters, hd) + val rule = getRuleFromTag(rules, hd.tag).get + assert(!rule.isSeparator) + lemmaMaxPrefixTagSoFindMaxPrefOneRuleWithThisRule(rules, hd.characters, hd.characters, Nil(), rule) + + lemmaMaxPrefReturnTokenSoItsTagBelongsToARule(rules, separatorToken.characters, separatorToken) + val separatorRule = getRuleFromTag(rules, separatorToken.tag).get + + if (!usedCharacters(separatorRule.regex).contains(separatorToken.characters.head)) { + lemmaRegexCannotMatchAStringContainingACharItDoesNotContain( + separatorRule.regex, + separatorToken.characters, + separatorToken.characters.head + ) + check(false) + } + + assert(suffixAfterSeparator == nextT.characters ++ separatorToken.characters ++ printWithSeparatorToken(tl, separatorToken)) + lemmaNonSepRuleNotContainsCharContainedInASepRule(rules, rules, rule, separatorRule, separatorToken.characters.head) + + lemmaMaxPrefWithOtherTypeUsedCharAtStartOfSuffixReturnSame(rules, hd, rule, suffix, separatorRule) + + lemmaRulesProduceEachTokenIndividuallyThenForAnyToken(rules, tokens, nextT) + assert(rulesProduceIndivualToken(rules, nextT)) + lemmaMaxPrefReturnTokenSoItsTagBelongsToARule(rules, nextT.characters, nextT) + val nextTRule = getRuleFromTag(rules, nextT.tag).get + assert(!nextTRule.isSeparator) + lemmaMaxPrefixTagSoFindMaxPrefOneRuleWithThisRule(rules, nextT.characters, nextT.characters, Nil(), nextTRule) + + if (!usedCharacters(nextTRule.regex).contains(nextT.characters.head)) { + lemmaRegexCannotMatchAStringContainingACharItDoesNotContain(nextTRule.regex, nextT.characters, nextT.characters.head) + check(false) + } + + lemmaSepRuleNotContainsCharContainedInANonSepRule(rules, rules, nextTRule, separatorRule, nextT.characters.head) + + lemmaMaxPrefWithOtherTypeUsedCharAtStartOfSuffixReturnSame(rules, separatorToken, separatorRule, suffixAfterSeparator, nextTRule) + + theoremInvertFromTokensSepTokenBetweenEach(rules, Cons(nextT, tl), separatorToken) + + } + } + + } ensuring (_ => lex(rules, printWithSeparatorToken(tokens, separatorToken))._1.filter(!_.isSeparator) == tokens) + + def theoremInvertFromString[C](rules: List[Rule[C]], input: List[C]): Unit = { + require(!rules.isEmpty) + require(rulesInvariant(rules)) + decreases(input.size) + + val (tokens, suffix) = lex(rules, input) + if (suffix.isEmpty) { + tokens match { + case Cons(hd, Nil()) => () + case Cons(hd, tl) => theoremInvertFromString(rules, maxPrefix(rules, input).get._2) + case Nil() => () + } + } else { + tokens match { + case Cons(hd, Nil()) => assert(print(tokens) ++ suffix == input) + case Cons(hd, tl) => { + theoremInvertFromString(rules, maxPrefix(rules, input).get._2) + lemmaRemovingFirstTokensCharactersPreservesLexSuffix(rules, input, tokens, suffix) + + assert(input == maxPrefix(rules, input).get._1.characters ++ maxPrefix(rules, input).get._2) + assert(input == maxPrefix(rules, input).get._1.characters ++ (print(tl) ++ suffix)) + ListUtils.lemmaTwoListsConcatAssociativity( + maxPrefix(rules, input).get._1.characters, + print(tl), + suffix + ) + } + case Nil() => assert(print(tokens) ++ suffix == input) + } + } + } ensuring (_ => { + val (tokens, suffix) = lex(rules, input) + print(tokens) ++ suffix == input + }) + + // Functions ----------------------------------------------------------------------------------------------------------------------------- + + def getRuleFromTag[C](rules: List[Rule[C]], tag: String): Option[Rule[C]] = { + require(rulesInvariant(rules)) + rules match { + case Cons(hd, tl) if hd.tag == tag => Some(hd) + case Cons(hd, tl) if hd.tag != tag => { + lemmaInvariantOnRulesThenOnTail(hd, tl) + getRuleFromTag(tl, tag) + } + case Nil() => None[Rule[C]]() + } + } ensuring (res => res.isEmpty || rules.contains(res.get) && res.get.tag == tag) + + // Lemmas -------------------------------------------------------------------------------------------------------------------------------- + + def lemmaPrintWithSepTokenWhenNeededThenMaxPrefReturnsHead[C](rules: List[Rule[C]], tokens: List[Token[C]], separatorToken: Token[C]): Unit = { + require(!rules.isEmpty) + require(rulesInvariant(rules)) + require(rulesProduceEachTokenIndividually(rules, tokens)) + require(rulesProduceIndivualToken(rules, separatorToken)) + require(separatorToken.isSeparator) + require(tokens.forall(!_.isSeparator)) + require(sepAndNonSepRulesDisjointChars(rules, rules)) + + tokens match { + case Cons(hd, tl) => { + lemmaMaxPrefReturnTokenSoItsTagBelongsToARule(rules, separatorToken.characters, separatorToken) + val separatorRule = getRuleFromTag(rules, separatorToken.tag).get + + lemmaMaxPrefReturnTokenSoItsTagBelongsToARule(rules, hd.characters, hd) + val rule = getRuleFromTag(rules, hd.tag).get + + val suffix = printWithSeparatorTokenWhenNeeded(rules, tl, separatorToken) + val maxPrefWithoutSep = maxPrefix(rules, hd.characters ++ suffix) + maxPrefWithoutSep match { + case Some((t, s)) if t == hd => () + case Some((t, s)) if t != hd => { + ListUtils.lemmaTwoListsConcatAssociativity(hd.characters, separatorToken.characters, suffix) + val resSuffix = separatorToken.characters ++ suffix + if (!usedCharacters(separatorRule.regex).contains(separatorToken.characters.head)) { + lemmaRegexCannotMatchAStringContainingACharItDoesNotContain( + separatorRule.regex, + separatorToken.characters, + separatorToken.characters.head + ) + check(false) + } + lemmaNonSepRuleNotContainsCharContainedInASepRule(rules, rules, rule, separatorRule, separatorToken.characters.head) + + check(maxPrefix(rules, hd.characters).isDefined) + check(maxPrefix(rules, hd.characters).get._1 == hd) + lemmaMaxPrefWithOtherTypeUsedCharAtStartOfSuffixReturnSame(rules, hd, rule, resSuffix, separatorRule) + } + case None() => { + lemmaLexIsDefinedWithStrThenLexWithSuffixIsDefined(rules, hd.characters, suffix) + check(false) + } + } + } + case Nil() => () + } + + } ensuring (_ => + tokens.isEmpty || + (!tokens.isEmpty && + maxPrefix(rules, printWithSeparatorTokenWhenNeeded(rules, tokens, separatorToken)).isDefined && + maxPrefix(rules, printWithSeparatorTokenWhenNeeded(rules, tokens, separatorToken)).get._1 == tokens.head) + ) + + def lemmaMaxPrefWithOtherTypeUsedCharAtStartOfSuffixReturnSame[C]( + rules: List[Rule[C]], + token: Token[C], + rule: Rule[C], + suffix: List[C], + anOtherTypeRule: Rule[C] + ): Unit = { + require(!rules.isEmpty) + require(rulesInvariant(rules)) + require(rules.contains(rule)) + require(rules.contains(anOtherTypeRule)) + require(anOtherTypeRule.isSeparator != rule.isSeparator) + require(maxPrefix(rules, token.characters).isDefined) + require(maxPrefix(rules, token.characters).get._1 == token) + require(maxPrefix(rules, token.characters).get._2.isEmpty) + require(token.tag == rule.tag) + require(token.isSeparator == rule.isSeparator) + require({ + lemmaRuleInListAndRulesValidThenRuleIsValid(rule, rules) + matchR(rule.regex, token.characters) + }) + require(!suffix.isEmpty) + require(!usedCharacters(rule.regex).contains(suffix.head)) + require(usedCharacters(anOtherTypeRule.regex).contains(suffix.head)) + require(sepAndNonSepRulesDisjointChars(rules, rules)) + + val input = token.characters ++ suffix + ListUtils.lemmaConcatTwoListThenFirstIsPrefix(token.characters, suffix) + val tokenOpt = maxPrefix(rules, input) + lemmaLexIsDefinedWithStrThenLexWithSuffixIsDefined(rules, token.characters, suffix) + val foundToken = tokenOpt.get._1 + val foundSuffix = tokenOpt.get._2 + lemmaMaxPrefReturnTokenSoItsTagBelongsToARule(rules, input, foundToken) + val foundRule = getRuleFromTag(rules, foundToken.tag).get + ListUtils.lemmaConcatTwoListThenFirstIsPrefix(foundToken.characters, foundSuffix) + assert(ListUtils.isPrefix(foundToken.characters, input)) + assert(foundRule.tag == foundToken.tag) + assert(matchR(foundRule.regex, foundToken.characters)) + assert(foundRule.isSeparator == foundToken.isSeparator) + + lemmaMaxPrefixTagSoFindMaxPrefOneRuleWithThisRule(rules, foundToken.characters, input, foundSuffix, foundRule) + ListUtils.lemmaSamePrefixThenSameSuffix( + foundToken.characters, + foundSuffix, + foundToken.characters, + ListUtils.getSuffix(input, foundToken.characters), + input + ) + assert(ListUtils.getSuffix(input, foundToken.characters) == foundSuffix) + assert(maxPrefixOneRule(foundRule, input) == Some((foundToken, ListUtils.getSuffix(input, foundToken.characters)))) + + if (!usedCharacters(rule.regex).contains(foundToken.characters.head)) { + lemmaRegexCannotMatchAStringContainingACharItDoesNotContain(rule.regex, token.characters, foundToken.characters.head) + check(false) + } + if (rule.isSeparator) { + if (!foundRule.isSeparator) { + assert(token.characters.contains(foundToken.characters.head)) + assert(usedCharacters(rule.regex).contains(foundToken.characters.head)) + lemmaNonSepRuleNotContainsCharContainedInASepRule(rules, rules, foundRule, rule, foundToken.characters.head) + lemmaRegexCannotMatchAStringContainingACharItDoesNotContain(foundRule.regex, foundToken.characters, foundToken.characters.head) + check(false) + } + } else { + if (foundRule.isSeparator) { + assert(token.characters.contains(foundToken.characters.head)) + assert(usedCharacters(rule.regex).contains(foundToken.characters.head)) + lemmaSepRuleNotContainsCharContainedInANonSepRule(rules, rules, rule, foundRule, foundToken.characters.head) + lemmaRegexCannotMatchAStringContainingACharItDoesNotContain(foundRule.regex, foundToken.characters, foundToken.characters.head) + check(false) + } + } + + if (foundToken.characters.size > token.characters.size) { + ListUtils.lemmaLongerPrefixContainsHeadOfSuffixOfSmallerPref(token.characters, suffix, foundToken.characters, input) + if (rule.isSeparator) { + lemmaSepRuleNotContainsCharContainedInANonSepRule(rules, rules, anOtherTypeRule, foundRule, suffix.head) + lemmaRegexCannotMatchAStringContainingACharItDoesNotContain(foundRule.regex, foundToken.characters, suffix.head) + check(false) + } else { + lemmaNonSepRuleNotContainsCharContainedInASepRule(rules, rules, foundRule, anOtherTypeRule, suffix.head) + lemmaRegexCannotMatchAStringContainingACharItDoesNotContain(foundRule.regex, foundToken.characters, suffix.head) + check(false) + } + } + if (foundToken.characters.size < token.characters.size) { + lemmaMaxPrefixOutputsMaxPrefix(rules, foundRule, foundToken.characters, input, token.characters, rule) + check(false) + } + ListUtils.lemmaIsPrefixSameLengthThenSameList(foundToken.characters, token.characters, input) + + assert(foundToken.characters == token.characters) + + if (foundToken.tag != token.tag) { + assert(foundRule != rule) + val foundRuleIndex = ListUtils.getIndex(rules, foundRule) + val ruleIndex = ListUtils.getIndex(rules, rule) + if (foundRuleIndex < ruleIndex) { + ListUtils.lemmaGetSuffixOnListWithItSelfIsEmpty(token.characters) + assert(ListUtils.getSuffix(token.characters, token.characters).isEmpty) + lemmaMaxPrefNoSmallerRuleMatches(rules, rule, token.characters, token.characters, foundRule) + check(false) + } + if (ruleIndex < foundRuleIndex) { + lemmaMaxPrefNoSmallerRuleMatches(rules, foundRule, token.characters, input, rule) + check(false) + } + + ListUtils.lemmaSameIndexThenSameElement(rules, foundRule, rule) + check(false) + } + assert(foundToken.tag == token.tag) + assert(foundToken.tag == rule.tag) + + lemmaMaxPrefixTagSoFindMaxPrefOneRuleWithThisRule(rules, token.characters, input, ListUtils.getSuffix(input, token.characters), rule) + ListUtils.lemmaSamePrefixThenSameSuffix( + token.characters, + ListUtils.getSuffix(input, token.characters), + foundToken.characters, + foundSuffix, + input + ) + ListUtils.lemmaSamePrefixThenSameSuffix(token.characters, suffix, foundToken.characters, foundSuffix, input) + + } ensuring (_ => maxPrefix(rules, token.characters ++ suffix) == Some((token, suffix))) + + def lemmaLexIsDefinedWithStrThenLexWithSuffixIsDefined[C](rules: List[Rule[C]], input: List[C], suffix: List[C]): Unit = { + require(!rules.isEmpty) + require(rulesInvariant(rules)) + require(!lex(rules, input)._1.isEmpty) + + val (tokens, _) = lex(rules, input) + val firstT = tokens.head + ListUtils.lemmaConcatTwoListThenFirstIsPrefix(firstT.characters, maxPrefix(rules, input).get._2) + ListUtils.lemmaPrefixStaysPrefixWhenAddingToSuffix(firstT.characters, input, suffix) + lemmaMaxPrefReturnTokenSoItsTagBelongsToARule(rules, input, firstT) + val rule: Rule[C] = getRuleFromTag(rules, firstT.tag).get + assert(matchR(rule.regex, firstT.characters)) + + if (maxPrefix(rules, input ++ suffix).isEmpty) { + lemmaMaxPrefixReturnsNoneThenAnyRuleReturnsNone(rule, rules, input ++ suffix) + lemmaMaxPrefOneRuleReturnsNoneThenNoPrefMaxRegex(rule, firstT.characters, input ++ suffix) + check(false) + } + + } ensuring (_ => maxPrefix(rules, input ++ suffix).isDefined) + + def lemmaMaxPrefReturnTokenSoItsTagBelongsToARule[C](rules: List[Rule[C]], input: List[C], token: Token[C]): Unit = { + require(rulesInvariant(rules)) + require(!rules.isEmpty) + require(maxPrefix(rules, input).isDefined && maxPrefix(rules, input).get._1 == token) + decreases(rules) + + rules match { + case Cons(hd, tl) => { + if (maxPrefixOneRule(hd, input).isDefined && maxPrefixOneRule(hd, input).get._1 == token) { + assert(hd.tag == token.tag) + assert(matchR(hd.regex, token.characters)) + } else { + if (!tl.isEmpty) { + lemmaInvariantOnRulesThenOnTail(hd, tl) + lemmaMaxPrefReturnTokenSoItsTagBelongsToARule(tl, input, token) + lemmaGetRuleFromTagInListThenSameListWhenAddingARuleDiffTag(tl, hd, token.tag) + } else { + check(false) + } + } + } + case Nil() => () + } + } ensuring (_ => + getRuleFromTag(rules, token.tag).isDefined && matchR(getRuleFromTag(rules, token.tag).get.regex, token.characters) && + token.isSeparator == getRuleFromTag(rules, token.tag).get.isSeparator + ) + + def lemmaGetRuleFromTagInListThenSameListWhenAddingARuleDiffTag[C](rules: List[Rule[C]], newHd: Rule[C], tag: String): Unit = { + require(rulesInvariant(Cons(newHd, rules))) + require({ + lemmaInvariantOnRulesThenOnTail(newHd, rules) + getRuleFromTag(rules, tag).isDefined + }) + + lemmaInvariantOnRulesThenOnTail(newHd, rules) + lemmaNoDuplicateAndTagInAccThenRuleCannotHaveSame(rules, getRuleFromTag(rules, tag).get, newHd.tag, List(newHd.tag)) + + } ensuring (_ => getRuleFromTag(rules, tag).get == getRuleFromTag(Cons(newHd, rules), tag).get) + + def lemmaRemovingFirstTokensCharactersPreservesLexSuffix[C]( + rules: List[Rule[C]], + input: List[C], + producedTokens: List[Token[C]], + suffix: List[C] + ): Unit = { + require(rulesInvariant(rules)) + require(!rules.isEmpty) + require(producedTokens.size > 0) + require(lex(rules, input) == (producedTokens, suffix)) + } ensuring (_ => lex(rules, maxPrefix(rules, input).get._2) == (producedTokens.tail, suffix)) + + def lemmaMaxPrefNoneThenNoRuleMatches[C](rules: List[Rule[C]], r: Rule[C], p: List[C], input: List[C]): Unit = { + require(ListUtils.isPrefix(p, input)) + require(!rules.isEmpty) + require(rulesInvariant(rules)) + require(rules.contains(r)) + require(maxPrefix(rules, input) == None[(Token[C], List[C])]()) + + lemmaRuleInListAndRulesValidThenRuleIsValid(r, rules) + + lemmaMaxPrefixReturnsNoneThenAnyRuleReturnsNone(r, rules, input) + lemmaMaxPrefOneRuleReturnsNoneThenNoPrefMaxRegex(r, p, input) + + } ensuring (_ => !matchR(r.regex, p)) + + def lemmaMaxPrefNoSmallerRuleMatches[C]( + rules: List[Rule[C]], + r: Rule[C], + p: List[C], + input: List[C], + rBis: Rule[C] + ): Unit = { + require(ListUtils.isPrefix(p, input)) + require(!rules.isEmpty) + require(rulesInvariant(rules)) + require(rules.contains(r)) + require(rules.contains(rBis)) + require(maxPrefix(rules, input) == Some(Token(p, r.tag, r.isSeparator), ListUtils.getSuffix(input, p))) + require(ListUtils.getIndex(rules, rBis) < ListUtils.getIndex(rules, r)) + require(ruleValid(r)) + require(matchR(r.regex, p)) + decreases(rules) + + assert(ListUtils.getIndex(rules, rBis) < ListUtils.getIndex(rules, r)) + + lemmaRuleInListAndRulesValidThenRuleIsValid(rBis, rules) + lemmaRuleInListAndRulesValidThenRuleIsValid(r, rules) + rules match { + case Cons(hd, tl) if hd == rBis => { + ListUtils.lemmaGetIndexBiggerAndHeadEqThenTailContains(rules, rBis, r) + lemmaNoDuplicateTagAndDiffIndexThenNoTwoRulesEq(rules, rBis, r) + + val tokenSuffOpt = maxPrefixOneRule(rBis, input) + if (tokenSuffOpt.isEmpty) { + lemmaMaxPrefOneRuleReturnsNoneThenNoPrefMaxRegex(rBis, p, input) + } else { + val (token, suff) = tokenSuffOpt.get + if (token.characters.size > p.size) { + lemmaRuleReturnsPrefixSmallerEqualThanGlobalMaxPref( + rules, + p, + Token(p, r.tag, r.isSeparator), + input, + ListUtils.getSuffix(input, p), + token.characters, + ListUtils.getSuffix(input, token.characters), + rBis, + token + ) + check(false) + check(!matchR(rBis.regex, p)) + } else { + if (token.characters.size < p.size) { + ListUtils.lemmaConcatTwoListThenFirstIsPrefix(token.characters, suff) + lemmaMaxPrefixOneRuleOutputsMaxPrefix(rBis, token.characters, token, input, suff, p) + check(!matchR(rBis.regex, p)) + } else { + lemmaNoDuplicateTagAndDiffIndexThenNoTwoRulesTagsEq(rules, rBis, r) + check(Some(token, suff) != Some(Token(p, r.tag, r.isSeparator), ListUtils.getSuffix(input, p))) + check(!matchR(rBis.regex, p)) + } + } + } + } + case Cons(hd, tl) if hd != rBis => { + assert(tl.contains(r)) + assert(tl.contains(rBis)) + + val tokenSuffOpt = maxPrefixOneRule(hd, input) + val tokenSuffTailOpt = maxPrefix(tl, input) + + lemmaInvariantOnRulesThenOnTail(hd, tl) + lemmaNoDuplicateTagAndDiffIndexThenNoTwoRulesTagsEq(rules, hd, r) + lemmaMaxPrefNoSmallerRuleMatches(tl, r, p, input, rBis) + } + case Nil() => check(false) + } + } ensuring (_ => !matchR(rBis.regex, p)) + + /** Lemma which proves that indeed the getMaxPrefix indeed returns the maximal prefix that matches any rules + * + * @param rules + * @param r + * @param p + * @param input + * @param pBis + * @param rBis + */ + def lemmaMaxPrefixOutputsMaxPrefix[C]( + rules: List[Rule[C]], + r: Rule[C], + p: List[C], + input: List[C], + pBis: List[C], + rBis: Rule[C] + ): Unit = { + require(ListUtils.isPrefix(p, input)) + require(ListUtils.isPrefix(pBis, input)) + + require(!rules.isEmpty) + require(rulesInvariant(rules)) + require(rules.contains(r)) + require(rules.contains(rBis)) + + require({ + lemmaRuleInListAndRulesValidThenRuleIsValid(r, rules) + matchR(r.regex, p) + }) + require({ + ListUtils.lemmaIsPrefixRefl(input, input) + maxPrefixOneRule(r, input) == Some(Token(p, r.tag, r.isSeparator), ListUtils.getSuffix(input, p)) + }) + + require(pBis.size > p.size) + + require(maxPrefix(rules, input) == Some(Token(p, r.tag, r.isSeparator), ListUtils.getSuffix(input, p))) + + // For preconditions + lemmaRuleInListAndRulesValidThenRuleIsValid(r, rules) + lemmaRuleInListAndRulesValidThenRuleIsValid(rBis, rules) + ListUtils.lemmaIsPrefixRefl(input, input) + + // Main lemma + lemmaMaxPrefixOutputsMaxPrefixInner(rules, r, p, input, pBis, rBis) + + } ensuring (_ => !matchR(rBis.regex, pBis)) + + def lemmaMaxPrefixOutputsMaxPrefixInner[C]( + rules: List[Rule[C]], + r: Rule[C], + p: List[C], + input: List[C], + pBis: List[C], + rBis: Rule[C] + ): Unit = { + require(ListUtils.isPrefix(p, input)) + require(ListUtils.isPrefix(pBis, input)) + + require(!rules.isEmpty) + require(rulesInvariant(rules)) + require(rules.contains(r)) + require(rules.contains(rBis)) + + require(validRegex(r.regex)) + require(matchR(r.regex, p)) + require(ruleValid(r)) + require({ + ListUtils.lemmaIsPrefixRefl(input, input) + maxPrefixOneRule(r, input) == Some(Token(p, r.tag, r.isSeparator), ListUtils.getSuffix(input, p)) + }) + + require(pBis.size > p.size) + + require(ruleValid(rBis)) + require(maxPrefix(rules, input) == Some(Token(p, r.tag, r.isSeparator), ListUtils.getSuffix(input, p))) + + assert(validRegex(r.regex)) + + ListUtils.lemmaIsPrefixThenSmallerEqSize(pBis, input) + lemmaRuleInListAndRulesValidThenRuleIsValid(rBis, rules) + + val bisTokenSuff = maxPrefixOneRule(rBis, input) // == Some(Token(pBis, rBis.tag), ListUtils.getSuffix(input, pBis)) + if (bisTokenSuff.isEmpty) { + lemmaMaxPrefOneRuleReturnsNoneThenNoPrefMaxRegex(rBis, pBis, input) + check(!matchR(rBis.regex, pBis)) + } else { + val tBis = bisTokenSuff.get._1 + val suffixBis = bisTokenSuff.get._2 + ListUtils.lemmaConcatTwoListThenFirstIsPrefix(tBis.characters, suffixBis) + if (tBis.characters == pBis) { + lemmaRuleReturnsPrefixSmallerEqualThanGlobalMaxPref( + rules, + p, + Token(p, r.tag, r.isSeparator), + input, + ListUtils.getSuffix(input, p), + pBis, + suffixBis, + rBis, + tBis + ) + check(!matchR(rBis.regex, pBis)) + } else { + if (tBis.characters.size < pBis.size) { + assert(ListUtils.isPrefix(tBis.characters, input)) + lemmaMaxPrefixOneRuleOutputsMaxPrefix(rBis, tBis.characters, tBis, input, suffixBis, pBis) + check(!matchR(rBis.regex, pBis)) + } else { + if (pBis.size == tBis.characters.size) { + ListUtils.lemmaIsPrefixSameLengthThenSameList(pBis, tBis.characters, input) + check(false) + } + assert(pBis.size < tBis.characters.size) + lemmaRuleReturnsPrefixSmallerEqualThanGlobalMaxPref( + rules, + p, + Token(p, r.tag, r.isSeparator), + input, + ListUtils.getSuffix(input, p), + tBis.characters, + suffixBis, + rBis, + tBis + ) + assert(tBis.characters.size <= p.size) + check(false) + check(!matchR(rBis.regex, pBis)) + + } + } + + } + + } ensuring (_ => !matchR(rBis.regex, pBis)) + + def lemmaMaxPrefixTagSoFindMaxPrefOneRuleWithThisRule[C]( + rules: List[Rule[C]], + p: List[C], + input: List[C], + suffix: List[C], + r: Rule[C] + ): Unit = { + require(!rules.isEmpty) + require(rulesInvariant(rules)) + require(rules.contains(r)) + require(input == p ++ suffix) + require(maxPrefix(rules, input) == Some(Token(p, r.tag, r.isSeparator), suffix)) + require({ + lemmaRuleInListAndRulesValidThenRuleIsValid(r, rules) + matchR(r.regex, p) + }) + decreases(rules.size) + + rules match { + case Cons(hd, tl) if hd == r => { + lemmaInvariantOnRulesThenOnTail(hd, tl) + if (tl.isEmpty) { + check(maxPrefixOneRule(r, input) == Some(Token(p, r.tag, r.isSeparator), suffix)) + } else { + lemmaNoDuplTagThenTailRulesCannotProduceHeadTagInTok(hd, tl, input) + assert(maxPrefix(tl, input).isEmpty || maxPrefix(tl, input).get._1.tag != r.tag) + check(maxPrefixOneRule(r, input) == Some(Token(p, r.tag, r.isSeparator), suffix)) + } + } + case Cons(hd, tl) if hd != r => { + lemmaInvariantOnRulesThenOnTail(hd, tl) + val otherTokSufOpt = maxPrefixOneRule(hd, input) + if (otherTokSufOpt.isEmpty) { + lemmaMaxPrefixTagSoFindMaxPrefOneRuleWithThisRule(tl, p, input, suffix, r) + } else { + assert(otherTokSufOpt.get._1.tag == hd.tag) + if (otherTokSufOpt.get._1.tag == r.tag) { + assert(hd.tag == r.tag) + lemmaNoDuplicateTagAndDiffIndexThenNoTwoRulesTagsEq(rules, hd, r) + check(false) + } else { + assert(otherTokSufOpt.get._1.tag != r.tag) + assert(maxPrefixOneRule(hd, input) != Some(Token(p, r.tag, r.isSeparator), suffix)) + assert(maxPrefix(tl, input) == Some(Token(p, r.tag, r.isSeparator), suffix)) + lemmaMaxPrefixTagSoFindMaxPrefOneRuleWithThisRule(tl, p, input, suffix, r) + } + + } + } + case Nil() => check(false) + + } + + } ensuring (_ => maxPrefixOneRule(r, input) == Some(Token(p, r.tag, r.isSeparator), suffix)) + + def lemmaNoDuplTagThenTailRulesCannotProduceHeadTagInTok[C](rHead: Rule[C], rTail: List[Rule[C]], input: List[C]): Unit = { + require(!rTail.isEmpty) + require(rulesInvariant(Cons(rHead, rTail))) + decreases(rTail) + + rTail match { + case Cons(hd, tl) => { + lemmaNoDuplicateCanReorder(rHead, hd, tl) + + lemmaNoDuplicateTagAndDiffIndexThenNoTwoRulesTagsEq(Cons(rHead, rTail), rHead, hd) + if (!tl.isEmpty) { + lemmaNoDupTagThenAlsoWithSubListAcc(List(hd.tag), Nil(), Cons(rHead, tl)) + lemmaNoDuplTagThenTailRulesCannotProduceHeadTagInTok(rHead, tl, input) + } + } + case Nil() => check(false) + } + + } ensuring (_ => maxPrefix(rTail, input).isEmpty || maxPrefix(rTail, input).get._1.tag != rHead.tag) + + def lemmaRuleReturnsPrefixSmallerEqualThanGlobalMaxPref[C]( + rules: List[Rule[C]], + p: List[C], + t: Token[C], + input: List[C], + suffix: List[C], + pBis: List[C], + suffixBis: List[C], + rBis: Rule[C], + tBis: Token[C] + ): Unit = { + decreases(rules) + require(p ++ suffix == input) + require(ListUtils.isPrefix(p, input)) + require(ListUtils.isPrefix(pBis, input)) + + require(!rules.isEmpty) + require(rulesInvariant(rules)) + require(rules.contains(rBis)) + require(maxPrefix(rules, input) == Some(t, suffix)) + require(ruleValid(rBis)) + require({ + ListUtils.lemmaIsPrefixRefl(input, input) + maxPrefixOneRule(rBis, input) == Some(tBis, suffixBis) + }) + require(tBis.tag == rBis.tag) + require(tBis.characters == pBis) + require(pBis ++ suffixBis == input) + + rules match { + case Cons(hd, Nil()) => () + case Cons(hd, tl) => { + if (hd == rBis) { + check(pBis.size <= p.size) + } else { + if (maxPrefixOneRule(hd, input) == Some(t, suffix)) { + val othersPrefix = maxPrefix(tl, input) + if (othersPrefix.isEmpty) { + lemmaMaxPrefixReturnsNoneThenAnyRuleReturnsNone(rBis, tl, input) + check(false) + } + val tokSuff = othersPrefix.get + val oPref = tokSuff._1.characters + val suff = tokSuff._2 + ListUtils.lemmaConcatTwoListThenFirstIsPrefix(oPref, suff) + lemmaInvariantOnRulesThenOnTail(hd, tl) + lemmaRuleReturnsPrefixSmallerEqualThanGlobalMaxPref(tl, oPref, tokSuff._1, input, suff, pBis, suffixBis, rBis, tBis) + check(pBis.size <= p.size) + } else { + lemmaInvariantOnRulesThenOnTail(hd, tl) + lemmaRuleReturnsPrefixSmallerEqualThanGlobalMaxPref(tl, p, t, input, suffix, pBis, suffixBis, rBis, tBis) + } + } + } + case Nil() => check(false) + } + + } ensuring (_ => pBis.size <= p.size) + + def lemmaMaxPrefixReturnsNoneThenAnyRuleReturnsNone[C]( + r: Rule[C], + rules: List[Rule[C]], + input: List[C] + ): Unit = { + require(!rules.isEmpty) + require(rulesValid(rules)) + require(rules.contains(r)) + + require(maxPrefix(rules, input).isEmpty) + decreases(rules) + + lemmaRuleInListAndRulesValidThenRuleIsValid(r, rules) + + rules match { + case Cons(hd, tl) if r == hd => () + case Cons(hd, tl) if r != hd => { + + lemmaMaxPrefixReturnsNoneThenAnyRuleReturnsNone(r, tl, input) + } + case Nil() => () + } + + } ensuring (_ => maxPrefixOneRule(r, input).isEmpty) + + def lemmaMaxPrefixOneRuleOutputsMaxPrefix[C]( + r: Rule[C], + p: List[C], + t: Token[C], + input: List[C], + suffix: List[C], + pBis: List[C] + ): Unit = { + decreases(input.size) + + require(p ++ suffix == input) + require(ListUtils.isPrefix(p, input)) + require(ListUtils.isPrefix(pBis, input)) + require(pBis.size <= input.size) + require(pBis.size > p.size) + + require(ruleValid(r)) + require(validRegex(r.regex)) + require(matchR(r.regex, p)) + require(t.characters == p) + require({ + ListUtils.lemmaIsPrefixRefl(input, input) + maxPrefixOneRule(r, input) == Some(t, suffix) + }) + + ListUtils.lemmaIsPrefixRefl(input, input) + + longestMatchNoBiggerStringMatch(r.regex, input, p, pBis) + } ensuring (_ => !matchR(r.regex, pBis)) + + def lemmaMaxPrefOneRuleReturnsNoneThenNoPrefMaxRegex[C]( + r: Rule[C], + p: List[C], + input: List[C] + ): Unit = { + require(ListUtils.isPrefix(p, input)) + require(ruleValid(r)) + require(maxPrefixOneRule(r, input) == None[(Token[C], List[C])]()) + + longestMatchNoBiggerStringMatch(r.regex, input, Nil(), p) + + } ensuring (_ => !matchR(r.regex, p)) + + def lemmaRuleInListAndRulesValidThenRuleIsValid[C](r: Rule[C], rules: List[Rule[C]]): Unit = { + require(rules.contains(r)) + require(rulesValid(rules)) + rules match { + case Cons(hd, tl) if (hd == r) => assert(ruleValid(r)) + case Cons(hd, tl) if (hd != r) => { + assert(tl.contains(r)) + lemmaRuleInListAndRulesValidThenRuleIsValid(r, tl) + } + case Nil() => assert(false) + } + } ensuring (_ => ruleValid(r)) + + def lemmaInvariantOnRulesThenOnTail[C](r: Rule[C], rules: List[Rule[C]]): Unit = { + require(rulesInvariant(Cons(r, rules))) + assert(rulesValid(Cons(r, rules)) && noDuplicateTag(Cons(r, rules), Nil())) + assert(rulesValid(rules)) + assert(noDuplicateTag(rules, List(r.tag))) + + lemmaNoDupTagThenAlsoWithSubListAcc(List(r.tag), Nil(), rules) + assert(noDuplicateTag(rules, Nil())) + + } ensuring (_ => rulesInvariant(rules)) + + def lemmaNoDuplicateCanReorder[C](e1: Rule[C], e2: Rule[C], l: List[Rule[C]]): Unit = { + require(noDuplicateTag(Cons(e1, Cons(e2, l)), List())) + + assert(noDuplicateTag(Cons(e1, Cons(e2, l)), List()) == noDuplicateTag(Cons(e2, l), List(e1.tag))) + assert(noDuplicateTag(Cons(e2, l), List(e1.tag)) == noDuplicateTag(l, List(e2.tag, e1.tag))) + assert(List(e2.tag, e1.tag).toSet == List(e1.tag, e2.tag).toSet) + lemmaNoDuplicateSameWithAccWithSameContent(l, List(e2.tag, e1.tag), List(e1.tag, e2.tag)) + assert(noDuplicateTag(l, List(e2.tag, e1.tag)) == noDuplicateTag(l, List(e1.tag, e2.tag))) // TODO + } ensuring (_ => noDuplicateTag(Cons(e2, Cons(e1, l)), List())) + + def lemmaNoDuplicateSameWithAccWithSameContent[C](l: List[Rule[C]], acc: List[String], newAcc: List[String]): Unit = { + require(noDuplicateTag(l, acc)) + require(acc.content == newAcc.content) + decreases(l) + + l match { + case Cons(hd, tl) => { + ListSpecs.subsetContains(acc, newAcc) + ListSpecs.subsetContains(newAcc, acc) + assert(acc.contains(hd.tag) == newAcc.contains(hd.tag)) + lemmaNoDuplicateSameWithAccWithSameContent(tl, Cons(hd.tag, acc), Cons(hd.tag, newAcc)) + } + case Nil() => () + } + + } ensuring (_ => noDuplicateTag(l, newAcc)) + + def lemmaNoDupTagThenAlsoWithSubListAcc[C](acc: List[String], newAcc: List[String], rules: List[Rule[C]]): Unit = { + require(ListSpecs.subseq(newAcc, acc)) + require(noDuplicateTag(rules, acc)) + + rules match { + case Cons(hd, tl) => { + lemmaNoDupTagThenAlsoWithSubListAcc(Cons(hd.tag, acc), Cons(hd.tag, newAcc), tl) + ListSpecs.subseqNotContains(newAcc, acc, hd.tag) + } + case Nil() => () + } + + } ensuring (_ => noDuplicateTag(rules, newAcc)) + + def lemmaNoDuplicateTagAndDiffIndexThenNoTwoRulesEq[C](rules: List[Rule[C]], r1: Rule[C], r2: Rule[C]): Unit = { + require(rules.contains(r1)) + require(rules.contains(r2)) + require(noDuplicateTag(rules)) + require(ListUtils.getIndex(rules, r1) < ListUtils.getIndex(rules, r2)) + + } ensuring (_ => r1 != r2) + + def lemmaNoDuplicateTagAndDiffIndexThenNoTwoRulesTagsEq[C](rules: List[Rule[C]], r1: Rule[C], r2: Rule[C]): Unit = { + require(rules.contains(r1)) + require(rules.contains(r2)) + require(noDuplicateTag(rules)) + require(ListUtils.getIndex(rules, r1) < ListUtils.getIndex(rules, r2)) + decreases(rules) + + if (rules.head == r1) { + lemmaNoDuplicateAndTagInAccThenRuleCannotHaveSame(rules.tail, r2, r1.tag, List(r1.tag)) + assert(noDuplicateTag(rules) == noDuplicateTag(rules.tail, List(r1.tag))) + } else { + lemmaNoDupTagThenAlsoWithSubListAcc(List(rules.head.tag), Nil(), rules.tail) + ListUtils.lemmaGetIndexBiggerAndHeadNotEqThenTailContains(rules, r1, r2) + lemmaNoDuplicateTagAndDiffIndexThenNoTwoRulesTagsEq(rules.tail, r1, r2) + } + + } ensuring (_ => r1.tag != r2.tag) + + def lemmaNoDuplicateAndTagInAccThenRuleCannotHaveSame[C](rules: List[Rule[C]], r: Rule[C], tag: String, acc: List[String]): Unit = { + require(acc.contains(tag)) + require(noDuplicateTag(rules, acc)) + require(rules.contains(r)) + + rules match { + case Nil() => check(false) + case Cons(hd, tl) if hd == r => () + case Cons(hd, tl) if hd != r => lemmaNoDuplicateAndTagInAccThenRuleCannotHaveSame(tl, r, tag, Cons(hd.tag, acc)) + } + } ensuring (_ => r.tag != tag) + + def lemmaNonSepRuleNotContainsCharContainedInASepRule[C]( + rules: List[Rule[C]], + rulesRec: List[Rule[C]], + rNSep: Rule[C], + rSep: Rule[C], + c: C + ): Unit = { + require(rulesInvariant(rules)) + require(rules.contains(rSep)) + require(rulesRec.contains(rNSep)) + require(rules.contains(rNSep)) + require(!rNSep.isSeparator) + require(rSep.isSeparator) + require(usedCharacters(rSep.regex).contains(c)) + require(sepAndNonSepRulesDisjointChars(rules, rulesRec)) + + rulesRec match { + case Cons(hd, tl) if hd == rNSep => lemmaNonSepRuleNotContainsCharContainedInASepRuleInner(rules, rNSep, rSep, c) + case Cons(hd, tl) => lemmaNonSepRuleNotContainsCharContainedInASepRule(rules, tl, rNSep, rSep, c) + case Nil() => () + } + + } ensuring (_ => !usedCharacters(rNSep.regex).contains(c)) + + def lemmaNonSepRuleNotContainsCharContainedInASepRuleInner[C](rules: List[Rule[C]], rNSep: Rule[C], rSep: Rule[C], c: C): Unit = { + require(rulesInvariant(rules)) + require(rules.contains(rSep)) + require(usedCharacters(rSep.regex).contains(c)) + require(!rNSep.isSeparator) + require(rSep.isSeparator) + require(ruleDisjointCharsFromAllFromOtherType(rNSep, rules)) + decreases(rules) + + rules match { + case Cons(hd, tl) if hd == rSep => + ListSpecs.forallContained(usedCharacters(rSep.regex), (x: C) => !usedCharacters(rNSep.regex).contains(x), c) + + case Cons(hd, tl) => { + lemmaInvariantOnRulesThenOnTail(hd, tl) + lemmaNonSepRuleNotContainsCharContainedInASepRuleInner(tl, rNSep, rSep, c) + } + case Nil() => () + } + + } ensuring (_ => !usedCharacters(rNSep.regex).contains(c)) + + def lemmaSepRuleNotContainsCharContainedInANonSepRule[C]( + rules: List[Rule[C]], + rulesRec: List[Rule[C]], + rNSep: Rule[C], + rSep: Rule[C], + c: C + ): Unit = { + require(rulesInvariant(rules)) + require(rules.contains(rSep)) + require(rulesRec.contains(rNSep)) + require(rules.contains(rNSep)) + require(!rNSep.isSeparator) + require(rSep.isSeparator) + require(usedCharacters(rNSep.regex).contains(c)) + require(sepAndNonSepRulesDisjointChars(rules, rulesRec)) + + rulesRec match { + case Cons(hd, tl) if hd == rNSep => lemmaSepRuleNotContainsCharContainedInANonSepRuleInner(rules, rNSep, rSep, c) + case Cons(hd, tl) => lemmaSepRuleNotContainsCharContainedInANonSepRule(rules, tl, rNSep, rSep, c) + case Nil() => () + } + + } ensuring (_ => !usedCharacters(rSep.regex).contains(c)) + + def lemmaSepRuleNotContainsCharContainedInANonSepRuleInner[C](rules: List[Rule[C]], rNSep: Rule[C], rSep: Rule[C], c: C): Unit = { + require(rulesInvariant(rules)) + require(rules.contains(rSep)) + require(usedCharacters(rNSep.regex).contains(c)) + require(!rNSep.isSeparator) + require(rSep.isSeparator) + require(ruleDisjointCharsFromAllFromOtherType(rNSep, rules)) + decreases(rules) + + rules match { + case Cons(hd, tl) if hd == rSep => + ListSpecs.forallContained(usedCharacters(rNSep.regex), (x: C) => !usedCharacters(rSep.regex).contains(x), c) + + case Cons(hd, tl) => { + lemmaInvariantOnRulesThenOnTail(hd, tl) + lemmaSepRuleNotContainsCharContainedInANonSepRuleInner(tl, rNSep, rSep, c) + } + case Nil() => () + } + + } ensuring (_ => !usedCharacters(rSep.regex).contains(c)) + + def lemmaRulesProduceEachTokenIndividuallyThenForAnyToken[C](rules: List[Rule[C]], tokens: List[Token[C]], t: Token[C]): Unit = { + require(!rules.isEmpty) + require(rulesInvariant(rules)) + require(tokens.contains(t)) + require(rulesProduceEachTokenIndividually(rules, tokens)) + decreases(tokens) + + tokens match { + case Cons(hd, tl) if hd == t => () + case Cons(hd, tl) => lemmaRulesProduceEachTokenIndividuallyThenForAnyToken(rules, tl, t) + case Nil() => () + } + } ensuring (_ => rulesProduceIndivualToken(rules, t)) + + } + +} diff --git a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedRegex.scala b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedRegex.scala new file mode 100644 index 00000000..7d827938 --- /dev/null +++ b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedRegex.scala @@ -0,0 +1,1165 @@ +/** Author: Samuel Chassot + */ + +import stainless.equations._ +import stainless.lang._ +import stainless.collection._ +import stainless.annotation._ +import stainless.proof._ +import scala.runtime.Statics +import stainless.lang.Heap.get + +trait IDGiver[C] { + def id(c: C): Long + @law def uniqueness(x: C, y: C) = if (id(x) == id(y)) then x == y else true + @law def smallEnough(x: C) = id(x) < Int.MaxValue && id(x) >= 0 +} + +object VerifiedRegex { + abstract sealed class Regex[C] {} + def validRegex[C](r: Regex[C]): Boolean = r match { + case ElementMatch(c) => true + case Star(r) => !nullable(r) && !isEmptyLang(r) && validRegex(r) + case Union(rOne, rTwo) => validRegex(rOne) && validRegex(rTwo) + case Concat(rOne, rTwo) => validRegex(rOne) && validRegex(rTwo) + case EmptyExpr() => true + case EmptyLang() => true + } + + def regexDepth[C](r: Regex[C]): BigInt = { + decreases(r) + r match { + case ElementMatch(c) => BigInt(1) + case Star(r) => BigInt(1) + regexDepth(r) + case Union(rOne, rTwo) => BigInt(1) + Utils.maxBigInt(regexDepth(rOne), regexDepth(rTwo)) + case Concat(rOne, rTwo) => BigInt(1) + Utils.maxBigInt(regexDepth(rOne), regexDepth(rTwo)) + case EmptyExpr() => BigInt(1) + case EmptyLang() => BigInt(1) + } + } ensuring (res => + res > 0 && (r match { + case Union(rOne, rTwo) => res > regexDepth(rOne) && res > regexDepth(rTwo) + case Concat(rOne, rTwo) => res > regexDepth(rOne) && res > regexDepth(rTwo) + case Star(r) => res > regexDepth(r) + case _ => res == BigInt(1) + }) + ) + def regexDepthLong[C](r: Regex[C]): Long = { + require(regexDepth(r) <= Long.MaxValue) + decreases(r) + r match { + case ElementMatch(c) => 1L + case Star(r) => 1L + regexDepthLong(r) + case Union(rOne, rTwo) => 1L + Utils.maxLong(regexDepthLong(rOne), regexDepthLong(rTwo)) + case Concat(rOne, rTwo) => 1L + Utils.maxLong(regexDepthLong(rOne), regexDepthLong(rTwo)) + case EmptyExpr() => 1L + case EmptyLang() => 1L + } + } ensuring (res => res > 0 && (r match { + case Union(rOne, rTwo) => res > regexDepthLong(rOne) && res > regexDepthLong(rTwo) + case Concat(rOne, rTwo) => res > regexDepthLong(rOne) && res > regexDepthLong(rTwo) + case Star(r) => res > regexDepthLong(r) + case _ => res == 1L + })) + + def getUniqueId[C](r: Regex[C])(implicit idC: IDGiver[C]): Long = { + decreases(r) + r match { + case ElementMatch(c) => + assert(idC.smallEnough(c)) + 2L * idC.id(c) + case Star(r) => 3L * getUniqueId(r) + case Union(rOne, rTwo) => 5L * (getUniqueId(rOne) + getUniqueId(rTwo)) + case Concat(rOne, rTwo) => 7L * (getUniqueId(rOne) + getUniqueId(rTwo)) + case EmptyExpr() => 11L + case EmptyLang() => 13L + } + } ensuring(res => res >= 0 && res <= Math.pow(11L, regexDepthLong(r))) + + case class ElementMatch[C](c: C) extends Regex[C] + case class Star[C](reg: Regex[C]) extends Regex[C] + case class Union[C](regOne: Regex[C], regTwo: Regex[C]) extends Regex[C] + case class Concat[C](regOne: Regex[C], regTwo: Regex[C]) extends Regex[C] + + /** Regex that accepts only the empty string: represents the language {""} + */ + case class EmptyExpr[C]() extends Regex[C] + + /** Regex that accepts nothing: represents the empty language + */ + case class EmptyLang[C]() extends Regex[C] + + def usedCharacters[C](r: Regex[C]): List[C] = { + r match { + case EmptyExpr() => Nil[C]() + case EmptyLang() => Nil[C]() + case ElementMatch(c) => List(c) + case Star(r) => usedCharacters(r) + case Union(rOne, rTwo) => usedCharacters(rOne) ++ usedCharacters(rTwo) + case Concat(rOne, rTwo) => usedCharacters(rOne) ++ usedCharacters(rTwo) + } + } + + def firstChars[C](r: Regex[C]): List[C] = { + r match { + case EmptyExpr() => Nil[C]() + case EmptyLang() => Nil[C]() + case ElementMatch(c) => List(c) + case Star(r) => firstChars(r) + case Union(rOne, rTwo) => firstChars(rOne) ++ firstChars(rTwo) + case Concat(rOne, rTwo) if nullable(rOne) => firstChars(rOne) ++ firstChars(rTwo) + case Concat(rOne, rTwo) if !nullable(rOne) => firstChars(rOne) + } + } + + def nullable[C](r: Regex[C]): Boolean = { + r match { + case EmptyExpr() => true + case EmptyLang() => false + case ElementMatch(c) => false + case Star(r) => true + case Union(rOne, rTwo) => nullable(rOne) || nullable(rTwo) + case Concat(rOne, rTwo) => nullable(rOne) && nullable(rTwo) + } + } + + @inline + def isEmptyExpr[C](r: Regex[C]): Boolean = { + r match { + case EmptyExpr() => true + case _ => false + } + } + @inline + def isEmptyLang[C](r: Regex[C]): Boolean = { + r match { + case EmptyLang() => true + case _ => false + } + } + @inline + def isElementMatch[C](r: Regex[C]): Boolean = { + r match { + case ElementMatch(_) => true + case _ => false + } + } + @inline + def elementMatchIsChar[C](r: Regex[C], c: C): Boolean = { + require(isElementMatch(r)) + r match { + case ElementMatch(cc) => c == cc + } + } + @inline + def isStar[C](r: Regex[C]): Boolean = { + r match { + case Star(_) => true + case _ => false + } + } + @inline + def isUnion[C](r: Regex[C]): Boolean = { + r match { + case Union(_, _) => true + case _ => false + } + } + @inline + def unionInnersEquals[C](r: Regex[C], r1: Regex[C], r2: Regex[C]): Boolean = { + require(isUnion(r)) + r match { + case Union(rOne, rTwo) => r1 == rOne && r2 == rTwo + } + } + + @inline + def isConcat[C](r: Regex[C]): Boolean = { + r match { + case Concat(_, _) => true + case _ => false + } + } +} + +object VerifiedRegexMatcher { + import VerifiedRegex._ + import ListUtils._ + + def derivativeStep[C](r: Regex[C], a: C): Regex[C] = { + require(validRegex(r)) + decreases(r) + val res: Regex[C] = r match { + case EmptyExpr() => EmptyLang() + case EmptyLang() => EmptyLang() + case ElementMatch(c) => if (a == c) EmptyExpr() else EmptyLang() + case Union(rOne, rTwo) => Union(derivativeStep(rOne, a), derivativeStep(rTwo, a)) + case Star(rInner) => Concat(derivativeStep(rInner, a), Star(rInner)) + case Concat(rOne, rTwo) => { + if (nullable(rOne)) Union(Concat(derivativeStep(rOne, a), rTwo), derivativeStep(rTwo, a)) + else Union(Concat(derivativeStep(rOne, a), rTwo), EmptyLang()) + } + } + res + } ensuring (res => validRegex(res)) + + def derivative[C](r: Regex[C], input: List[C]): Regex[C] = { + require(validRegex(r)) + input match { + case Cons(hd, tl) => derivative(derivativeStep(r, hd), tl) + case Nil() => r + } + } ensuring (res => validRegex(res)) + + def matchRMem[C](r: Regex[C], input: List[C])(implicit cache: Map[(Regex[C], List[C]), Boolean]): Boolean = { + require(validRegex(r)) + decreases(input.size) + if (input.isEmpty) nullable(r) else matchR(derivativeStep(r, input.head), input.tail) + } ensuring (res => matchR(r, input) == res) + + def matchR[C](r: Regex[C], input: List[C]): Boolean = { + require(validRegex(r)) + decreases(input.size) + if (input.isEmpty) nullable(r) else matchR(derivativeStep(r, input.head), input.tail) + } ensuring (res => + r match { + case EmptyExpr() => res == input.isEmpty + case EmptyLang() => !res + case ElementMatch(c) => + (res && !input.isEmpty && input.tail.isEmpty && input.head == c) || (!res && !(!input.isEmpty && input.tail.isEmpty && input.head == c)) + case _ => true + } + ) + + def matchRSpec[C](r: Regex[C], s: List[C]): Boolean = { + require(validRegex(r)) + decreases(s.size + regexDepth(r)) + r match { + case EmptyExpr() => s.isEmpty + case EmptyLang() => false + case ElementMatch(c) => s == List(c) + case Union(r1, r2) => matchRSpec(r1, s) || matchRSpec(r2, s) + case Star(rInner) => s.isEmpty || findConcatSeparation(rInner, Star(rInner), Nil(), s, s).isDefined + case Concat(r1, r2) => findConcatSeparation(r1, r2, Nil(), s, s).isDefined + } + } + + def mainMatchTheorem[C](r: Regex[C], s: List[C]): Unit = { + require(validRegex(r)) + decreases(s.size + regexDepth(r)) + r match { + case EmptyExpr() => () + case EmptyLang() => () + case ElementMatch(c) => () + case Union(r1, r2) => { + if (matchR(r, s)) { + lemmaRegexUnionAcceptsThenOneOfTheTwoAccepts(r1, r2, s) + mainMatchTheorem(r1, s) + mainMatchTheorem(r2, s) + } else { + if (matchR(r1, s)) { + lemmaRegexAcceptsStringThenUnionWithAnotherAcceptsToo(r1, r2, s) + check(false) + } + if (matchR(r2, s)) { + lemmaRegexAcceptsStringThenUnionWithAnotherAcceptsToo(r2, r1, s) + lemmaReversedUnionAcceptsSameString(r2, r1, s) + check(false) + } + mainMatchTheorem(r1, s) + mainMatchTheorem(r2, s) + } + + } + case Star(rInner) => { + if (s.isEmpty) { + () + } else { + val cut = findConcatSeparation(rInner, Star(rInner), Nil(), s, s) + if (cut.isDefined) { + mainMatchTheorem(rInner, cut.get._1) + mainMatchTheorem(Star(rInner), cut.get._2) + if (!matchR(r, s)) { + lemmaFindSeparationIsDefinedThenConcatMatches(rInner, Star(rInner), cut.get._1, cut.get._2, s) + check(false) + } + } else { + if (matchR(r, s)) { + lemmaStarAppConcat(rInner, s) + lemmaConcatAcceptsStringThenFindSeparationIsDefined(rInner, Star(rInner), s) + check(false) + } + } + } + } + case Concat(r1, r2) => { + if (matchR(r, s)) { + lemmaConcatAcceptsStringThenFindSeparationIsDefined(r1, r2, s) + } else { + val cut = findConcatSeparation(r1, r2, Nil(), s, s) + if (cut.isDefined) { + lemmaFindSeparationIsDefinedThenConcatMatches(r1, r2, cut.get._1, cut.get._2, s) + check(false) + } + } + + } + } + } ensuring (matchR(r, s) == matchRSpec(r, s)) + + /** Enumerate all cuts in s and returns one that works, i.e., r1 matches s1 and r2 matches s2 Specifically, it is the right most one, i.e., s2 is the largest, if multiple exists Returns None is no valid cut + * exists + * + * @param r1 + * @param r2 + * @param s1 + * @param s2 + * @param s + */ + def findConcatSeparation[C](r1: Regex[C], r2: Regex[C], s1: List[C], s2: List[C], s: List[C]): Option[(List[C], List[C])] = { + require(validRegex(r1)) + require(validRegex(r2)) + require(s1 ++ s2 == s) + decreases(s2.size) + + val res: Option[(List[C], List[C])] = (s1, s2) match { + case (_, _) if matchR(r1, s1) && matchR(r2, s2) => Some((s1, s2)) + case (_, Nil()) => None() + case (_, Cons(hd2, tl2)) => { + lemmaMoveElementToOtherListKeepsConcatEq(s1, hd2, tl2, s) + assert(s1 ++ List(hd2) ++ tl2 == s) + findConcatSeparation(r1, r2, s1 ++ List(hd2), tl2, s) + } + } + res + + } ensuring (res => (res.isDefined && matchR(r1, res.get._1) && matchR(r2, res.get._2) && res.get._1 ++ res.get._2 == s) || !res.isDefined) + + def findLongestMatch[C](r: Regex[C], input: List[C]): (List[C], List[C]) = { + require(validRegex(r)) + findLongestMatchInner(r, Nil(), input) + } ensuring (res => res._1 ++ res._2 == input) + + def findLongestMatchInner[C](r: Regex[C], testedP: List[C], totalInput: List[C]): (List[C], List[C]) = { + require(validRegex(r)) + require(ListUtils.isPrefix(testedP, totalInput)) + decreases(totalInput.size - testedP.size) + + if (testedP == totalInput) { + if (nullable(r)) { + (testedP, Nil[C]()) + } else { + (Nil[C](), totalInput) + } + } else { + ListUtils.lemmaIsPrefixThenSmallerEqSize(testedP, totalInput) + if (testedP.size == totalInput.size) { + ListUtils.lemmaIsPrefixRefl(totalInput, totalInput) + ListUtils.lemmaIsPrefixSameLengthThenSameList(totalInput, testedP, totalInput) + check(false) + } + assert(testedP.size < totalInput.size) + val suffix = ListUtils.getSuffix(totalInput, testedP) + val newP = testedP ++ List(suffix.head) + lemmaAddHeadSuffixToPrefixStillPrefix(testedP, totalInput) + if (nullable(r)) { + val recursive = findLongestMatchInner(derivativeStep(r, suffix.head), newP, totalInput) + if (recursive._1.isEmpty) { + (testedP, ListUtils.getSuffix(totalInput, testedP)) + } else { + recursive + } + } else { + findLongestMatchInner(derivativeStep(r, suffix.head), newP, totalInput) + } + } + } ensuring (res => res._1 ++ res._2 == totalInput && (res._1.isEmpty || res._1.size >= testedP.size)) + + // Longest match theorems + def longestMatchIsAcceptedByMatchOrIsEmpty[C](r: Regex[C], input: List[C]): Unit = { + require(validRegex(r)) + longestMatchIsAcceptedByMatchOrIsEmptyRec(r, r, Nil(), input) + + } ensuring (findLongestMatchInner(r, Nil(), input)._1.isEmpty || matchR(r, findLongestMatchInner(r, Nil(), input)._1)) + + def longestMatchNoBiggerStringMatch[C](baseR: Regex[C], input: List[C], returnP: List[C], bigger: List[C]): Unit = { + require(validRegex(baseR)) + require(ListUtils.isPrefix(returnP, input)) + require(ListUtils.isPrefix(bigger, input)) + require(bigger.size >= returnP.size) + require(findLongestMatchInner(baseR, Nil(), input)._1 == returnP) + + if (bigger.size == returnP.size) { + ListUtils.lemmaIsPrefixSameLengthThenSameList(bigger, returnP, input) + } else { + if (matchR(baseR, bigger)) { + lemmaKnownAcceptedStringThenFromSmallPAtLeastThat(baseR, baseR, input, Nil(), bigger) + check(false) + } + } + + } ensuring (bigger == returnP || !matchR(baseR, bigger)) + + def lemmaIfMatchRThenLongestMatchFromThereReturnsAtLeastThis[C](baseR: Regex[C], r: Regex[C], input: List[C], testedP: List[C]): Unit = { + require(validRegex(baseR)) + require(validRegex(r)) + require(ListUtils.isPrefix(testedP, input)) + require(matchR(baseR, testedP)) + require(derivative(baseR, testedP) == r) + + lemmaMatchRIsSameAsWholeDerivativeAndNil(baseR, testedP) + assert(matchR(r, Nil())) + assert(nullable(r)) + + } ensuring (findLongestMatchInner(r, testedP, input)._1.size >= testedP.size) + + def lemmaKnownAcceptedStringThenFromSmallPAtLeastThat[C](baseR: Regex[C], r: Regex[C], input: List[C], testedP: List[C], knownP: List[C]): Unit = { + require(validRegex(baseR)) + require(validRegex(r)) + require(ListUtils.isPrefix(testedP, input)) + require(ListUtils.isPrefix(knownP, input)) + require(knownP.size >= testedP.size) + require(matchR(baseR, knownP)) + require(derivative(baseR, testedP) == r) + decreases(knownP.size - testedP.size) + + if (testedP.size == knownP.size) { + ListUtils.lemmaIsPrefixSameLengthThenSameList(testedP, knownP, input) + lemmaIfMatchRThenLongestMatchFromThereReturnsAtLeastThis(baseR, r, input, testedP) + check(findLongestMatchInner(r, testedP, input)._1.size >= knownP.size) + } else { + assert(testedP.size < input.size) + val suffix = ListUtils.getSuffix(input, testedP) + val newP = testedP ++ List(suffix.head) + lemmaAddHeadSuffixToPrefixStillPrefix(testedP, input) + + lemmaDerivativeOnLWithANewCharIsANewDerivativeStep(baseR, r, testedP, suffix.head) + lemmaKnownAcceptedStringThenFromSmallPAtLeastThat(baseR, derivativeStep(r, suffix.head), input, newP, knownP) + + check(findLongestMatchInner(r, testedP, input)._1.size >= knownP.size) + } + + } ensuring (findLongestMatchInner(r, testedP, input)._1.size >= knownP.size) + + def longestMatchIsAcceptedByMatchOrIsEmptyRec[C](baseR: Regex[C], r: Regex[C], testedP: List[C], input: List[C]): Unit = { + require(validRegex(baseR)) + require(ListUtils.isPrefix(testedP, input)) + require(derivative(baseR, testedP) == r) + decreases(input.size - testedP.size) + + if (findLongestMatchInner(r, testedP, input)._1.isEmpty) { + () + } else { + if (testedP == input) { + if (nullable(r)) { + lemmaMatchRIsSameAsWholeDerivativeAndNil(baseR, testedP) + } else { + () + } + } else { + ListUtils.lemmaIsPrefixThenSmallerEqSize(testedP, input) + if (testedP.size == input.size) { + ListUtils.lemmaIsPrefixRefl(input, input) + ListUtils.lemmaIsPrefixSameLengthThenSameList(input, testedP, input) + check(false) + } + assert(testedP.size < input.size) + val suffix = ListUtils.getSuffix(input, testedP) + val newP = testedP ++ List(suffix.head) + lemmaAddHeadSuffixToPrefixStillPrefix(testedP, input) + if (nullable(r)) { + val recursive = findLongestMatchInner(derivativeStep(r, suffix.head), newP, input) + if (recursive._1.isEmpty) { + lemmaMatchRIsSameAsWholeDerivativeAndNil(baseR, testedP) + } else { + lemmaDerivativeOnLWithANewCharIsANewDerivativeStep(baseR, r, testedP, suffix.head) + longestMatchIsAcceptedByMatchOrIsEmptyRec(baseR, derivativeStep(r, suffix.head), newP, input) + } + } else { + lemmaDerivativeOnLWithANewCharIsANewDerivativeStep(baseR, r, testedP, suffix.head) + longestMatchIsAcceptedByMatchOrIsEmptyRec(baseR, derivativeStep(r, suffix.head), newP, input) + } + } + } + + } ensuring (findLongestMatchInner(r, testedP, input)._1.isEmpty || matchR(baseR, findLongestMatchInner(r, testedP, input)._1)) + + def lemmaMatchRIsSameAsWholeDerivativeAndNil[C](r: Regex[C], input: List[C]): Unit = { + require(validRegex(r)) + input match { + case Cons(hd, tl) => lemmaMatchRIsSameAsWholeDerivativeAndNil(derivativeStep(r, hd), tl) + case Nil() => () + } + } ensuring (matchR(r, input) == matchR(derivative(r, input), Nil())) + + def lemmaDerivativeOnLWithANewCharIsANewDerivativeStep[C](baseR: Regex[C], r: Regex[C], input: List[C], c: C): Unit = { + require(validRegex(baseR)) + require(derivative(baseR, input) == r) + + input match { + case Cons(hd, tl) => lemmaDerivativeOnLWithANewCharIsANewDerivativeStep(derivativeStep(baseR, hd), r, tl, c) + case Nil() => () + } + + } ensuring (derivative(baseR, input ++ List(c)) == derivativeStep(r, c)) + + // Basic lemmas + def lemmaIfAcceptEmptyStringThenNullable[C](r: Regex[C], s: List[C]): Unit = { + require(validRegex(r)) + require(s.isEmpty) + require(matchR(r, s)) + } ensuring (nullable(r)) + + def lemmaRegexAcceptsStringThenDerivativeAcceptsTail[C](r: Regex[C], s: List[C]): Unit = { + require(validRegex(r)) + require(matchR(r, s)) + + } ensuring (if (s.isEmpty) nullable(r) else matchR(derivativeStep(r, s.head), s.tail)) + + // EmptyString Lemma + def lemmaRegexEmptyStringAcceptsTheEmptyString[C](r: EmptyExpr[C]): Unit = { + require(validRegex(r)) + } ensuring (matchR(r, List())) + + // Single Character Lemma + def lemmaElementRegexAcceptsItsCharacterAndOnlyIt[C]( + r: ElementMatch[C], + c: C, + d: C + ): Unit = { + require(validRegex(r) && r == ElementMatch(c)) + require(c != d) + } ensuring (matchR(r, List(c)) && !matchR(r, List(d))) + + def lemmaElementRegexDoesNotAcceptMultipleCharactersString[C]( + r: ElementMatch[C], + c: C, + s: List[C] + ): Unit = { + require(validRegex(r) && r == ElementMatch(c)) + require(!s.isEmpty) + } ensuring (!matchR(r, Cons(c, s))) + + // Union lemmas + def lemmaRegexAcceptsStringThenUnionWithAnotherAcceptsToo[C]( + r1: Regex[C], + r2: Regex[C], + s: List[C] + ): Unit = { + require(validRegex(r1) && validRegex(r2)) + require(matchR(r1, s)) + + s match { + case Cons(hd, tl) => { + lemmaRegexAcceptsStringThenUnionWithAnotherAcceptsToo(derivativeStep(r1, hd), derivativeStep(r2, hd), tl) + assert(matchR(Union(r1, r2), s)) + } + case Nil() => assert(matchR(Union(r1, r2), s)) + } + } ensuring (matchR(Union(r1, r2), s)) + + def lemmaRegexUnionAcceptsThenOneOfTheTwoAccepts[C](r1: Regex[C], r2: Regex[C], s: List[C]): Unit = { + require(validRegex(r1) && validRegex(r2)) + require(matchR(Union(r1, r2), s)) + + s match { + case Cons(hd, tl) => { + lemmaRegexUnionAcceptsThenOneOfTheTwoAccepts(derivativeStep(r1, hd), derivativeStep(r2, hd), tl) + } + case Nil() => + } + } ensuring (matchR(r1, s) || matchR(r2, s)) + + def lemmaReversedUnionAcceptsSameString[C]( + r1: Regex[C], + r2: Regex[C], + s: List[C] + ): Unit = { + require(validRegex(r1) && validRegex(r2)) + require(matchR(Union(r1, r2), s)) + + s match { + case Cons(hd, tl) => { + lemmaReversedUnionAcceptsSameString(derivativeStep(r1, hd), derivativeStep(r2, hd), tl) + assert(matchR(Union(r2, r1), s)) + } + case Nil() => assert(matchR(Union(r1, r2), s)) + } + } ensuring (matchR(Union(r2, r1), s)) + + // Concat lemmas + + def lemmaRegexConcatWithNullableAcceptsSameStr[C]( + r1: Regex[C], + r2: Regex[C], + s: List[C] + ): Unit = { + require(validRegex(r1)) + require(validRegex(r2)) + require(matchR(r1, s)) + require(nullable(r2)) + + val newR = Concat(r2, r1) + + s match { + case Cons(hd, tl) => { + assert(nullable(r2)) + assert( + derivativeStep(newR, hd) == Union(Concat(derivativeStep(r2, hd), r1), derivativeStep(r1, hd)) + ) + lemmaRegexAcceptsStringThenDerivativeAcceptsTail(r1, s) + lemmaRegexAcceptsStringThenUnionWithAnotherAcceptsToo( + derivativeStep(r1, hd), + Concat(derivativeStep(r2, hd), r1), + tl + ) + lemmaReversedUnionAcceptsSameString(derivativeStep(r1, hd), Concat(derivativeStep(r2, hd), r1), tl) + } + case Nil() => () + } + } ensuring (matchR(Concat(r2, r1), s)) + + def lemmaTwoRegexMatchThenConcatMatchesConcatString[C]( + r1: Regex[C], + r2: Regex[C], + s1: List[C], + s2: List[C] + ): Unit = { + require(validRegex(r1) && validRegex(r2)) + require(matchR(r1, s1)) + require(matchR(r2, s2)) + decreases(s1) + + s1 match { + case Cons(hd, tl) => { + lemmaTwoRegexMatchThenConcatMatchesConcatString(derivativeStep(r1, hd), r2, tl, s2) + assert(matchR(Concat(derivativeStep(r1, hd), r2), tl ++ s2)) + if (nullable(r1)) { + assert( + derivativeStep(Concat(r1, r2), hd) == Union(Concat(derivativeStep(r1, hd), r2), derivativeStep(r2, hd)) + ) + lemmaRegexAcceptsStringThenUnionWithAnotherAcceptsToo( + Concat(derivativeStep(r1, hd), r2), + derivativeStep(r2, hd), + tl ++ s2 + ) + } else { + assert(derivativeStep(Concat(r1, r2), hd) == Union(Concat(derivativeStep(r1, hd), r2), EmptyLang())) + lemmaRegexAcceptsStringThenUnionWithAnotherAcceptsToo( + Concat(derivativeStep(r1, hd), r2), + EmptyLang(), + tl ++ s2 + ) + assert(matchR(Concat(r1, r2), s1 ++ s2)) + } + } + case Nil() => + lemmaRegexConcatWithNullableAcceptsSameStr(r2, r1, s2) + + } + } ensuring (matchR(Concat(r1, r2), s1 ++ s2)) + + def lemmaFindSeparationIsDefinedThenConcatMatches[C](r1: Regex[C], r2: Regex[C], s1: List[C], s2: List[C], s: List[C]): Unit = { + require(validRegex(r1)) + require(validRegex(r2)) + require(s == s1 ++ s2) + require(findConcatSeparation(r1, r2, Nil(), s, s).isDefined) + require(findConcatSeparation(r1, r2, Nil(), s, s).get == (s1, s2)) + + lemmaTwoRegexMatchThenConcatMatchesConcatString(r1, r2, s1, s2) + + } ensuring (matchR(Concat(r1, r2), s1 ++ s2)) + + def lemmaR1MatchesS1AndR2MatchesS2ThenFindSeparationFindsAtLeastThem[C]( + r1: Regex[C], + r2: Regex[C], + s1: List[C], + s2: List[C], + s: List[C], + s1Rec: List[C], + s2Rec: List[C] + ): Unit = { + require(validRegex(r1)) + require(validRegex(r2)) + require(s1 ++ s2 == s) + require(ListUtils.isPrefix(s1Rec, s1)) + require(s1Rec ++ s2Rec == s) + require(matchR(r1, s1)) + require(matchR(r2, s2)) + decreases(s2Rec.size) + + (s1Rec, s2Rec) match { + case (_, _) if matchR(r1, s1Rec) && matchR(r2, s2Rec) => () + case (_, Nil()) => { + assert(s1Rec.size == s.size) + assert(s1Rec.size == s1.size) + assert(s1Rec == s1) + assert(s2Rec == s2) + assert(findConcatSeparation(r1, r2, s1Rec, s2Rec, s) == Some(s1Rec, s2Rec)) + } + case (_, Cons(hd2, tl2)) => { + ListUtils.lemmaConcatTwoListThenFirstIsPrefix(s1, s2) + ListUtils.lemmaConcatTwoListThenFirstIsPrefix(s1Rec, s2Rec) + if (s1Rec == s1) { + ListUtils.lemmaConcatTwoListThenFirstIsPrefix(s1, s2) + ListUtils.lemmaSamePrefixThenSameSuffix(s1, s2, s1Rec, s2Rec, s) + check(false) + } + lemmaMoveElementToOtherListKeepsConcatEq(s1Rec, hd2, tl2, s) + ListUtils.lemmaConcatTwoListThenFirstIsPrefix(s1Rec ++ List(hd2), tl2) + if (s1Rec.size == s1.size) { + ListUtils.lemmaIsPrefixSameLengthThenSameList(s1, s1Rec, s) + check(false) + } + + ListUtils.lemmaPrefixFromSameListAndStrictlySmallerThenPrefixFromEachOther(s1, s1Rec ++ List(hd2), s) + lemmaR1MatchesS1AndR2MatchesS2ThenFindSeparationFindsAtLeastThem(r1, r2, s1, s2, s, s1Rec ++ List(hd2), tl2) + } + } + + } ensuring (findConcatSeparation(r1, r2, s1Rec, s2Rec, s).isDefined) + + def lemmaConcatAcceptsStringThenFindSeparationIsDefined[C](r1: Regex[C], r2: Regex[C], s: List[C]): Unit = { + require(validRegex(r1)) + require(validRegex(r2)) + require(matchR(Concat(r1, r2), s)) + decreases(s) + + val r = Concat(r1, r2) + s match { + case Cons(hd, tl) => { + assert(matchR(derivativeStep(Concat(r1, r2), hd), tl)) + // if (nullable(rOne)) Union(Concat(derivativeStep(rOne, a), rTwo), derivativeStep(rTwo, a)) + // else Union(Concat(derivativeStep(rOne, a), rTwo), EmptyLang()) + if (nullable(r1)) { + assert(derivativeStep(r, hd) == Union(Concat(derivativeStep(r1, hd), r2), derivativeStep(r2, hd))) + lemmaRegexUnionAcceptsThenOneOfTheTwoAccepts(Concat(derivativeStep(r1, hd), r2), derivativeStep(r2, hd), tl) + if (matchR(Concat(derivativeStep(r1, hd), r2), tl)) { + lemmaConcatAcceptsStringThenFindSeparationIsDefined(derivativeStep(r1, hd), r2, tl) + assert(findConcatSeparation(derivativeStep(r1, hd), r2, Nil(), tl, tl).isDefined) + val (s1, s2) = findConcatSeparation(derivativeStep(r1, hd), r2, Nil(), tl, tl).get + lemmaR1MatchesS1AndR2MatchesS2ThenFindSeparationFindsAtLeastThem(r1, r2, Cons(hd, s1), s2, s, Nil(), s) + } else { + assert(matchR(derivativeStep(r2, hd), tl)) + } + } else { + assert(derivativeStep(r, hd) == Union(Concat(derivativeStep(r1, hd), r2), EmptyLang())) + lemmaRegexUnionAcceptsThenOneOfTheTwoAccepts(Concat(derivativeStep(r1, hd), r2), EmptyLang(), tl) + lemmaEmptyLangDerivativeIsAFixPoint(EmptyLang(), tl) + assert(matchR(Concat(derivativeStep(r1, hd), r2), tl)) + lemmaConcatAcceptsStringThenFindSeparationIsDefined(derivativeStep(r1, hd), r2, tl) + val (s1, s2) = findConcatSeparation(derivativeStep(r1, hd), r2, Nil(), tl, tl).get + lemmaR1MatchesS1AndR2MatchesS2ThenFindSeparationFindsAtLeastThem(r1, r2, Cons(hd, s1), s2, s, Nil(), s) + } + } + case Nil() => { + assert(nullable(r1) && nullable(r2)) + assert(findConcatSeparation(r1, r2, Nil(), Nil(), Nil()) == Some((Nil[C](), Nil[C]()))) + } + } + + } ensuring (findConcatSeparation(r1, r2, Nil(), s, s).isDefined) + + // Star lemmas + def lemmaStarAcceptsEmptyString[C](r: Star[C]): Unit = { + require(validRegex(r)) + } ensuring (matchR(r, List())) + + def lemmaStarApp[C](r: Regex[C], s1: List[C], s2: List[C]): Unit = { + require(validRegex(Star(r))) + require(matchR(r, s1)) + require(matchR(Star(r), s2)) + + s1 match { + case Cons(hd, tl) => { + assert(derivativeStep(Star(r), hd) == Concat(derivativeStep(r, hd), Star(r))) + lemmaTwoRegexMatchThenConcatMatchesConcatString(derivativeStep(r, hd), Star(r), tl, s2) + } + case Nil() => () + } + } ensuring (matchR(Star(r), s1 ++ s2)) + + def lemmaStarAppConcat[C](r: Regex[C], s: List[C]): Unit = { + require(validRegex(Star(r))) + require(matchR(Star(r), s)) + + s match { + case Cons(hd, tl) => { + assert(derivativeStep(Star(r), hd) == Concat(derivativeStep(r, hd), Star(r))) + val r1 = derivativeStep(r, hd) + val r2 = Star(r) + lemmaConcatAcceptsStringThenFindSeparationIsDefined(r1, r2, tl) + val cut = findConcatSeparation(r1, r2, Nil(), tl, tl) + lemmaTwoRegexMatchThenConcatMatchesConcatString(r, Star(r), Cons(hd, cut.get._1), cut.get._2) + } + case Nil() => () + } + } ensuring (s.isEmpty || matchR(Concat(r, Star(r)), s)) + + // usedCharacters lemmas --------------------------------------------------------------------------------------------------- + + def lemmaRegexCannotMatchAStringContainingACharItDoesNotContain[C](r: Regex[C], s: List[C], c: C): Unit = { + require(validRegex(r)) + require(s.contains(c)) + require(!usedCharacters(r).contains(c)) + decreases(s) + + s match { + case Cons(hd, tl) if hd == c => lemmaRegexCannotMatchAStringStartingWithACharItDoesNotContain(r, s, c) + case Cons(hd, tl) if hd != c => { + lemmaDerivativeStepDoesNotAddCharToUsedCharacters(r, hd, c) + lemmaRegexCannotMatchAStringContainingACharItDoesNotContain(derivativeStep(r, hd), tl, c) + } + case Nil() => check(false) + } + + } ensuring (!matchR(r, s)) + + def lemmaRegexCannotMatchAStringStartingWithACharItDoesNotContain[C](r: Regex[C], s: List[C], c: C): Unit = { + require(validRegex(r)) + require(s.contains(c)) + require(s.head == c) + require(!usedCharacters(r).contains(c)) + + if (matchR(r, s)) { + lemmaMatchRIsSameAsWholeDerivativeAndNil(r, s) + lemmaDerivativeAfterDerivativeStepIsNullableThenUsedCharsContainsHead(r, c, s.tail) + check(false) + } + + } ensuring (!matchR(r, s)) + + def lemmaRegexCannotMatchAStringStartingWithACharWhichIsNotInFirstChars[C](r: Regex[C], s: List[C], c: C): Unit = { + require(validRegex(r)) + require(s.contains(c)) + require(s.head == c) + require(!firstChars(r).contains(c)) + + if (matchR(r, s)) { + lemmaMatchRIsSameAsWholeDerivativeAndNil(r, s) + lemmaDerivAfterDerivStepIsNullableThenFirstCharsContainsHead(r, c, s.tail) + check(false) + } + + } ensuring (!matchR(r, s)) + + // not used + def lemmaRIsNotNullableDerivativeStepIsThenUsedCharContainsC[C](r: Regex[C], c: C): Unit = { + require(validRegex(r)) + require(!nullable(r)) + require(nullable(derivativeStep(r, c))) + decreases(r) + + r match { + case EmptyExpr() => check(false) + case EmptyLang() => () + case ElementMatch(a) => () + case Union(rOne, rTwo) => { + if (nullable(rOne)) { + check(false) + } + if (nullable(rTwo)) { + check(false) + } + if (nullable(derivativeStep(rOne, c))) { + lemmaRIsNotNullableDerivativeStepIsThenUsedCharContainsC(rOne, c) + } else { + assert(nullable(derivativeStep(rTwo, c))) + lemmaRIsNotNullableDerivativeStepIsThenUsedCharContainsC(rTwo, c) + } + } + case Star(rInner) => () + case Concat(rOne, rTwo) => { + if (nullable(rOne)) { + if (nullable(Concat(derivativeStep(rOne, c), rTwo))) { + lemmaRIsNotNullableDerivativeStepIsThenUsedCharContainsC(rOne, c) + } else { + assert(nullable(derivativeStep(rTwo, c))) + lemmaRIsNotNullableDerivativeStepIsThenUsedCharContainsC(rTwo, c) + + } + } else { + lemmaRIsNotNullableDerivativeStepIsThenUsedCharContainsC(rOne, c) + } + } + } + + } ensuring (usedCharacters(r).contains(c)) + + // DONE + def lemmaDerivativeAfterDerivativeStepIsNullableThenUsedCharsContainsHead[C](r: Regex[C], c: C, tl: List[C]): Unit = { + require(validRegex(r)) + require(nullable(derivative(derivativeStep(r, c), tl))) + decreases(r) + + r match { + case EmptyExpr() => { + assert(derivativeStep(r, c) == EmptyLang[C]()) + lemmaEmptyLangDerivativeIsAFixPoint(derivativeStep(r, c), tl) + check(false) + } + case EmptyLang() => { + assert(derivativeStep(r, c) == EmptyLang[C]()) + lemmaEmptyLangDerivativeIsAFixPoint(derivativeStep(r, c), tl) + check(false) + } + case ElementMatch(a) => { + if (c == a) { + assert(derivativeStep(r, c) == EmptyExpr[C]()) + if (tl.isEmpty) { + assert(usedCharacters(r).contains(c)) + assert(nullable(derivative(derivativeStep(r, c), tl))) + } else { + lemmaEmptyLangDerivativeIsAFixPoint(derivativeStep(derivativeStep(r, c), tl.head), tl.tail) + check(false) + } + } else { + assert(derivativeStep(r, c) == EmptyLang[C]()) + lemmaEmptyLangDerivativeIsAFixPoint(derivativeStep(r, c), tl) + check(false) + } + } + case Union(rOne, rTwo) => { + if (nullable(derivative(derivativeStep(rOne, c), tl))) { + lemmaDerivativeAfterDerivativeStepIsNullableThenUsedCharsContainsHead(rOne, c, tl) + } else if (nullable(derivative(derivativeStep(rTwo, c), tl))) { + lemmaDerivativeAfterDerivativeStepIsNullableThenUsedCharsContainsHead(rTwo, c, tl) + } else { + lemmaMatchRIsSameAsWholeDerivativeAndNil(r, Cons(c, tl)) + lemmaMatchRIsSameAsWholeDerivativeAndNil(rOne, Cons(c, tl)) + lemmaMatchRIsSameAsWholeDerivativeAndNil(rTwo, Cons(c, tl)) + lemmaRegexUnionAcceptsThenOneOfTheTwoAccepts(rOne, rTwo, Cons(c, tl)) + check(false) + } + } + case Star(rInner) => { + assert(derivativeStep(r, c) == Concat(derivativeStep(rInner, c), Star(rInner))) + if (nullable(derivative(derivativeStep(rInner, c), tl))) { + lemmaDerivativeAfterDerivativeStepIsNullableThenUsedCharsContainsHead(rInner, c, tl) + } else { + lemmaMatchRIsSameAsWholeDerivativeAndNil(derivativeStep(r, c), tl) + assert(matchR(derivativeStep(r, c), tl)) + lemmaConcatAcceptsStringThenFindSeparationIsDefined(derivativeStep(rInner, c), Star(rInner), tl) + val (s1, s2) = findConcatSeparation(derivativeStep(rInner, c), Star(rInner), Nil(), tl, tl).get + assert(s1 ++ s2 == tl) + assert(matchR(Star(rInner), s2)) + + assert(matchR(rInner, Cons(c, s1))) + assert(matchR(derivativeStep(rInner, c), s1)) + lemmaMatchRIsSameAsWholeDerivativeAndNil(derivativeStep(rInner, c), s1) + lemmaDerivativeAfterDerivativeStepIsNullableThenUsedCharsContainsHead(rInner, c, s1) + } + } + case Concat(rOne, rTwo) => { + // if (nullable(rOne)) Union(Concat(derivativeStep(rOne, a), rTwo), derivativeStep(rTwo, a)) + // else Union(Concat(derivativeStep(rOne, a), rTwo), EmptyLang()) + + if (nullable(rOne)) { + lemmaMatchRIsSameAsWholeDerivativeAndNil(Union(Concat(derivativeStep(rOne, c), rTwo), derivativeStep(rTwo, c)), tl) + lemmaRegexUnionAcceptsThenOneOfTheTwoAccepts(Concat(derivativeStep(rOne, c), rTwo), derivativeStep(rTwo, c), tl) + if (matchR(Concat(derivativeStep(rOne, c), rTwo), tl)) { + + lemmaConcatAcceptsStringThenFindSeparationIsDefined(derivativeStep(rOne, c), rTwo, tl) + val (s1, s2) = findConcatSeparation(derivativeStep(rOne, c), rTwo, Nil(), tl, tl).get + assert(s1 ++ s2 == tl) + assert(matchR(derivativeStep(rOne, c), s1)) + assert(matchR(rTwo, s2)) + assert(matchR(rOne, Cons(c, s1))) + lemmaMatchRIsSameAsWholeDerivativeAndNil(derivativeStep(rOne, c), s1) + lemmaDerivativeAfterDerivativeStepIsNullableThenUsedCharsContainsHead(rOne, c, s1) + } else { + lemmaMatchRIsSameAsWholeDerivativeAndNil(derivativeStep(rTwo, c), tl) + lemmaDerivativeAfterDerivativeStepIsNullableThenUsedCharsContainsHead(rTwo, c, tl) + } + } else { + lemmaMatchRIsSameAsWholeDerivativeAndNil(Union(Concat(derivativeStep(rOne, c), rTwo), EmptyLang()), tl) + lemmaRegexUnionAcceptsThenOneOfTheTwoAccepts(Concat(derivativeStep(rOne, c), rTwo), EmptyLang(), tl) + lemmaEmptyLangDerivativeIsAFixPoint(EmptyLang(), tl) + assert(matchR(Concat(derivativeStep(rOne, c), rTwo), tl)) + lemmaConcatAcceptsStringThenFindSeparationIsDefined(derivativeStep(rOne, c), rTwo, tl) + val (s1, s2) = findConcatSeparation(derivativeStep(rOne, c), rTwo, Nil(), tl, tl).get + assert(s1 ++ s2 == tl) + assert(matchR(derivativeStep(rOne, c), s1)) + assert(matchR(rTwo, s2)) + assert(matchR(rOne, Cons(c, s1))) + lemmaMatchRIsSameAsWholeDerivativeAndNil(derivativeStep(rOne, c), s1) + lemmaDerivativeAfterDerivativeStepIsNullableThenUsedCharsContainsHead(rOne, c, s1) + + } + } + } + + } ensuring (usedCharacters(r).contains(c)) + + def lemmaDerivativeStepDoesNotAddCharToUsedCharacters[C](r: Regex[C], c: C, cNot: C): Unit = { + decreases(r) + require(validRegex(r)) + require(!usedCharacters(r).contains(cNot)) + + r match { + case EmptyExpr() => () + case EmptyLang() => () + case ElementMatch(c) => () + case Union(rOne, rTwo) => { + lemmaDerivativeStepDoesNotAddCharToUsedCharacters(rOne, c, cNot) + lemmaDerivativeStepDoesNotAddCharToUsedCharacters(rTwo, c, cNot) + lemmaConcatTwoListsWhichNotContainThenTotNotContain(usedCharacters(derivativeStep(rOne, c)), usedCharacters(derivativeStep(rTwo, c)), cNot) + } + case Star(rInner) => { + lemmaDerivativeStepDoesNotAddCharToUsedCharacters(rInner, c, cNot) + } + case Concat(rOne, rTwo) => { + if (nullable(rOne)) { + lemmaDerivativeStepDoesNotAddCharToUsedCharacters(rOne, c, cNot) + lemmaDerivativeStepDoesNotAddCharToUsedCharacters(rTwo, c, cNot) + lemmaConcatTwoListsWhichNotContainThenTotNotContain(usedCharacters(derivativeStep(rOne, c)), usedCharacters(derivativeStep(rTwo, c)), cNot) + } else { + lemmaDerivativeStepDoesNotAddCharToUsedCharacters(rOne, c, cNot) + } + } + } + + } ensuring (!usedCharacters(derivativeStep(r, c)).contains(cNot)) + + def lemmaEmptyLangDerivativeIsAFixPoint[C](r: Regex[C], s: List[C]): Unit = { + require(r == EmptyLang[C]()) + s match { + case Cons(hd, tl) => lemmaEmptyLangDerivativeIsAFixPoint(derivativeStep(r, hd), tl) + case Nil() => () + } + + } ensuring (derivative(r, s) == r) + + def lemmaUsedCharsContainsAllFirstChars[C](r: Regex[C], c: C): Unit = { + require(validRegex(r)) + require(firstChars(r).contains(c)) + decreases(r) + r match { + case EmptyExpr() => () + case EmptyLang() => () + case ElementMatch(c) => () + case Star(r) => lemmaUsedCharsContainsAllFirstChars(r, c) + case Union(rOne, rTwo) => + if (firstChars(rOne).contains(c)) { + lemmaUsedCharsContainsAllFirstChars(rOne, c) + } else { + lemmaUsedCharsContainsAllFirstChars(rTwo, c) + } + + case Concat(rOne, rTwo) if nullable(rOne) => + if (firstChars(rOne).contains(c)) { + lemmaUsedCharsContainsAllFirstChars(rOne, c) + } else { + lemmaUsedCharsContainsAllFirstChars(rTwo, c) + } + + case Concat(rOne, rTwo) if !nullable(rOne) => lemmaUsedCharsContainsAllFirstChars(rOne, c) + } + + } ensuring (usedCharacters(r).contains(c)) + + def lemmaDerivAfterDerivStepIsNullableThenFirstCharsContainsHead[C](r: Regex[C], c: C, tl: List[C]): Unit = { + require(validRegex(r)) + require(nullable(derivative(derivativeStep(r, c), tl))) + + r match { + case EmptyExpr() => { + assert(derivativeStep(r, c) == EmptyLang[C]()) + lemmaEmptyLangDerivativeIsAFixPoint(derivativeStep(r, c), tl) + check(false) + } + case EmptyLang() => { + assert(derivativeStep(r, c) == EmptyLang[C]()) + lemmaEmptyLangDerivativeIsAFixPoint(derivativeStep(r, c), tl) + check(false) + } + case ElementMatch(a) => { + if (c == a) { + assert(derivativeStep(r, c) == EmptyExpr[C]()) + if (tl.isEmpty) { + assert(firstChars(r).contains(c)) + assert(nullable(derivative(derivativeStep(r, c), tl))) + } else { + lemmaEmptyLangDerivativeIsAFixPoint(derivativeStep(derivativeStep(r, c), tl.head), tl.tail) + check(false) + } + } else { + assert(derivativeStep(r, c) == EmptyLang[C]()) + lemmaEmptyLangDerivativeIsAFixPoint(derivativeStep(r, c), tl) + check(false) + } + } + case Union(rOne, rTwo) => { + if (nullable(derivative(derivativeStep(rOne, c), tl))) { + lemmaDerivAfterDerivStepIsNullableThenFirstCharsContainsHead(rOne, c, tl) + } else if (nullable(derivative(derivativeStep(rTwo, c), tl))) { + lemmaDerivAfterDerivStepIsNullableThenFirstCharsContainsHead(rTwo, c, tl) + } else { + lemmaMatchRIsSameAsWholeDerivativeAndNil(r, Cons(c, tl)) + lemmaMatchRIsSameAsWholeDerivativeAndNil(rOne, Cons(c, tl)) + lemmaMatchRIsSameAsWholeDerivativeAndNil(rTwo, Cons(c, tl)) + lemmaRegexUnionAcceptsThenOneOfTheTwoAccepts(rOne, rTwo, Cons(c, tl)) + check(false) + } + } + case Star(rInner) => { + assert(derivativeStep(r, c) == Concat(derivativeStep(rInner, c), Star(rInner))) + if (nullable(derivative(derivativeStep(rInner, c), tl))) { + lemmaDerivAfterDerivStepIsNullableThenFirstCharsContainsHead(rInner, c, tl) + } else { + lemmaMatchRIsSameAsWholeDerivativeAndNil(derivativeStep(r, c), tl) + assert(matchR(derivativeStep(r, c), tl)) + lemmaConcatAcceptsStringThenFindSeparationIsDefined(derivativeStep(rInner, c), Star(rInner), tl) + val (s1, s2) = findConcatSeparation(derivativeStep(rInner, c), Star(rInner), Nil(), tl, tl).get + assert(s1 ++ s2 == tl) + assert(matchR(Star(rInner), s2)) + + assert(matchR(rInner, Cons(c, s1))) + assert(matchR(derivativeStep(rInner, c), s1)) + lemmaMatchRIsSameAsWholeDerivativeAndNil(derivativeStep(rInner, c), s1) + lemmaDerivAfterDerivStepIsNullableThenFirstCharsContainsHead(rInner, c, s1) + } + } + case Concat(rOne, rTwo) => { + if (nullable(rOne)) { + lemmaMatchRIsSameAsWholeDerivativeAndNil(Union(Concat(derivativeStep(rOne, c), rTwo), derivativeStep(rTwo, c)), tl) + lemmaRegexUnionAcceptsThenOneOfTheTwoAccepts(Concat(derivativeStep(rOne, c), rTwo), derivativeStep(rTwo, c), tl) + if (matchR(Concat(derivativeStep(rOne, c), rTwo), tl)) { + + lemmaConcatAcceptsStringThenFindSeparationIsDefined(derivativeStep(rOne, c), rTwo, tl) + val (s1, s2) = findConcatSeparation(derivativeStep(rOne, c), rTwo, Nil(), tl, tl).get + assert(s1 ++ s2 == tl) + assert(matchR(derivativeStep(rOne, c), s1)) + assert(matchR(rTwo, s2)) + assert(matchR(rOne, Cons(c, s1))) + lemmaMatchRIsSameAsWholeDerivativeAndNil(derivativeStep(rOne, c), s1) + lemmaDerivAfterDerivStepIsNullableThenFirstCharsContainsHead(rOne, c, s1) + } else { + lemmaMatchRIsSameAsWholeDerivativeAndNil(derivativeStep(rTwo, c), tl) + lemmaDerivAfterDerivStepIsNullableThenFirstCharsContainsHead(rTwo, c, tl) + } + } else { + lemmaMatchRIsSameAsWholeDerivativeAndNil(Union(Concat(derivativeStep(rOne, c), rTwo), EmptyLang()), tl) + lemmaRegexUnionAcceptsThenOneOfTheTwoAccepts(Concat(derivativeStep(rOne, c), rTwo), EmptyLang(), tl) + lemmaEmptyLangDerivativeIsAFixPoint(EmptyLang(), tl) + assert(matchR(Concat(derivativeStep(rOne, c), rTwo), tl)) + lemmaConcatAcceptsStringThenFindSeparationIsDefined(derivativeStep(rOne, c), rTwo, tl) + val (s1, s2) = findConcatSeparation(derivativeStep(rOne, c), rTwo, Nil(), tl, tl).get + assert(s1 ++ s2 == tl) + assert(matchR(derivativeStep(rOne, c), s1)) + assert(matchR(rTwo, s2)) + assert(matchR(rOne, Cons(c, s1))) + lemmaMatchRIsSameAsWholeDerivativeAndNil(derivativeStep(rOne, c), s1) + lemmaDerivAfterDerivStepIsNullableThenFirstCharsContainsHead(rOne, c, s1) + + } + } + } + + } ensuring (firstChars(r).contains(c)) +} + +object Utils { + def maxBigInt(a: BigInt, b: BigInt): BigInt = if (a >= b) a else b + def maxLong(a: Long, b: Long): Long = if (a >= b) a else b +} diff --git a/lexers/regex/verifiedlexer/stainless.conf b/lexers/regex/verifiedlexer/stainless.conf new file mode 100644 index 00000000..e8f43f0e --- /dev/null +++ b/lexers/regex/verifiedlexer/stainless.conf @@ -0,0 +1,15 @@ +# The settings below correspond to the various +# options listed by `stainless --help` + +vc-cache = true +# debug = ["verification", "smt"] +timeout = 10 +check-models = false +print-ids = false +print-types = false +batched = true +strict-arithmetic = true +solvers = "smt-cvc5,smt-z3,smt-cvc4" +check-measures = yes +infer-measures = true +simplifier = "bland" \ No newline at end of file diff --git a/lexers/regex/verifiedlexer/verify.sh b/lexers/regex/verifiedlexer/verify.sh new file mode 100755 index 00000000..511c4a3e --- /dev/null +++ b/lexers/regex/verifiedlexer/verify.sh @@ -0,0 +1 @@ +stainless-dotty --config-file=stainless.conf --watch src/main/scala/ch/epfl/lexer/VerifiedRegex.scala src/main/scala/ch/epfl/lexer/ListUtils.scala $1 \ No newline at end of file From 3cbe89bf18af1f78493691c0d6c5a7992a1f17cb Mon Sep 17 00:00:00 2001 From: Samuel Chassot Date: Mon, 5 Feb 2024 15:09:19 +0100 Subject: [PATCH 03/78] original version --- .../main/scala/ch/epfl/lexer/ListUtils.scala | 6 +- .../scala/ch/epfl/lexer/VerifiedRegex.scala | 70 ++++--------------- 2 files changed, 16 insertions(+), 60 deletions(-) diff --git a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/ListUtils.scala b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/ListUtils.scala index 1d5d816f..fc7a6fc3 100644 --- a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/ListUtils.scala +++ b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/ListUtils.scala @@ -20,6 +20,7 @@ object ListUtils { def removeLast[B](l: List[B]): List[B] = { require(!l.isEmpty) + decreases(l) val res: List[B] = l match { case Cons(hd, Nil()) => Nil() case Cons(hd, tl) => Cons(hd, removeLast(tl)) @@ -64,7 +65,7 @@ object ListUtils { } def consecutiveSubseqAtHead[B](l1: List[B], lTot: List[B]): Boolean = { - decreases(l1) + decreases(lTot) (l1, lTot) match { case (Nil(), _) => true case (Cons(hd1, tl1), Cons(hdTot, tlTot)) if hd1 == hdTot => consecutiveSubseqAtHead(tl1, tlTot) @@ -271,7 +272,7 @@ object ListUtils { require(isPrefix(p1, l)) require(isPrefix(p2, l)) require(p1.size == p2.size) - decreases(p1) + p1 match { case Cons(hd, tl) => lemmaIsPrefixSameLengthThenSameList(tl, p2.tail, l.tail) case Nil() => () @@ -384,6 +385,7 @@ object ListUtils { def concatWithoutDuplicates[B](baseList: List[B], newList: List[B]): List[B] = { require(ListOps.noDuplicate(baseList)) decreases(newList) + newList match { case Cons(hd, tl) if baseList.contains(hd) => concatWithoutDuplicates(baseList, tl) case Cons(hd, tl) if !baseList.contains(hd) => concatWithoutDuplicates(Cons(hd, baseList), tl) diff --git a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedRegex.scala b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedRegex.scala index 7d827938..805cb47d 100644 --- a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedRegex.scala +++ b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedRegex.scala @@ -7,13 +7,6 @@ import stainless.collection._ import stainless.annotation._ import stainless.proof._ import scala.runtime.Statics -import stainless.lang.Heap.get - -trait IDGiver[C] { - def id(c: C): Long - @law def uniqueness(x: C, y: C) = if (id(x) == id(y)) then x == y else true - @law def smallEnough(x: C) = id(x) < Int.MaxValue && id(x) >= 0 -} object VerifiedRegex { abstract sealed class Regex[C] {} @@ -26,17 +19,17 @@ object VerifiedRegex { case EmptyLang() => true } - def regexDepth[C](r: Regex[C]): BigInt = { + def regexDepth[C](r: Regex[C]): BigInt ={ decreases(r) - r match { - case ElementMatch(c) => BigInt(1) - case Star(r) => BigInt(1) + regexDepth(r) - case Union(rOne, rTwo) => BigInt(1) + Utils.maxBigInt(regexDepth(rOne), regexDepth(rTwo)) - case Concat(rOne, rTwo) => BigInt(1) + Utils.maxBigInt(regexDepth(rOne), regexDepth(rTwo)) - case EmptyExpr() => BigInt(1) - case EmptyLang() => BigInt(1) - } - } ensuring (res => + r match { + case ElementMatch(c) => BigInt(1) + case Star(r) => BigInt(1) + regexDepth(r) + case Union(rOne, rTwo) => BigInt(1) + Utils.maxBigInt(regexDepth(rOne), regexDepth(rTwo)) + case Concat(rOne, rTwo) => BigInt(1) + Utils.maxBigInt(regexDepth(rOne), regexDepth(rTwo)) + case EmptyExpr() => BigInt(1) + case EmptyLang() => BigInt(1) + } +} ensuring (res => res > 0 && (r match { case Union(rOne, rTwo) => res > regexDepth(rOne) && res > regexDepth(rTwo) case Concat(rOne, rTwo) => res > regexDepth(rOne) && res > regexDepth(rTwo) @@ -44,37 +37,6 @@ object VerifiedRegex { case _ => res == BigInt(1) }) ) - def regexDepthLong[C](r: Regex[C]): Long = { - require(regexDepth(r) <= Long.MaxValue) - decreases(r) - r match { - case ElementMatch(c) => 1L - case Star(r) => 1L + regexDepthLong(r) - case Union(rOne, rTwo) => 1L + Utils.maxLong(regexDepthLong(rOne), regexDepthLong(rTwo)) - case Concat(rOne, rTwo) => 1L + Utils.maxLong(regexDepthLong(rOne), regexDepthLong(rTwo)) - case EmptyExpr() => 1L - case EmptyLang() => 1L - } - } ensuring (res => res > 0 && (r match { - case Union(rOne, rTwo) => res > regexDepthLong(rOne) && res > regexDepthLong(rTwo) - case Concat(rOne, rTwo) => res > regexDepthLong(rOne) && res > regexDepthLong(rTwo) - case Star(r) => res > regexDepthLong(r) - case _ => res == 1L - })) - - def getUniqueId[C](r: Regex[C])(implicit idC: IDGiver[C]): Long = { - decreases(r) - r match { - case ElementMatch(c) => - assert(idC.smallEnough(c)) - 2L * idC.id(c) - case Star(r) => 3L * getUniqueId(r) - case Union(rOne, rTwo) => 5L * (getUniqueId(rOne) + getUniqueId(rTwo)) - case Concat(rOne, rTwo) => 7L * (getUniqueId(rOne) + getUniqueId(rTwo)) - case EmptyExpr() => 11L - case EmptyLang() => 13L - } - } ensuring(res => res >= 0 && res <= Math.pow(11L, regexDepthLong(r))) case class ElementMatch[C](c: C) extends Regex[C] case class Star[C](reg: Regex[C]) extends Regex[C] @@ -172,7 +134,7 @@ object VerifiedRegex { case Union(rOne, rTwo) => r1 == rOne && r2 == rTwo } } - + @inline def isConcat[C](r: Regex[C]): Boolean = { r match { @@ -211,12 +173,6 @@ object VerifiedRegexMatcher { } } ensuring (res => validRegex(res)) - def matchRMem[C](r: Regex[C], input: List[C])(implicit cache: Map[(Regex[C], List[C]), Boolean]): Boolean = { - require(validRegex(r)) - decreases(input.size) - if (input.isEmpty) nullable(r) else matchR(derivativeStep(r, input.head), input.tail) - } ensuring (res => matchR(r, input) == res) - def matchR[C](r: Regex[C], input: List[C]): Boolean = { require(validRegex(r)) decreases(input.size) @@ -307,8 +263,7 @@ object VerifiedRegexMatcher { } } ensuring (matchR(r, s) == matchRSpec(r, s)) - /** Enumerate all cuts in s and returns one that works, i.e., r1 matches s1 and r2 matches s2 Specifically, it is the right most one, i.e., s2 is the largest, if multiple exists Returns None is no valid cut - * exists + /** Enumerate all cuts in s and returns one that works, i.e., r1 matches s1 and r2 matches s2 Specifically, it is the right most one, i.e., s2 is the largest, if multiple exists Returns None is no valid cut exists * * @param r1 * @param r2 @@ -1161,5 +1116,4 @@ object VerifiedRegexMatcher { object Utils { def maxBigInt(a: BigInt, b: BigInt): BigInt = if (a >= b) a else b - def maxLong(a: Long, b: Long): Long = if (a >= b) a else b } From a6e8c552651ebe91ff0250950102003af18c1dc3 Mon Sep 17 00:00:00 2001 From: Samuel Chassot Date: Mon, 5 Feb 2024 15:11:10 +0100 Subject: [PATCH 04/78] working on the memoisation --- .../scala/ch/epfl/lexer/VerifiedRegex.scala | 78 ++++++++++++++++--- lexers/regex/verifiedlexer/stainless.conf | 2 +- 2 files changed, 67 insertions(+), 13 deletions(-) diff --git a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedRegex.scala b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedRegex.scala index 805cb47d..1343f98e 100644 --- a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedRegex.scala +++ b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedRegex.scala @@ -10,6 +10,10 @@ import scala.runtime.Statics object VerifiedRegex { abstract sealed class Regex[C] {} + val INT_MAX_VALUE: BigInt = 2147483647 + val INT_MAX_VALUE_L: Long = 2147483647L + // val LONG_MAX_VALUE: BigInt = 9223372036854775807L + def validRegex[C](r: Regex[C]): Boolean = r match { case ElementMatch(c) => true case Star(r) => !nullable(r) && !isEmptyLang(r) && validRegex(r) @@ -19,17 +23,17 @@ object VerifiedRegex { case EmptyLang() => true } - def regexDepth[C](r: Regex[C]): BigInt ={ + def regexDepth[C](r: Regex[C]): BigInt = { decreases(r) - r match { - case ElementMatch(c) => BigInt(1) - case Star(r) => BigInt(1) + regexDepth(r) - case Union(rOne, rTwo) => BigInt(1) + Utils.maxBigInt(regexDepth(rOne), regexDepth(rTwo)) - case Concat(rOne, rTwo) => BigInt(1) + Utils.maxBigInt(regexDepth(rOne), regexDepth(rTwo)) - case EmptyExpr() => BigInt(1) - case EmptyLang() => BigInt(1) - } -} ensuring (res => + r match { + case ElementMatch(c) => BigInt(1) + case Star(r) => BigInt(1) + regexDepth(r) + case Union(rOne, rTwo) => BigInt(1) + Utils.maxBigInt(regexDepth(rOne), regexDepth(rTwo)) + case Concat(rOne, rTwo) => BigInt(1) + Utils.maxBigInt(regexDepth(rOne), regexDepth(rTwo)) + case EmptyExpr() => BigInt(1) + case EmptyLang() => BigInt(1) + } + } ensuring (res => res > 0 && (r match { case Union(rOne, rTwo) => res > regexDepth(rOne) && res > regexDepth(rTwo) case Concat(rOne, rTwo) => res > regexDepth(rOne) && res > regexDepth(rTwo) @@ -37,6 +41,40 @@ object VerifiedRegex { case _ => res == BigInt(1) }) ) + def regexDepthLong[C](r: Regex[C]): Long = { + require(regexDepthLong(r) <= Long.MaxValue) + decreases(r) + r match { + case ElementMatch(c) => 1L + case Star(r) => 1L + regexDepthLong(r) + case Union(rOne, rTwo) => 1L + Utils.maxLong(regexDepthLong(rOne), regexDepthLong(rTwo)) + case Concat(rOne, rTwo) => 1L + Utils.maxLong(regexDepthLong(rOne), regexDepthLong(rTwo)) + case EmptyExpr() => 1L + case EmptyLang() => 1L + } + } ensuring (res => + res > 0 && (r match { + case Union(rOne, rTwo) => res > regexDepthLong(rOne) && res > regexDepthLong(rTwo) + case Concat(rOne, rTwo) => res > regexDepthLong(rOne) && res > regexDepthLong(rTwo) + case Star(r) => res > regexDepthLong(r) + case _ => res == 1L + }) + ) + + def getUniqueId[C](r: Regex[C])(implicit idC: IDGiver[C]): Long = { + require(regexDepth(r) <= 30) + decreases(r) + r match { + case ElementMatch(c) => + assert(idC.smallEnough(c)) + 2L * idC.id(c) + case Star(r) => 3L * getUniqueId(r) + case Union(rOne, rTwo) => 5L * (getUniqueId(rOne) + getUniqueId(rTwo)) + case Concat(rOne, rTwo) => 7L * (getUniqueId(rOne) + getUniqueId(rTwo)) + case EmptyExpr() => 11L + case EmptyLang() => 13L + } + } ensuring (res => res >= 0 && res <= Utils.power(7L, regexDepthLong(r)) * 26L * INT_MAX_VALUE_L * 4L) case class ElementMatch[C](c: C) extends Regex[C] case class Star[C](reg: Regex[C]) extends Regex[C] @@ -134,7 +172,7 @@ object VerifiedRegex { case Union(rOne, rTwo) => r1 == rOne && r2 == rTwo } } - + @inline def isConcat[C](r: Regex[C]): Boolean = { r match { @@ -263,7 +301,8 @@ object VerifiedRegexMatcher { } } ensuring (matchR(r, s) == matchRSpec(r, s)) - /** Enumerate all cuts in s and returns one that works, i.e., r1 matches s1 and r2 matches s2 Specifically, it is the right most one, i.e., s2 is the largest, if multiple exists Returns None is no valid cut exists + /** Enumerate all cuts in s and returns one that works, i.e., r1 matches s1 and r2 matches s2 Specifically, it is the right most one, i.e., s2 is the largest, if multiple exists Returns None is no valid cut + * exists * * @param r1 * @param r2 @@ -1116,4 +1155,19 @@ object VerifiedRegexMatcher { object Utils { def maxBigInt(a: BigInt, b: BigInt): BigInt = if (a >= b) a else b + def maxLong(a: Long, b: Long): Long = if (a >= b) a else b + def power(a: Long, exp: Long): Long = { + require(exp >= 0) + require(a >= 0) + decreases(exp) + if (exp == 0) 1 + else a * power(a, exp - 1) + } ensuring (res => res >= 0) + + def lemmaPowerMinusOne(a: Long, exp: Long, res: Long): Unit = { + require(exp > 0) + require(a >= 0) + require(res == power(a, exp)) + } ensuring (_ => power(a, exp - 1) < res) + >>>>>>> Stashed changes } diff --git a/lexers/regex/verifiedlexer/stainless.conf b/lexers/regex/verifiedlexer/stainless.conf index e8f43f0e..286d55ce 100644 --- a/lexers/regex/verifiedlexer/stainless.conf +++ b/lexers/regex/verifiedlexer/stainless.conf @@ -8,7 +8,7 @@ check-models = false print-ids = false print-types = false batched = true -strict-arithmetic = true +strict-arithmetic = false solvers = "smt-cvc5,smt-z3,smt-cvc4" check-measures = yes infer-measures = true From 8e070cb7083960ec901d350d7f5c98b233575efb Mon Sep 17 00:00:00 2001 From: Samuel Chassot Date: Mon, 5 Feb 2024 16:18:06 +0100 Subject: [PATCH 05/78] not working --- .../scala/ch/epfl/lexer/VerifiedRegex.scala | 27 +++++++------------ 1 file changed, 10 insertions(+), 17 deletions(-) diff --git a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedRegex.scala b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedRegex.scala index 1343f98e..89fa613e 100644 --- a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedRegex.scala +++ b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedRegex.scala @@ -8,11 +8,17 @@ import stainless.annotation._ import stainless.proof._ import scala.runtime.Statics +trait IDGiver[C] { + def id(c: C): Long + val MAX_ID = Int.MaxValue + @law def smallEnough(c: C): Boolean = id(c) >= 0 && id(c) <= MAX_ID + @law def uniqueness(c1: C, c2: C): Boolean = if(id(c1) == id(c2)) then c1 == c2 else true +} + object VerifiedRegex { abstract sealed class Regex[C] {} val INT_MAX_VALUE: BigInt = 2147483647 val INT_MAX_VALUE_L: Long = 2147483647L - // val LONG_MAX_VALUE: BigInt = 9223372036854775807L def validRegex[C](r: Regex[C]): Boolean = r match { case ElementMatch(c) => true @@ -41,8 +47,9 @@ object VerifiedRegex { case _ => res == BigInt(1) }) ) + def regexDepthLong[C](r: Regex[C]): Long = { - require(regexDepthLong(r) <= Long.MaxValue) + require(regexDepth(r) < INT_MAX_VALUE) decreases(r) r match { case ElementMatch(c) => 1L @@ -74,7 +81,7 @@ object VerifiedRegex { case EmptyExpr() => 11L case EmptyLang() => 13L } - } ensuring (res => res >= 0 && res <= Utils.power(7L, regexDepthLong(r)) * 26L * INT_MAX_VALUE_L * 4L) + } ensuring (res => res >= 0 && res < 12L) case class ElementMatch[C](c: C) extends Regex[C] case class Star[C](reg: Regex[C]) extends Regex[C] @@ -1156,18 +1163,4 @@ object VerifiedRegexMatcher { object Utils { def maxBigInt(a: BigInt, b: BigInt): BigInt = if (a >= b) a else b def maxLong(a: Long, b: Long): Long = if (a >= b) a else b - def power(a: Long, exp: Long): Long = { - require(exp >= 0) - require(a >= 0) - decreases(exp) - if (exp == 0) 1 - else a * power(a, exp - 1) - } ensuring (res => res >= 0) - - def lemmaPowerMinusOne(a: Long, exp: Long, res: Long): Unit = { - require(exp > 0) - require(a >= 0) - require(res == power(a, exp)) - } ensuring (_ => power(a, exp - 1) < res) - >>>>>>> Stashed changes } From 716df70a3959771c32c19faea193642c92848d16 Mon Sep 17 00:00:00 2001 From: Samuel Chassot Date: Mon, 5 Feb 2024 17:19:12 +0100 Subject: [PATCH 06/78] add longMap + work on cache --- .../scala/ch/epfl/chassot/ListLongMap.scala | 970 ++ .../ch/epfl/chassot/MutableLongMap.scala | 8407 +++++++++++++++++ .../main/scala/ch/epfl/lexer/ListUtils.scala | 1 + .../scala/ch/epfl/lexer/VerifiedLexer.scala | 2 + .../scala/ch/epfl/lexer/VerifiedRegex.scala | 163 +- .../src/main/scala/test.worksheet.sc | 178 + lexers/regex/verifiedlexer/verify.sh | 2 +- 7 files changed, 9657 insertions(+), 66 deletions(-) create mode 100644 lexers/regex/verifiedlexer/src/main/scala/ch/epfl/chassot/ListLongMap.scala create mode 100644 lexers/regex/verifiedlexer/src/main/scala/ch/epfl/chassot/MutableLongMap.scala create mode 100644 lexers/regex/verifiedlexer/src/main/scala/test.worksheet.sc diff --git a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/chassot/ListLongMap.scala b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/chassot/ListLongMap.scala new file mode 100644 index 00000000..2ab9ad17 --- /dev/null +++ b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/chassot/ListLongMap.scala @@ -0,0 +1,970 @@ +/** Author: Samuel Chassot + */ + +package ch.epfl.chassot + +import stainless.annotation._ +import stainless.collection._ +import stainless.equations._ +import stainless.lang._ +import stainless.proof.check +import scala.annotation.tailrec +import scala.collection.immutable + +// Uncomment the following import to run benchmarks +// import OptimisedChecks.* + +case class ListLongMap[B](toList: List[(Long, B)]) { + require(TupleListOps.isStrictlySorted(toList)) + + def isEmpty: Boolean = toList.isEmpty + + def head: (Long, B) = { + require(!isEmpty) + toList.head + } + + def size: Int = { + require(toList.size < Integer.MAX_VALUE) + TupleListOps.intSize(toList) + } + + def nKeys: Int = { + require(toList.size < Integer.MAX_VALUE) + TupleListOps.intSizeKeys(TupleListOps.getKeysList(toList)) + } + + def tail: ListLongMap[B] = { + require(!isEmpty) + ListLongMap(toList.tail) + } + + def contains(key: Long): Boolean = { + val res = TupleListOps.containsKey(toList, key) + if (res) { + TupleListOps.lemmaContainsKeyImpliesGetValueByKeyDefined(toList, key) + } + res + + }.ensuring(res => !res || this.get(key).isDefined) + + @inline + def get(key: Long): Option[B] = { + TupleListOps.getValueByKey(toList, key) + } + + @inline + def keysOf(value: B): List[Long] = { + TupleListOps.getKeysOf(toList, value) + } + + def keys(): List[Long] = { + TupleListOps.getKeysList(toList) + } + + def apply(key: Long): B = { + require(contains(key)) + get(key).get + } + + def +(keyValue: (Long, B)): ListLongMap[B] = { + + val newList = + TupleListOps.insertStrictlySorted(toList, keyValue._1, keyValue._2) + + TupleListOps.lemmaContainsTupThenGetReturnValue( + newList, + keyValue._1, + keyValue._2 + ) + ListLongMap(newList) + + }.ensuring(res => + res.contains(keyValue._1) && res.get(keyValue._1) == Some[B]( + keyValue._2 + ) && res.toList.contains(keyValue) + ) + + def ++(keyValues: List[(Long, B)]): ListLongMap[B] = { + decreases(keyValues) + keyValues match { + case Nil() => this + case Cons(keyValue, rest) => (this + keyValue) ++ rest + } + } + def -(key: Long): ListLongMap[B] = { + ListLongMap(TupleListOps.removeStrictlySorted(toList, key)) + }.ensuring(res => !res.contains(key)) + + def --(keys: List[Long]): ListLongMap[B] = { + decreases(keys) + keys match { + case Nil() => this + case Cons(key, rest) => (this - key) -- rest + } + } + @inline + def forall(p: ((Long, B)) => Boolean): Boolean = { + toList.forall(p) + } +} + +object TupleListOps { + + @inline + def invariantList[B](l: List[(Long, B)]): Boolean = { + isStrictlySorted(l) + } + + def getKeysList[B](l: List[(Long, B)]): List[Long] = { + require(invariantList(l)) + decreases(l) + l match { + case Cons(head, tl) => Cons(head._1, getKeysList(tl)) + case Nil() => Nil[Long]() + } + }.ensuring(res => isStrictlySortedLong(res) && res.length == l.length) + + def intSizeKeys(l: List[Long]): Int = { + require(l.length < Integer.MAX_VALUE) + decreases(l) + + l match { + case Cons(head, tl) => 1 + intSizeKeys(tl) + case Nil() => 0 + } + } + + def intSize[B](l: List[(Long, B)]): Int = { + decreases(l) + l match { + case Cons(head, tl) => { + val s1 = intSize(tl) + if (s1 < Integer.MAX_VALUE) { + 1 + s1 + } else { + 0 + } + } + + case Nil() => 0 + } + }.ensuring(res => res >= 0) + + def getKeysOf[B](l: List[(Long, B)], value: B): List[Long] = { + require(invariantList(l)) + decreases(l) + + l match { + case Cons(head, tl) if (head._2 == value) => { + if (!getKeysOf(tl, value).isEmpty) { + lemmaForallGetValueByKeySameWithASmallerHead( + tl, + getKeysOf(tl, value), + value, + head + ) + + } + Cons(head._1, getKeysOf(tl, value)) + } + case Cons(head, tl) if (head._2 != value) => { + val r = getKeysOf(tl, value) + if (!getKeysOf(tl, value).isEmpty) { + lemmaForallGetValueByKeySameWithASmallerHead( + tl, + getKeysOf(tl, value), + value, + head + ) + } + getKeysOf(tl, value) + } + case Nil() => Nil[Long]() + } + + }.ensuring(res => res.forall(getValueByKey(l, _) == Some[B](value))) + + def filterByValue[B](l: List[(Long, B)], value: B): List[(Long, B)] = { + require(invariantList(l)) + decreases(l) + + l match { + case Cons(head, tl) if (head._2 == value) => + head :: filterByValue(tl, value) + case Cons(head, tl) if (head._2 != value) => filterByValue(tl, value) + case Nil() => Nil[(Long, B)]() + } + }.ensuring(res => + invariantList(res) && res.forall(_._2 == value) && + (if (l.isEmpty) res.isEmpty else res.isEmpty || res.head._1 >= l.head._1) + ) + + def getValueByKey[B](l: List[(Long, B)], key: Long): Option[B] = { + require(invariantList(l)) + decreases(l) + + l match { + case Cons(head, tl) if (head._1 == key) => Some[B](head._2) + case Cons(head, tl) if (head._1 != key) => getValueByKey(tl, key) + case Nil() => None[B]() + } + + } + + def insertStrictlySorted[B]( + l: List[(Long, B)], + newKey: Long, + newValue: B + ): List[(Long, B)] = { + require(invariantList(l)) + decreases(l) + + l match { + case Cons(head, tl) if (head._1 < newKey) => + head :: insertStrictlySorted(tl, newKey, newValue) + case Cons(head, tl) if (head._1 == newKey) => (newKey, newValue) :: tl + case Cons(head, tl) if (head._1 > newKey) => + (newKey, newValue) :: Cons(head, tl) + case Nil() => (newKey, newValue) :: Nil() + } + }.ensuring(res => + invariantList(res) && containsKey(res, newKey) && res.contains( + (newKey, newValue) + ) + ) + + def removeStrictlySorted[B]( + l: List[(Long, B)], + key: Long + ): List[(Long, B)] = { + require(invariantList(l)) + decreases(l) + + l match { + case Cons(head, tl) if (head._1 == key) => tl + case Cons(head, tl) if (head._1 != key) => + head :: removeStrictlySorted(tl, key) + case Nil() => Nil[(Long, B)]() + } + }.ensuring(res => invariantList(res) && !containsKey(res, key)) + + def isStrictlySorted[B](l: List[(Long, B)]): Boolean = { + decreases(l) + l match { + case Nil() => true + case Cons(_, Nil()) => true + case Cons(h1, Cons(h2, _)) if (h1._1 >= h2._1) => false + case Cons(_, t) => isStrictlySorted(t) + } + } + + def isStrictlySortedLong(l: List[Long]): Boolean = { + decreases(l) + l match { + case Nil() => true + case Cons(_, Nil()) => true + case Cons(h1, Cons(h2, _)) if (h1 >= h2) => false + case Cons(_, t) => isStrictlySortedLong(t) + } + } + + def containsKey[B](l: List[(Long, B)], key: Long): Boolean = { + require(invariantList(l)) + decreases(l) + l match { + case Cons(head, tl) if (head._1 == key) => true + case Cons(head, tl) if (head._1 > key) => false + case Cons(head, tl) if (head._1 < key) => containsKey(tl, key) + case Nil() => false + + } + } + + // ----------- LEMMAS ----------------------------------------------------- + + @opaque + @inlineOnce + def lemmaInsertAndRemoveStrictlySortedCommutative[B]( + l: List[(Long, B)], + key1: Long, + v1: B, + key2: Long + ): Unit = { + require(key1 != key2) + require(invariantList(l)) + decreases(l) + + l match { + case Cons(head, tl) => { + lemmaInsertAndRemoveStrictlySortedCommutative(tl, key1, v1, key2) + } + case _ => () + } + + }.ensuring(_ => + insertStrictlySorted( + removeStrictlySorted(l, key2), + key1, + v1 + ) == removeStrictlySorted( + insertStrictlySorted(l, key1, v1), + key2 + ) + ) + + @opaque + @inlineOnce + def lemmaInsertStrictlySortedThenRemoveIsSame[B]( + l: List[(Long, B)], + key1: Long, + v1: B + ): Unit = { + require(invariantList(l)) + require(!containsKey(l, key1)) + decreases(l) + + l match { + case Cons(head, tl) => { + lemmaInsertStrictlySortedThenRemoveIsSame(tl, key1, v1) + } + case _ => () + } + + }.ensuring(_ => removeStrictlySorted(insertStrictlySorted(l, key1, v1), key1) == l) + + @opaque + @inlineOnce + def lemmaInsertStrictlySortedCommutative[B]( + l: List[(Long, B)], + key1: Long, + v1: B, + key2: Long, + v2: B + ): Unit = { + require(key1 != key2) + require(invariantList(l)) + decreases(l) + + l match { + case Cons(head, tl) if (head._1 < key1 && head._1 < key2) => { + lemmaInsertStrictlySortedCommutative(tl, key1, v1, key2, v2) + } + case _ => () + } + + }.ensuring(_ => + insertStrictlySorted( + insertStrictlySorted(l, key1, v1), + key2, + v2 + ) == insertStrictlySorted( + insertStrictlySorted(l, key2, v2), + key1, + v1 + ) + ) + + @opaque + @inlineOnce + def lemmaRemoveStrictlySortedCommutative[B]( + l: List[(Long, B)], + key1: Long, + key2: Long + ): Unit = { + require(invariantList(l)) + decreases(l) + + l match { + case Cons(head, tl) => { + lemmaRemoveStrictlySortedCommutative(tl, key1, key2) + } + case _ => () + } + + }.ensuring(_ => + removeStrictlySorted( + removeStrictlySorted(l, key1), + key2 + ) == removeStrictlySorted( + removeStrictlySorted(l, key2), + key1 + ) + ) + + @opaque + @inlineOnce + def lemmaRemoveStrictlySortedNotPresentPreserves[B]( + l: List[(Long, B)], + key: Long + ): Unit = { + require(invariantList(l)) + require(!containsKey(l, key)) + decreases(l) + + l match { + case Cons(head, tl) => { + lemmaRemoveStrictlySortedNotPresentPreserves(tl, key) + } + case _ => () + } + + }.ensuring(_ => removeStrictlySorted(l, key) == l) + + @opaque + @inlineOnce + def lemmaInsertStrictlySortedErasesIfSameKey[B]( + l: List[(Long, B)], + key1: Long, + v1: B, + v2: B + ): Unit = { + require(invariantList(l)) + decreases(l) + + l match { + case Cons(head, tl) if (head._1 < key1) => { + lemmaInsertStrictlySortedErasesIfSameKey(tl, key1, v1, v2) + } + case _ => () + } + + }.ensuring(_ => + insertStrictlySorted( + insertStrictlySorted(l, key1, v1), + key1, + v2 + ) == insertStrictlySorted( + l, + key1, + v2 + ) + ) + + @opaque + @inlineOnce + def lemmaAddNewKeyIncrementSize[B]( + l: List[(Long, B)], + key: Long, + value: B + ): Unit = { + require(invariantList(l)) + require(!containsKey(l, key)) + decreases(l) + + val inserted = insertStrictlySorted(l, key, value) + l match { + case Cons(head, tl) if (head._1 < key) => { + lemmaAddNewKeyIncrementSize(tl, key, value) + + } + case Cons(head, tl) if (head._1 == key) => check(false) + case _ => + } + + }.ensuring(_ => insertStrictlySorted(l, key, value).length == l.length + 1) + + @opaque + @inlineOnce + def lemmaAddExistingKeyPreservesSize[B]( + l: List[(Long, B)], + key: Long, + value: B + ): Unit = { + decreases(l) + require(invariantList(l)) + require(containsKey(l, key)) + + val inserted = insertStrictlySorted(l, key, value) + l match { + case Cons(head, tl) if (head._1 < key) => { + lemmaAddExistingKeyPreservesSize(tl, key, value) + } + case Cons(head, tl) if (head._1 == key) => { + assert(inserted == Cons((key, value), tl)) + } + case _ => + } + + }.ensuring(_ => insertStrictlySorted(l, key, value).length == l.length) + + @opaque + @inlineOnce + def lemmaGetValueByKeyIsDefinedImpliesContainsKey[B]( + l: List[(Long, B)], + key: Long + ): Unit = { + require(invariantList(l) && getValueByKey(l, key).isDefined) + decreases(l) + l match { + case Cons(head, tl) if (head._1 != key) => + lemmaGetValueByKeyIsDefinedImpliesContainsKey(tl, key) + case _ => () + } + }.ensuring(_ => containsKey(l, key)) + + @opaque + @inlineOnce + def lemmaContainsKeyImpliesGetValueByKeyDefined[B]( + l: List[(Long, B)], + key: Long + ): Unit = { + require(invariantList(l) && containsKey(l, key)) + decreases(l) + l match { + case Cons(head, tl) if (head._1 != key) => + lemmaContainsKeyImpliesGetValueByKeyDefined(tl, key) + case _ => () + } + }.ensuring(_ => getValueByKey(l, key).isDefined) + + @opaque + @inlineOnce + def lemmaForallGetValueByKeySameWithASmallerHead[B]( + l: List[(Long, B)], + keys: List[Long], + value: B, + newHead: (Long, B) + ): Unit = { + require( + invariantList(l) && !l.isEmpty && + keys.forall(getValueByKey(l, _) == Some[B](value)) && + newHead._1 < l.head._1 + ) + decreases(keys) + + keys match { + case Cons(head, tl) => { + lemmaGetValueByKeyIsDefinedImpliesContainsKey(l, head) + lemmaContainsKeyImpliesGetValueByKeyDefined(Cons(newHead, l), head) + lemmaForallGetValueByKeySameWithASmallerHead(l, tl, value, newHead) + } + case _ => () + } + + }.ensuring(_ => keys.forall(k => getValueByKey(Cons(newHead, l), k) == Some[B](value))) + + @opaque + @inlineOnce + def lemmaInsertStrictlySortedDoesNotModifyOtherKeyValues[B]( + l: List[(Long, B)], + newKey: Long, + newValue: B, + otherKey: Long + ): Unit = { + require(invariantList(l) && newKey != otherKey) + decreases(l) + + l match { + case Cons(head, tl) if (head._1 != otherKey) => + lemmaInsertStrictlySortedDoesNotModifyOtherKeyValues( + tl, + newKey, + newValue, + otherKey + ) + case _ => () + } + + }.ensuring(_ => + containsKey( + insertStrictlySorted(l, newKey, newValue), + otherKey + ) == containsKey(l, otherKey) && + getValueByKey( + insertStrictlySorted(l, newKey, newValue), + otherKey + ) == getValueByKey( + l, + otherKey + ) + ) + + @opaque + @inlineOnce + def lemmaInsertStrictlySortedDoesNotModifyOtherKeysNotContained[B]( + l: List[(Long, B)], + newKey: Long, + newValue: B, + otherKey: Long + ): Unit = { + require(invariantList(l) && !containsKey(l, otherKey) && otherKey != newKey) + decreases(l) + + l match { + case Cons(head, tl) => + lemmaInsertStrictlySortedDoesNotModifyOtherKeysNotContained( + tl, + newKey, + newValue, + otherKey + ) + case _ => () + } + }.ensuring(_ => !containsKey(insertStrictlySorted(l, newKey, newValue), otherKey)) + + @opaque + @inlineOnce + def lemmaInsertStrictlySortedDoesNotModifyOtherKeysContained[B]( + l: List[(Long, B)], + newKey: Long, + newValue: B, + otherKey: Long + ): Unit = { + require(invariantList(l) && containsKey(l, otherKey) && otherKey != newKey) + decreases(l) + + l match { + case Cons(head, tl) if (head._1 != otherKey) => + lemmaInsertStrictlySortedDoesNotModifyOtherKeysContained( + tl, + newKey, + newValue, + otherKey + ) + case _ => () + } + }.ensuring(_ => containsKey(insertStrictlySorted(l, newKey, newValue), otherKey)) + + @opaque + @inlineOnce + def lemmaInsertStrictlySortedNotContainedContent[B]( + @induct l: List[(Long, B)], + newKey: Long, + newValue: B + ): Unit = { + require(invariantList(l)) + require(!containsKey(l, newKey)) + + } ensuring (_ => + l.content ++ Set((newKey, newValue)) == insertStrictlySorted( + l, + newKey, + newValue + ).content + ) + + @opaque + @inlineOnce + def lemmaNotContainsKeyThenNotContainsTuple[B]( + @induct l: List[(Long, B)], + key: Long, + value: B + ): Unit = { + require(invariantList(l) && !containsKey(l, key)) + + }.ensuring(_ => !l.contains((key, value))) + + @opaque + @inlineOnce + def lemmaContainsTupleThenContainsKey[B]( + l: List[(Long, B)], + key: Long, + value: B + ): Unit = { + require(invariantList(l) && l.contains((key, value))) + decreases(l) + + l match { + case Cons(head, tl) if (head != (key, value)) => + lemmaContainsTupleThenContainsKey(tl, key, value) + case _ => () + } + }.ensuring(_ => containsKey(l, key)) + + @opaque + @inlineOnce + def lemmaContainsTupThenGetReturnValue[B]( + l: List[(Long, B)], + key: Long, + value: B + ): Unit = { + require(invariantList(l) && containsKey(l, key) && l.contains((key, value))) + decreases(l) + + l match { + case head :: Nil() => () + case Cons(head, tl) if (head._1 == key) => + lemmaNotContainsKeyThenNotContainsTuple(tl, key, value) + case Cons(head, tl) => lemmaContainsTupThenGetReturnValue(tl, key, value) + case Nil() => () + } + }.ensuring(_ => getValueByKey(l, key) == Some[B](value)) +} + +object ListLongMap { + def empty[B]: ListLongMap[B] = ListLongMap[B](List.empty[(Long, B)]) +} + +object ListLongMapLemmas { + import ListSpecs._ + + @opaque + @inlineOnce + def removeNotPresentStillSame[B](lm: ListLongMap[B], a: Long): Unit = { + require(!lm.contains(a)) + TupleListOps.lemmaRemoveStrictlySortedNotPresentPreserves(lm.toList, a) + }.ensuring(_ => lm - a == lm) + + @opaque + @inlineOnce + def addSameAsAddTwiceSameKeyDiffValues[B]( + lm: ListLongMap[B], + a: Long, + b1: B, + b2: B + ): Unit = { + TupleListOps.lemmaInsertStrictlySortedErasesIfSameKey(lm.toList, a, b1, b2) + }.ensuring(_ => lm + (a, b2) == (lm + (a, b1) + (a, b2))) + + @opaque + @inlineOnce + def addRemoveCommutativeForDiffKeys[B]( + lm: ListLongMap[B], + a1: Long, + b1: B, + a2: Long + ): Unit = { + require(a1 != a2) + TupleListOps.lemmaInsertAndRemoveStrictlySortedCommutative( + lm.toList, + a1, + b1, + a2 + ) + }.ensuring(_ => lm + (a1, b1) - a2 == lm - a2 + (a1, b1)) + + @opaque + @inlineOnce + def addThenRemoveForNewKeyIsSame[B]( + lm: ListLongMap[B], + a1: Long, + b1: B + ): Unit = { + require(!lm.contains(a1)) + TupleListOps.lemmaInsertStrictlySortedThenRemoveIsSame(lm.toList, a1, b1) + }.ensuring(_ => lm + (a1, b1) - a1 == lm) + + @opaque + @inlineOnce + def removeCommutative[B](lm: ListLongMap[B], a1: Long, a2: Long): Unit = { + TupleListOps.lemmaRemoveStrictlySortedCommutative(lm.toList, a1, a2) + }.ensuring(_ => lm - a1 - a2 == lm - a2 - a1) + + @opaque + @inlineOnce + def addCommutativeForDiffKeys[B]( + lm: ListLongMap[B], + a1: Long, + b1: B, + a2: Long, + b2: B + ): Unit = { + require(a1 != a2) + TupleListOps.lemmaInsertStrictlySortedCommutative(lm.toList, a1, b1, a2, b2) + }.ensuring(_ => lm + (a1, b1) + (a2, b2) == lm + (a2, b2) + (a1, b1)) + + @opaque + @inlineOnce + def emptyContainsNothing[B](k: Long): Unit = {}.ensuring(_ => !ListLongMap.empty[B].contains(k)) + + @opaque + @inlineOnce + def addValidProp[B]( + lm: ListLongMap[B], + p: ((Long, B)) => Boolean, + a: Long, + b: B + ): Unit = { + require(lm.forall(p) && p(a, b)) + decreases(lm.toList.size) + + if (!lm.isEmpty) + addValidProp(lm.tail, p, a, b) + + }.ensuring { _ => + val nlm = lm + (a, b) + nlm.forall(p) + } + + @opaque + @inlineOnce + def removeValidProp[B]( + lm: ListLongMap[B], + p: ((Long, B)) => Boolean, + a: Long + ): Unit = { + require(lm.forall(p)) + decreases(lm.toList.size) + if (!lm.isEmpty) + removeValidProp(lm.tail, p, a) + + }.ensuring { _ => + val nlm = lm - a + nlm.forall(p) + } + + @opaque + @inlineOnce + def insertAllValidProp[B]( + lm: ListLongMap[B], + kvs: List[(Long, B)], + p: ((Long, B)) => Boolean + ): Unit = { + require(lm.forall(p) && kvs.forall(p)) + decreases(kvs) + + if (!kvs.isEmpty) { + addValidProp(lm, p, kvs.head._1, kvs.head._2) + insertAllValidProp(lm + kvs.head, kvs.tail, p) + } + + }.ensuring { _ => + val nlm = lm ++ kvs + nlm.forall(p) + } + + @opaque + @inlineOnce + def removeAllValidProp[B]( + lm: ListLongMap[B], + l: List[Long], + p: ((Long, B)) => Boolean + ): Unit = { + require(lm.forall(p)) + decreases(l) + + if (!l.isEmpty) { + removeValidProp(lm, p, l.head) + removeAllValidProp(lm - l.head, l.tail, p) + } + + }.ensuring { _ => + val nlm = lm -- l + nlm.forall(p) + } + + @opaque + @inlineOnce + def addApplyDifferent[B]( + lm: ListLongMap[B], + a: Long, + b: B, + a0: Long + ): Unit = { + require(lm.contains(a0) && a0 != a) + assert(TupleListOps.containsKey(lm.toList, a0)) + TupleListOps.lemmaInsertStrictlySortedDoesNotModifyOtherKeyValues( + lm.toList, + a, + b, + a0 + ) + TupleListOps.lemmaContainsKeyImpliesGetValueByKeyDefined(lm.toList, a0) + + }.ensuring(_ => (lm + (a -> b))(a0) == lm(a0)) + + @opaque + @inlineOnce + def addStillContains[B]( + lm: ListLongMap[B], + a: Long, + b: B, + a0: Long + ): Unit = { + require(lm.contains(a0)) + + if (a != a0) + TupleListOps.lemmaInsertStrictlySortedDoesNotModifyOtherKeysContained( + lm.toList, + a, + b, + a0 + ) + + }.ensuring(_ => (lm + (a, b)).contains(a0)) + + @opaque + @inlineOnce + def addStillNotContains[B]( + lm: ListLongMap[B], + a: Long, + b: B, + a0: Long + ): Unit = { + require(!lm.contains(a0) && a != a0) + + TupleListOps.lemmaInsertStrictlySortedDoesNotModifyOtherKeysNotContained( + lm.toList, + a, + b, + a0 + ) + + }.ensuring(_ => !(lm + (a, b)).contains(a0)) + + @opaque + @inlineOnce + def applyForall[B]( + lm: ListLongMap[B], + p: ((Long, B)) => Boolean, + k: Long + ): Unit = { + require(lm.forall(p) && lm.contains(k)) + decreases(lm.toList.size) + + if (!lm.isEmpty && lm.toList.head._1 != k) + applyForall(lm.tail, p, k) + + }.ensuring(_ => p(k, lm(k))) + + @opaque + @inlineOnce + def getForall[B]( + lm: ListLongMap[B], + p: ((Long, B)) => Boolean, + k: Long + ): Unit = { + require(lm.forall(p)) + decreases(lm.toList.size) + + if (!lm.isEmpty && lm.toList.head._1 != k) + getForall(lm.tail, p, k) + + }.ensuring(_ => lm.get(k).forall(v => p(k, v))) + + @opaque + @inlineOnce + def uniqueImage[B](lm: ListLongMap[B], a: Long, b: B): Unit = { + require(lm.toList.contains((a, b))) + + TupleListOps.lemmaContainsTupleThenContainsKey(lm.toList, a, b) + TupleListOps.lemmaContainsTupThenGetReturnValue(lm.toList, a, b) + + }.ensuring(_ => lm.get(a) == Some[B](b)) + + @opaque + def keysOfSound[B](@induct lm: ListLongMap[B], value: B): Unit = { + // trivial by postcondition of getKeysOf + assert(TupleListOps.getKeysOf(lm.toList, value).forall(k => lm.get(k) == Some[B](value))) + }.ensuring(_ => lm.keysOf(value).forall(key => lm.get(key) == Some[B](value))) + + @opaque + @inlineOnce + def addNotContainedContent[B]( + lm: ListLongMap[B], + key: Long, + value: B + ): Unit = { + require(!lm.contains(key)) + TupleListOps.lemmaInsertStrictlySortedNotContainedContent( + lm.toList, + key, + value + ) + } ensuring (_ => + lm.toList.content ++ Set( + (key, value) + ) == (lm + (key, value)).toList.content + ) +} diff --git a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/chassot/MutableLongMap.scala b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/chassot/MutableLongMap.scala new file mode 100644 index 00000000..ba51bf7b --- /dev/null +++ b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/chassot/MutableLongMap.scala @@ -0,0 +1,8407 @@ +/** Author: Samuel Chassot + */ +package ch.epfl.chassot + +import stainless.annotation._ +import stainless.collection._ +import stainless.equations._ +import stainless.lang.{ghost => ghostExpr, *} +import stainless.proof.check +import scala.annotation.tailrec +import stainless.lang.Cell + +import stainless.lang.StaticChecks.* // Comment out when using the OptimisedEnsuring object below +// import OptimisedChecks.* // Import to remove `ensuring` and `require` from the code for the benchmarks + +object MutableLongMap { + import LongMapFixedSize.validMask + + /** Helper method to create a new empty LongMap + * + * @param defaultEntry + * @return + */ + def getEmptyLongMap[V](defaultEntry: Long => V): LongMap[V] = { + val m = 15 + assert(validMask(m)) + LongMap(Cell(LongMapFixedSize.getNewLongMapFixedSize(m, defaultEntry))) + } ensuring (res => res.valid && res.size == 0) + + /** Helper method to create a new empty LongMap with a given initial array size WARNING: UNSOUND!!! The given size must be a power of 2 <= 2^30 + * + * @param defaultEntry + * @return + */ + def getEmptyLongMap[V](defaultEntry: Long => V, initialSize: Int): LongMap[V] = { + require(validMask(initialSize - 1)) + val m = initialSize - 1 + assert(validMask(m)) + LongMap(Cell(LongMapFixedSize.getNewLongMapFixedSize(m, defaultEntry))) + } ensuring (res => res.valid && res.size == 0) + + @mutable + final case class LongMap[V]( + val underlying: Cell[LongMapFixedSize[V]] + ) { + + @pure + def imbalanced(): Boolean = (2 * (underlying.v._size + underlying.v._vacant)) > underlying.v.mask || underlying.v._vacant > underlying.v._size + + @pure + def size: Int = underlying.v.size + + @pure + def isEmpty: Boolean = { + require(valid) + underlying.v.isEmpty + } ensuring (_ => valid) + + @pure + def contains(key: Long): Boolean = { + require(valid) + underlying.v.contains(key) + } ensuring (res => valid && (res == map.contains(key))) + + @pure + def apply(key: Long): V = { + require(valid) + underlying.v.apply(key) + } ensuring (res => + valid + && (if (contains(key)) res == map.get(key).get + else res == underlying.v.defaultEntry(key)) + ) + + def update(key: Long, v: V): Boolean = { + require(valid) + val repacked = if (imbalanced()) { + repack() + } else { + true + } + if (repacked) { + underlying.v.update(key, v) + } else { + false + } + } ensuring (res => valid && (if (res) map.contains(key) && (map == old(this).map + (key, v)) else map == old(this).map)) + + def remove(key: Long): Boolean = { + require(valid) + underlying.v.remove(key) + } ensuring (res => valid && (if (res) map == old(this).map - key else map == old(this).map)) + + // require(_size < 268435456) // Smallest size that can trigger a problem with a certain mask + @pure + def computeNewMask(oldMask: Int, _vacant: Int, _size: Int): Int = { + require(validMask(oldMask)) + require(_size >= 0 && _size <= oldMask + 1) + require(_vacant >= 0) + var m = oldMask + if (2 * (_size + _vacant) >= oldMask && !(5 * _vacant > oldMask)) { + m = ((m << 1) + 1) & MAX_MASK + } + while (m > 8 && 8 * _size < m && ((m >> 1) & MAX_MASK) + 1 >= _size) { + decreases(m) + m = m >>> 1 + } + m + } ensuring (res => validMask(res) && _size <= res + 1) + + def repack(): Boolean = { + require(valid) + + val newMask: Int = computeNewMask(underlying.v.mask, underlying.v._vacant, underlying.v._size) + val newMapCell: Cell[LongMapFixedSize[V]] = Cell(LongMapFixedSize.getNewLongMapFixedSize(newMask, underlying.v.defaultEntry)) + val resExtraKeys = if ((underlying.v.extraKeys & 1) != 0 && (underlying.v.extraKeys & 2) != 0) { + // it means there is a mapping for the key 0 and the Long.MIN_VALUE + val u1 = newMapCell.v.update(0L, underlying.v.zeroValue) + val u2 = newMapCell.v.update(Long.MinValue, underlying.v.minValue) + u1 && u2 + } else if ((underlying.v.extraKeys & 1) != 0 && (underlying.v.extraKeys & 2) == 0) { + // it means there is a mapping for the key 0 + newMapCell.v.update(0L, underlying.v.zeroValue) + } else if ((underlying.v.extraKeys & 2) != 0 && (underlying.v.extraKeys & 1) == 0) { + // it means there is a mapping for the key Long.MIN_VALUE + newMapCell.v.update(Long.MinValue, underlying.v.minValue) + } else { + true + } + + if (!resExtraKeys) { + false + } else { + assert(LongMapFixedSize.validMask(underlying.v.mask)) + assert(underlying.v._keys.length == underlying.v.mask + 1) + assert((underlying.v._keys.length - 1) >= 0) + val repackFromRes = repackFrom(newMapCell.v, underlying.v._keys.length - 1) + if (repackFromRes) { + // Swap the current underyling with the new one + swap(underlying, newMapCell) + true + } else { + false + } + } + } ensuring (res => res == false || map == old(this).map) + + // @tailrec + def repackFrom(newMap: LongMapFixedSize[V], from: Int): Boolean = { + require(valid) + require(from >= 0 && from < underlying.v._keys.length) + require(newMap.valid) + require(newMap.mask + 1 >= underlying.v._size) + require( + LongMapFixedSize.getCurrentListMap( + underlying.v._keys, + underlying.v._values, + underlying.v.mask, + underlying.v.extraKeys, + underlying.v.zeroValue, + underlying.v.minValue, + from + 1, + underlying.v.defaultEntry + ) == newMap.map + ) + decreases(from) + val currentKey = underlying.v._keys(from) + // println(f"RepackFrom: from = $from, key = $currentKey") + + val currentValue = underlying.v._values(from).get(underlying.v.defaultEntry(0L)) + + @ghost val newMapListMapBefore = newMap.map + + if (currentKey != 0 && currentKey != Long.MinValue) { + + // There is a key in the array, add it to the new map + val res = newMap.update(currentKey, currentValue) + + ghostExpr(if (newMapListMapBefore.contains(currentKey)) { + LongMapFixedSize.lemmaListMapContainsThenArrayContainsFrom( + underlying.v._keys, + underlying.v._values, + underlying.v.mask, + underlying.v.extraKeys, + underlying.v.zeroValue, + underlying.v.minValue, + currentKey, + from + 1, + underlying.v.defaultEntry + ) + LongMapFixedSize.lemmaNoDuplicateFromThenFromBigger(underlying.v._keys, 0, from) + LongMapFixedSize.lemmaArrayNoDuplicateFromNotContainsKeysInAcc(underlying.v._keys, from + 1, currentKey, List(currentKey)) + check(false) + } else { () }) + + if (res) { + if (from > 0) { + + // val underlyingMapFromPOneNXtra = LongMapFixedSize.getCurrentListMapNoExtraKeys(underlying.v._keys,underlying.v._values,underlying.v.mask,underlying.v.extraKeys,underlying.v.zeroValue,underlying.v.minValue,from + 1,underlying.v.defaultEntry) + ghostExpr( + ListLongMapLemmas.addCommutativeForDiffKeys( + LongMapFixedSize.getCurrentListMapNoExtraKeys( + underlying.v._keys, + underlying.v._values, + underlying.v.mask, + underlying.v.extraKeys, + underlying.v.zeroValue, + underlying.v.minValue, + from + 1, + underlying.v.defaultEntry + ), + currentKey, + currentValue, + 0L, + underlying.v.zeroValue + ) + ) + ghostExpr( + ListLongMapLemmas.addCommutativeForDiffKeys( + LongMapFixedSize.getCurrentListMapNoExtraKeys( + underlying.v._keys, + underlying.v._values, + underlying.v.mask, + underlying.v.extraKeys, + underlying.v.zeroValue, + underlying.v.minValue, + from + 1, + underlying.v.defaultEntry + ) + (0L, underlying.v.zeroValue), + currentKey, + currentValue, + Long.MinValue, + underlying.v.minValue + ) + ) + assert( + LongMapFixedSize.getCurrentListMap( + underlying.v._keys, + underlying.v._values, + underlying.v.mask, + underlying.v.extraKeys, + underlying.v.zeroValue, + underlying.v.minValue, + from, + underlying.v.defaultEntry + ) == newMap.map + ) + repackFrom(newMap, from - 1) + } else { + + ghostExpr( + ListLongMapLemmas.addCommutativeForDiffKeys( + LongMapFixedSize.getCurrentListMapNoExtraKeys( + underlying.v._keys, + underlying.v._values, + underlying.v.mask, + underlying.v.extraKeys, + underlying.v.zeroValue, + underlying.v.minValue, + from + 1, + underlying.v.defaultEntry + ), + currentKey, + currentValue, + 0L, + underlying.v.zeroValue + ) + ) + ghostExpr( + ListLongMapLemmas.addCommutativeForDiffKeys( + LongMapFixedSize.getCurrentListMapNoExtraKeys( + underlying.v._keys, + underlying.v._values, + underlying.v.mask, + underlying.v.extraKeys, + underlying.v.zeroValue, + underlying.v.minValue, + from + 1, + underlying.v.defaultEntry + ) + (0L, underlying.v.zeroValue), + currentKey, + currentValue, + Long.MinValue, + underlying.v.minValue + ) + ) + true + } + } else { + false + } + + } else { + if (from > 0) { + repackFrom(newMap, from - 1) + } else { + true + } + } + + } ensuring (res => if (res) newMap.valid && newMap.map == underlying.v.map else true) + + @ghost + def valid: Boolean = underlying.v.valid + + @pure + @ghost + private def map: ListLongMap[V] = { + require(valid) + underlying.v.map + } + + } + + sealed trait ValueCell[V] { + def get(defaultValue: V): V + } + + case class ValueCellFull[V](v: V) extends ValueCell[V] { + def get(defaultValue: V): V = v + } + + case class EmptyCell[V]() extends ValueCell[V] { + def get(defaultValue: V): V = defaultValue + } + + def isFull[V](c: ValueCell[V]): Boolean = { + c match { + case ValueCellFull(_) => true + case EmptyCell() => false + } + } + + def extractCell[V](c: ValueCell[V]): V = { + require(isFull(c)) + c match { + case ValueCellFull(v) => v + } + } + + private final val MAX_MASK: Int = 0x3fffffff + + private final val MAX_ITER = Int.MaxValue - 1 // arbitrary + + /** A Map with keys of type Long and values of type Long mask must be a valid mask, i.e., 2^n - 1. The smallest possible mask is 0 and the biggest is 0x3fffffff _keys and _values must be initialized + * to an array of length mask + 1, containing all 0 values, i.e., Array.fill(mask + 1)(0) extraKeys must be initialized to 0 _size must be initialized to 0 + * + * @param mask + * @param extraKeys + * @param zeroValue + * @param minValue + * @param _size + * @param _keys + * @param _values + */ + @mutable + final case class LongMapFixedSize[V]( + val defaultEntry: Long => V, + val mask: Int, + var extraKeys: Int, + var zeroValue: V, + var minValue: V, + var _size: Int, + val _keys: Array[Long], + val _values: Array[ValueCell[V]], + var _vacant: Int + ) { + import LongMapFixedSize.validKeyInArray + import LongMapFixedSize.arrayCountValidKeys + import LongMapFixedSize.arrayContainsKey + import LongMapFixedSize.arrayScanForKey + import LongMapFixedSize.arrayNoDuplicates + import LongMapFixedSize.seekEntryOrOpen + import LongMapFixedSize.toIndex + import LongMapFixedSize.lemmaArrayContainsFromImpliesContainsFromZero + import LongMapFixedSize.arrayForallSeekEntryOrOpenFound + import LongMapFixedSize.lemmaValidKeyAtIImpliesCountKeysIsOne + import LongMapFixedSize.lemmaAddValidKeyIncreasesNumberOfValidKeysInArray + import LongMapFixedSize.lemmaRemoveValidKeyDecreasesNumberOfValidKeysInArray + import LongMapFixedSize.lemmaArrayForallSeekEntryOrOpenFoundFromSmallerThenFromBigger + import LongMapFixedSize.lemmaArrayNoDuplicateThenKeysContainedNotEqual + import LongMapFixedSize.lemmaNoDuplicateFromThenFromBigger + import LongMapFixedSize.lemmaPutNonValidKeyPreservesNoDuplicate + import LongMapFixedSize.lemmaPutNewValidKeyPreservesNoDuplicate + import LongMapFixedSize.seekEntry + import LongMapFixedSize.inRange + import LongMapFixedSize.validMask + import LongMapFixedSize.lemmaPutLongMinValuePreservesForallSeekEntryOrOpen + import LongMapFixedSize.lemmaSeekEntryOrOpenFindsThenSeekEntryFinds + import LongMapFixedSize.lemmaPutValidKeyPreservesForallSeekEntryOrOpen + import LongMapFixedSize.lemmaArrayNoDuplicateRemoveOneThenNotContain + import LongMapFixedSize.getCurrentListMap + import LongMapFixedSize.lemmaKeyInListMapIsInArray + import LongMapFixedSize.lemmaValidKeyInArrayIsInListMap + import LongMapFixedSize.lemmaKeyInListMapThenSameValueInArray + import LongMapFixedSize.lemmaArrayContainsKeyThenInListMap + import LongMapFixedSize.lemmaSeekEntryGivesInRangeIndex + import LongMapFixedSize.lemmaInListMapThenSeekEntryOrOpenFindsIt + import LongMapFixedSize.lemmaNotInListMapThenSeekEntryOrOpenFindsFreeOrNothing + import LongMapFixedSize.lemmaAddValidKeyToArrayThenAddPairToListMap + import LongMapFixedSize.lemmaChangeZeroKeyThenAddPairToListMap + import LongMapFixedSize.lemmaChangeLongMinValueKeyThenAddPairToListMap + import LongMapFixedSize.lemmaChangeValueExistingKeyToArrayThenAddPairToListMap + import LongMapFixedSize.lemmaRemoveZeroKeyThenRemoveKeyFromListMap + import LongMapFixedSize.lemmaRemoveLongMinValueKeyThenRemoveKeyFromListMap + import LongMapFixedSize.lemmaRemoveValidKeyToArrayThenRemoveKeyFromListMap + + @pure + def size: Int = { + _size + (extraKeys + 1) / 2 + } + + @pure + def imbalanced: Boolean = + 2 * _size > mask + + @pure + def isEmpty: Boolean = { + require(valid) + size == 0 + } ensuring (_ => valid) + + @pure + def contains(key: Long): Boolean = { + require(valid) + if (key == 0) (extraKeys & 1) != 0 + else if (key == Long.MinValue) (extraKeys & 2) != 0 + else { + val seekEntryRes = seekEntry(key)(_keys, mask) + seekEntryRes match { + case Found(index) => { + ghostExpr(lemmaArrayContainsFromImpliesContainsFromZero(_keys, key, index)) + ghostExpr( + lemmaValidKeyInArrayIsInListMap( + _keys, + _values, + mask, + extraKeys, + zeroValue, + minValue, + index, + defaultEntry + ) + ) + true + } + case _ => { + ghostExpr( + if ( + getCurrentListMap( + _keys, + _values, + mask, + extraKeys, + zeroValue, + minValue, + 0, + defaultEntry + ).contains( + key + ) + ) { + lemmaKeyInListMapIsInArray( + _keys, + _values, + mask, + extraKeys, + zeroValue, + minValue, + key, + defaultEntry + ) + val i = arrayScanForKey(_keys, key, 0) + lemmaArrayForallSeekEntryOrOpenFoundFromSmallerThenFromBigger(_keys, mask, 0, i) + lemmaSeekEntryOrOpenFindsThenSeekEntryFinds(key, i, _keys, mask) + check(false) + } + ) + false + } + } + } + } ensuring (res => valid && (res == map.contains(key))) + + /** Retrieves the value associated with a key. If the key does not exist in the map, the `defaultEntry` for that key is returned instead. + * + * @param key + * @return + */ + @pure + def apply(key: Long): V = { + require(valid) + if (key == -key) { + if (key == 0 && (extraKeys & 1) != 0) zeroValue + else if (key == Long.MinValue && (extraKeys & 2) != 0) minValue + else defaultEntry(key) + } else { + val seekEntryRes = seekEntry(key)(_keys, mask) + ghostExpr(lemmaSeekEntryGivesInRangeIndex(_keys, _values, mask, extraKeys, zeroValue, minValue, key)) + seekEntryRes match { + case Found(index) => { + ghostExpr(lemmaArrayContainsFromImpliesContainsFromZero(_keys, key, index)) + ghostExpr( + lemmaValidKeyInArrayIsInListMap( + _keys, + _values, + mask, + extraKeys, + zeroValue, + minValue, + index, + defaultEntry + ) + ) + ghostExpr( + lemmaKeyInListMapThenSameValueInArray( + _keys, + _values, + mask, + extraKeys, + zeroValue, + minValue, + key, + index, + defaultEntry + ) + ) + _values(index).get(defaultEntry(0L)) + } + case _ => defaultEntry(key) + } + } + } ensuring (res => + valid + && (if (contains(key)) res == map.get(key).get + else res == defaultEntry(key)) + ) + + /** Updates the map to include a new key-value pair. Returns a boolean indicating if the update was successful. It is not successful if no free space is found (i.e., the map is full) + * + * This is the fastest way to add an entry to a `LongMap`. + */ + def update(key: Long, v: V): Boolean = { + require(valid) + if (key == -key) { + if (key == 0) { + ghostExpr( + lemmaChangeZeroKeyThenAddPairToListMap( + _keys, + _values, + mask, + extraKeys, + (extraKeys | 1), + zeroValue, + v, + minValue, + defaultEntry + ) + ) + zeroValue = v + extraKeys |= 1 + true + } else { + val extraKeysBefore = extraKeys + ghostExpr( + lemmaChangeLongMinValueKeyThenAddPairToListMap( + _keys, + _values, + mask, + extraKeys, + (extraKeys | 2), + zeroValue, + minValue, + v, + defaultEntry + ) + ) + minValue = v + extraKeys |= 2 + true + } + + } else { + val seekEntryRes = seekEntryOrOpen(key)(_keys, mask) + seekEntryRes match { + case Undefined() => { + // the key is not in the array, it was not able to find an empty space, the map is maybe full + ghostExpr( + if ( + getCurrentListMap( + _keys, + _values, + mask, + extraKeys, + zeroValue, + minValue, + 0, + defaultEntry + ).contains( + key + ) + ) { + lemmaInListMapThenSeekEntryOrOpenFindsIt( + _keys, + _values, + mask, + extraKeys, + zeroValue, + minValue, + key, + defaultEntry + ) + check(false) + } else { + lemmaNotInListMapThenSeekEntryOrOpenFindsFreeOrNothing( + _keys, + _values, + mask, + extraKeys, + zeroValue, + minValue, + key, + defaultEntry + ) + } + ) + false + } + case MissingVacant(index) => updateHelperNewKey(key, v, index) + case MissingZero(index) => updateHelperNewKey(key, v, index) + case Found(index) => { + ghostExpr( + if ( + getCurrentListMap( + _keys, + _values, + mask, + extraKeys, + zeroValue, + minValue, + 0, + defaultEntry + ).contains( + key + ) + ) { + lemmaInListMapThenSeekEntryOrOpenFindsIt( + _keys, + _values, + mask, + extraKeys, + zeroValue, + minValue, + key, + defaultEntry + ) + } else { + lemmaNotInListMapThenSeekEntryOrOpenFindsFreeOrNothing( + _keys, + _values, + mask, + extraKeys, + zeroValue, + minValue, + key, + defaultEntry + ) + check(false) + } + ) + + ghostExpr( + lemmaChangeValueExistingKeyToArrayThenAddPairToListMap( + _keys, + _values, + mask, + extraKeys, + zeroValue, + minValue, + index, + key, + v, + defaultEntry + ) + ) + + _values(index) = ValueCellFull(v) + + ghostExpr( + lemmaValidKeyInArrayIsInListMap( + _keys, + _values, + mask, + extraKeys, + zeroValue, + minValue, + index, + defaultEntry + ) + ) + true + } + } + } + } ensuring (res => valid && (if (res) map.contains(key) && (map == old(this).map + (key, v)) else map == old(this).map)) + + /** Removes the given key from the map. Returns true if the key was present and is removed, false if the key was not present + * + * @param key + * @return + */ + def remove(key: Long): Boolean = { + require(valid) + if (key == -key) { + if (key == 0L) { + ghostExpr( + lemmaRemoveZeroKeyThenRemoveKeyFromListMap( + _keys, + _values, + mask, + extraKeys, + extraKeys & 0x2, + zeroValue, + defaultEntry(0L), + minValue, + defaultEntry + ) + ) + extraKeys &= 0x2 + zeroValue = defaultEntry(0L) + + true + } else { + ghostExpr( + lemmaRemoveLongMinValueKeyThenRemoveKeyFromListMap( + _keys, + _values, + mask, + extraKeys, + extraKeys & 0x1, + zeroValue, + minValue, + defaultEntry(Long.MinValue), + defaultEntry + ) + ) + extraKeys &= 0x1 + minValue = defaultEntry(Long.MinValue) + + true + } + } else { + val seekEntryRes = seekEntry(key)(_keys, mask) + seekEntryRes match { + case Found(index) => { + // _vacant += 1 + ghostExpr(lemmaRemoveValidKeyDecreasesNumberOfValidKeysInArray(_keys, index, Long.MinValue)) + ghostExpr(lemmaPutNonValidKeyPreservesNoDuplicate(_keys, Long.MinValue, index, 0, List())) + ghostExpr(lemmaPutLongMinValuePreservesForallSeekEntryOrOpen(_keys, index)(mask)) + ghostExpr(lemmaArrayNoDuplicateRemoveOneThenNotContain(_keys, index, key)) + ghostExpr( + lemmaRemoveValidKeyToArrayThenRemoveKeyFromListMap( + _keys, + _values, + mask, + extraKeys, + zeroValue, + minValue, + index, + key, + defaultEntry + ) + ) + + _size -= 1 + _keys(index) = Long.MinValue + _values(index) = ValueCellFull(defaultEntry(0L)) + val tempVac = _vacant + 1 + if (tempVac > 0) { + _vacant = tempVac + } + + ghostExpr( + if ( + getCurrentListMap( + _keys, + _values, + mask, + extraKeys, + zeroValue, + minValue, + 0, + defaultEntry + ).contains( + key + ) + ) { + lemmaKeyInListMapIsInArray( + _keys, + _values, + mask, + extraKeys, + zeroValue, + minValue, + key, + defaultEntry + ) + check(false) + } else { () } + ) + + true + } + case _ => false + } + } + } ensuring (res => valid && (if (res) map == old(this).map - key else map == old(this).map)) + + /** Go through an helper function because this piece of code has to be called in 2 cases of the pattern matching + * + * @return + */ + private def updateHelperNewKey(key: Long, v: V, index: Int): Boolean = { + require(valid) + require(key != 0) + require(key != Long.MinValue) + require( + seekEntryOrOpen(key)(_keys, mask) == MissingZero( + index + ) || seekEntryOrOpen(key)( + _keys, + mask + ) == MissingVacant(index) + ) + + ghostExpr( + if ( + getCurrentListMap(_keys, _values, mask, extraKeys, zeroValue, minValue, 0, defaultEntry) + .contains(key) + ) { + ghostExpr( + lemmaInListMapThenSeekEntryOrOpenFindsIt( + _keys, + _values, + mask, + extraKeys, + zeroValue, + minValue, + key, + defaultEntry + ) + ) + check(false) + } else { + ghostExpr( + lemmaNotInListMapThenSeekEntryOrOpenFindsFreeOrNothing( + _keys, + _values, + mask, + extraKeys, + zeroValue, + minValue, + key, + defaultEntry + ) + ) + } + ) + assert(inRange(index, mask)) + ghostExpr(if (arrayContainsKey(_keys, key, 0)) { + + lemmaArrayContainsKeyThenInListMap( + _keys, + _values, + mask, + extraKeys, + zeroValue, + minValue, + key, + 0, + defaultEntry + ) + + check(false) + } else {}) + + ghostExpr(lemmaPutNewValidKeyPreservesNoDuplicate(_keys, key, index, 0, List())) + ghostExpr(lemmaAddValidKeyIncreasesNumberOfValidKeysInArray(_keys, index, key)) + ghostExpr(lemmaPutValidKeyPreservesForallSeekEntryOrOpen(key, _keys, index)(mask)) + + ghostExpr( + lemmaAddValidKeyToArrayThenAddPairToListMap( + _keys, + _values, + mask, + extraKeys, + zeroValue, + minValue, + index, + key, + v, + defaultEntry + ) + ) + + _keys(index) = key + _size += 1 + + ghostExpr(lemmaArrayContainsFromImpliesContainsFromZero(_keys, key, index)) + ghostExpr(lemmaValidKeyAtIImpliesCountKeysIsOne(_keys, index)) + + _values(index) = ValueCellFull(v) + + ghostExpr( + lemmaValidKeyInArrayIsInListMap( + _keys, + _values, + mask, + extraKeys, + zeroValue, + minValue, + index, + defaultEntry + ) + ) + true + + } ensuring (res => res && valid && map.contains(key) && (map == old(this).map + (key, v))) + + @ghost + def valid: Boolean = { + // class invariant + simpleValid && + arrayCountValidKeys(_keys, 0, _keys.length) == _size && + arrayForallSeekEntryOrOpenFound(0)(_keys, mask) && + arrayNoDuplicates(_keys, 0) + } + + @ghost + def simpleValid: Boolean = { + validMask(mask) && + _values.length == mask + 1 && + _keys.length == _values.length && + _size >= 0 && + _size <= mask + 1 && + size >= _size && + size == _size + (extraKeys + 1) / 2 && + extraKeys >= 0 && + extraKeys <= 3 && + _vacant >= 0 + } + + @pure + @ghost + def map: ListLongMap[V] = { + require(valid) + getCurrentListMap(_keys, _values, mask, extraKeys, zeroValue, minValue, 0, defaultEntry) + } + + } + + abstract sealed class SeekEntryResult + case class Found(index: Int) extends SeekEntryResult + case class MissingZero(index: Int) extends SeekEntryResult + case class MissingVacant(index: Int) extends SeekEntryResult + case class Intermediate(undefined: Boolean, index: Int, x: Int) extends SeekEntryResult + case class Undefined() extends SeekEntryResult + + object LongMapFixedSize { + + def getNewLongMapFixedSize[V](mask: Int, defaultEntry: Long => V): LongMapFixedSize[V] = { + require(validMask(mask)) + + val res = LongMapFixedSize[V]( + defaultEntry = defaultEntry, + mask = mask, + extraKeys = 0, + zeroValue = defaultEntry(0L), + minValue = defaultEntry(0L), + _size = 0, + _keys = Array.fill(mask + 1)(0L), + _values = Array.fill(mask + 1)(EmptyCell[V]()), + _vacant = 0 + ) + ghostExpr(LongMapFixedSize.lemmaArrayCountValidKeysOfFilled0ArrayIs0(res._keys, 0, mask + 1)) + + ghostExpr(LongMapFixedSize.lemmaArrayForallSeekEntryOrOpenFoundAlwaysTrueFor0Array(res._keys, mask, 0)) + + ghostExpr(LongMapFixedSize.lemmaArrayNoDuplicatesInAll0Array(res._keys, 0, mask + 1)) + ghostExpr(if (res.map != ListLongMap.empty[V]) { + assert(!res.map.toList.isEmpty) + val kv = res.map.toList.head + val k = kv._1 + LongMapFixedSize.lemmaKeyInListMapIsInArray(res._keys, res._values, mask, res.extraKeys, res.zeroValue, res.minValue, k, res.defaultEntry) + val index = arrayScanForKey(res._keys, k, 0) + check(false) + + } else { () }) + res + } ensuring (res => res.valid && res.mask == mask && res.map == ListLongMap.empty[V]) + + @pure + def validMask(mask: Int): Boolean = { + (mask == 0x00000007 || + mask == 0x0000000f || + mask == 0x0000001f || + mask == 0x0000003f || + mask == 0x0000007f || + mask == 0x000000ff || + mask == 0x000001ff || + mask == 0x000003ff || + mask == 0x000007ff || + mask == 0x00000fff || + mask == 0x00001fff || + mask == 0x00003fff || + mask == 0x00007fff || + mask == 0x0000ffff || + mask == 0x0001ffff || + mask == 0x0003ffff || + mask == 0x0007ffff || + mask == 0x000fffff || + mask == 0x001fffff || + mask == 0x003fffff || + mask == 0x007fffff || + mask == 0x00ffffff || + mask == 0x01ffffff || + mask == 0x03ffffff || + mask == 0x07ffffff || + mask == 0x0fffffff || + mask == 0x1fffffff || + mask == 0x3fffffff) && mask <= MAX_MASK // MAX is MAX_MASK + + } + + /** Checks if i is a valid index in the Array of values + * + * @param i + * @return + */ + private def inRange(i: Int, mask: Int): Boolean = { + // mask + 1 is the size of the Array + i >= 0 && i < mask + 1 + } + + @pure + @ghost + def arrayForallSeekEntryOrOpenFound(i: Int)(implicit _keys: Array[Long], mask: Int): Boolean = { + require(validMask(mask)) + require(_keys.length == mask + 1) + require(i >= 0) + require(i <= _keys.length) + + decreases(_keys.length - i) + + if (i >= _keys.length) true + else if (validKeyInArray(_keys(i))) { + lemmaArrayContainsFromImpliesContainsFromZero(_keys, _keys(i), i) + LongMapFixedSize.seekEntryOrOpen(_keys(i))(_keys, mask) == Found(i) && + arrayForallSeekEntryOrOpenFound(i + 1) + } else arrayForallSeekEntryOrOpenFound(i + 1) + } + + /** Compute the index in the array for a given key with hashing and magic stuff + * + * @param k + * the key + * @return + */ + @pure + private def toIndex(k: Long, mask: Int): Int = { + require(mask >= 0) + require(mask <= MAX_MASK) + // Part of the MurmurHash3 32 bit finalizer + val h = ((k ^ (k >>> 32)) & 0xffffffffL).toInt + val x = (h ^ (h >>> 16)) * 0x85ebca6b + (x ^ (x >>> 13)) & mask + } ensuring (res => res < mask + 1 && res >= 0) + + /** Given a key, seek for its index into the array returns a corresponding instance of SeekEntryResult with the index if found + * + * @param k + * the key + * @return + * the index of the given key into the array + */ + @pure + def seekEntry(k: Long)(implicit _keys: Array[Long], mask: Int): SeekEntryResult = { + require(validMask(mask)) + require(_keys.length == mask + 1) + require(validKeyInArray(k)) + decreases(1) + + val intermediate = + seekKeyOrZeroOrLongMinValue(0, toIndex(k, mask))(k, _keys, mask) + intermediate match { + case Intermediate(undefined, index, x) if (undefined) => Undefined() + case Intermediate(undefined, index, x) if (!undefined) => { + val q = _keys(index) + if (q == k) Found(index) + else if (q == 0) MissingZero(index) + else { + // e is the index of Long.MinValue i.e. the spot of a key that was removed + // we need to search from there until we see a zero. Maybe the key we're + // searching was added after the removed one and is therefore after in the array. + // If we find a zero before finding the key, we return the index of the Long.MinValue to + // reuse the spot + assert(_keys(index) == Long.MinValue) + assert(index >= 0 && index < mask + 1) + val res = seekKeyOrZeroReturnVacant(x, index, index)(k, _keys, mask) + res match { + case MissingVacant(index) => MissingZero(index) + case _ => res + } + } + } + } + + } ensuring (res => + res match { + case MissingVacant(index) => false // should never happen + case Found(index) => _keys(index) == k + case MissingZero(_) => true + case Undefined() => true + } + ) + + /** Search the index of the given key. If the key is in the array, it finds its index (OK is returned). If the key is not in the array, it finds either: + * - A free space with a 0 value (MissingBit is returned) + * - A freed space with a Long.MinValue value (MissingVacant is returned) + * - Nothing (EntryNotFound is returned as a second value) + * + * @param k + * @return + */ + @pure + def seekEntryOrOpen(k: Long)(implicit _keys: Array[Long], mask: Int): SeekEntryResult = { + require(validMask(mask)) + require(mask >= 0) + require(_keys.length == mask + 1) + + require(validKeyInArray(k)) + + val intermediate = + seekKeyOrZeroOrLongMinValue(0, toIndex(k, mask))(k, _keys, mask) + intermediate match { + case Intermediate(undefined, index, x) if (undefined) => Undefined() + case Intermediate(undefined, index, x) if (!undefined) => { + val q = _keys(index) + if (q == k) Found(index) + else if (q == 0) MissingZero(index) + else { + // index is the index of Long.MinValue i.e. the spot of a key that was removed + // we need to search from there until we see a zero. Maybe the key we're + // searching was added after the removed one and is therefore after in the array. + // If we find a zero before finding the key, we return the index of the Long.MinValue to + // reuse the spot + assert(_keys(index) == Long.MinValue) + assert(index >= 0 && index < mask + 1) + val res = seekKeyOrZeroReturnVacant(x, index, index)(k, _keys, mask) + res + } + } + } + } ensuring (res => + ( + res match { + case Undefined() => true + case Found(index) => _keys(index) == k + case MissingZero(index) => _keys(index) == 0 + case MissingVacant(index) => _keys(index) == Long.MinValue + case _ => false + } + ) + ) + + @opaque + @pure + private def nextIndex(ee: Int, x: Int, mask: Int): Int = { + require(validMask(mask)) + require(mask >= 0) + require(ee >= 0 && ee < mask + 1) + require(x <= MAX_ITER && x >= 0) + + (ee + 2 * (x + 1) * x - 3) & mask + } ensuring (res => res >= 0 && res < mask + 1) + + // @tailrec + @pure + private def seekKeyOrZeroOrLongMinValue(x: Int, ee: Int)(implicit + k: Long, + _keys: Array[Long], + mask: Int + ): SeekEntryResult = { + require(validMask(mask)) + require(mask >= 0) + require(_keys.length == mask + 1) + + require(ee >= 0 && ee < mask + 1) + require(x <= MAX_ITER && x >= 0) + require(validKeyInArray(k)) + + decreases(MAX_ITER - x) + val q = _keys(ee) + if (x >= MAX_ITER) Intermediate(true, ee, x) + else if (q == k || q + q == 0) Intermediate(false, ee, x) + else + seekKeyOrZeroOrLongMinValue(x + 1, nextIndex(ee, x, mask)) + } ensuring (res => + (res match { + case Intermediate(undefined, index, resx) if (undefined) => resx >= MAX_ITER + case Intermediate(undefined, index, resx) if (!undefined) => + resx < MAX_ITER && resx >= 0 && resx >= x && (_keys(index) == k || _keys( + index + ) == 0 || _keys( + index + ) == Long.MinValue) + case _ => false + }) + ) + + // @tailrec + @pure + private def seekKeyOrZeroReturnVacant(x: Int, ee: Int, vacantSpotIndex: Int)(implicit + k: Long, + _keys: Array[Long], + mask: Int + ): SeekEntryResult = { + require(validMask(mask)) + require(mask >= 0) + require(_keys.length == mask + 1) + + require(ee >= 0 && ee < mask + 1) + require(x <= MAX_ITER && x >= 0) + require(vacantSpotIndex >= 0 && vacantSpotIndex < mask + 1) + require(_keys(vacantSpotIndex) == Long.MinValue) + require(validKeyInArray(k)) + + decreases(MAX_ITER + 1 - x) + val q = _keys(ee) + if (x >= MAX_ITER) Undefined() + else if (q == k) Found(ee) + else if (q == 0) MissingVacant(vacantSpotIndex) + else + seekKeyOrZeroReturnVacant( + x + 1, + nextIndex(ee, x, mask), + vacantSpotIndex + ) + + } ensuring (res => + res match { + case Undefined() => true + case Found(index) => _keys(index) == k + case MissingVacant(index) => index == vacantSpotIndex && _keys(index) == Long.MinValue + case _ => false + } + ) + + @pure + @ghost + def getCurrentListMap[V]( + _keys: Array[Long], + _values: Array[ValueCell[V]], + mask: Int, + extraKeys: Int, + zeroValue: V, + minValue: V, + from: Int, + defaultEntry: Long => V + ): ListLongMap[V] = { + require(validMask(mask)) + require(_values.length == mask + 1) + require(_keys.length == _values.length) + require(mask >= 0) + require(extraKeys >= 0) + require(extraKeys <= 3) + require(arrayForallSeekEntryOrOpenFound(0)(_keys, mask)) + require(arrayNoDuplicates(_keys, 0)) + + require(from >= 0 && from <= _keys.length) + + val res = if ((extraKeys & 1) != 0 && (extraKeys & 2) != 0) { + // it means there is a mapping for the key 0 and the Long.MIN_VALUE + (getCurrentListMapNoExtraKeys(_keys, _values, mask, extraKeys, zeroValue, minValue, from, defaultEntry) + (0L, zeroValue)) + (Long.MinValue, minValue) + } else if ((extraKeys & 1) != 0 && (extraKeys & 2) == 0) { + // it means there is a mapping for the key 0 + getCurrentListMapNoExtraKeys(_keys, _values, mask, extraKeys, zeroValue, minValue, from, defaultEntry) + (0L, zeroValue) + } else if ((extraKeys & 2) != 0 && (extraKeys & 1) == 0) { + // it means there is a mapping for the key Long.MIN_VALUE + getCurrentListMapNoExtraKeys(_keys, _values, mask, extraKeys, zeroValue, minValue, from, defaultEntry) + (Long.MinValue, minValue) + } else { + getCurrentListMapNoExtraKeys(_keys, _values, mask, extraKeys, zeroValue, minValue, from, defaultEntry) + } + if (from < _keys.length && validKeyInArray(_keys(from))) { + ListLongMapLemmas.addStillContains(getCurrentListMapNoExtraKeys(_keys, _values, mask, extraKeys, zeroValue, minValue, from, defaultEntry), 0, zeroValue, _keys(from)) + ListLongMapLemmas.addApplyDifferent(getCurrentListMapNoExtraKeys(_keys, _values, mask, extraKeys, zeroValue, minValue, from, defaultEntry), Long.MinValue, minValue, _keys(from)) + ListLongMapLemmas.addApplyDifferent(getCurrentListMapNoExtraKeys(_keys, _values, mask, extraKeys, zeroValue, minValue, from, defaultEntry), 0, zeroValue, _keys(from)) + ListLongMapLemmas.addApplyDifferent(getCurrentListMapNoExtraKeys(_keys, _values, mask, extraKeys, zeroValue, minValue, from, defaultEntry), Long.MinValue, minValue, _keys(from)) + } + + res + + } ensuring (res => + (if (from < _keys.length && validKeyInArray(_keys(from))) + res.contains(_keys(from)) && res(_keys(from)) == _values(from).get(defaultEntry(0L)) + else + // else if (from < _keys.length) res == getCurrentListMap(from + 1) else + true) && + (if ((extraKeys & 1) != 0) res.contains(0) && res(0) == zeroValue else !res.contains(0)) && + (if ((extraKeys & 2) != 0) res.contains(Long.MinValue) && res(Long.MinValue) == minValue + else !res.contains(Long.MinValue)) + ) + + @ghost + @pure + def getCurrentListMapNoExtraKeys[V]( + _keys: Array[Long], + _values: Array[ValueCell[V]], + mask: Int, + extraKeys: Int, + zeroValue: V, + minValue: V, + from: Int, + defaultEntry: Long => V + ): ListLongMap[V] = { + require(validMask(mask)) + require(_values.length == mask + 1) + require(_keys.length == _values.length) + require(mask >= 0) + + require(extraKeys >= 0) + require(extraKeys <= 3) + require(arrayForallSeekEntryOrOpenFound(0)(_keys, mask)) + require(arrayNoDuplicates(_keys, 0)) + + require(from >= 0 && from <= _keys.length) + decreases(_keys.length + 1 - from) + if (from >= _keys.length) { + ListLongMap.empty[V] + } else if (validKeyInArray(_keys(from))) { + ListLongMapLemmas.addStillNotContains( + getCurrentListMapNoExtraKeys(_keys, _values, mask, extraKeys, zeroValue, minValue, from + 1, defaultEntry), + _keys(from), + _values(from).get(defaultEntry(0L)), + 0 + ) + + getCurrentListMapNoExtraKeys(_keys, _values, mask, extraKeys, zeroValue, minValue, from + 1, defaultEntry) + (_keys(from), _values(from).get(defaultEntry(0L))) + } else { + getCurrentListMapNoExtraKeys(_keys, _values, mask, extraKeys, zeroValue, minValue, from + 1, defaultEntry) + } + } ensuring (res => + !res.contains(0) && !res.contains(Long.MinValue) && + (if (from < _keys.length && validKeyInArray(_keys(from))) + res.contains(_keys(from)) && res(_keys(from)) == _values(from).get(defaultEntry(0L)) + else if (from < _keys.length) + res == getCurrentListMapNoExtraKeys(_keys, _values, mask, extraKeys, zeroValue, minValue, from + 1, defaultEntry) + else res.isEmpty) + ) + + // LEMMAS -----------------–-----------------–-----------------–-----------------–-----------------–--------------- + + @opaque + @inlineOnce + @pure + @ghost + def lemmaAddValidKeyToArrayThenAddPairToListMap[V]( + _keys: Array[Long], + _values: Array[ValueCell[V]], + mask: Int, + extraKeys: Int, + zeroValue: V, + minValue: V, + i: Int, + k: Long, + v: V, + defaultEntry: Long => V + ): Unit = { + require(validMask(mask)) + require(_values.length == mask + 1) + require(_keys.length == _values.length) + require(mask >= 0) + require(extraKeys >= 0) + require(extraKeys <= 3) + require(arrayForallSeekEntryOrOpenFound(0)(_keys, mask)) + require(arrayNoDuplicates(_keys, 0)) + + require(i >= 0 && i < _keys.length) + require(validKeyInArray(k)) + require(_keys(i) == Long.MinValue || _keys(i) == 0) + require(!arrayContainsKey(_keys, k, 0)) + require(arrayForallSeekEntryOrOpenFound(0)(_keys.updated(i, k), mask)) + require(arrayNoDuplicates(_keys.updated(i, k), 0)) + + val mapNoExtraKeysBefore = + getCurrentListMapNoExtraKeys( + _keys, + _values, + mask, + extraKeys, + zeroValue, + minValue, + 0, + defaultEntry + ) + val mapNoExtraKeysAfter = getCurrentListMapNoExtraKeys( + _keys.updated(i, k), + _values.updated(i, ValueCellFull(v)), + mask, + extraKeys, + zeroValue, + minValue, + 0, + defaultEntry + ) + + lemmaAddValidKeyToArrayThenMapNoExtrasAddPair( + _keys, + _values, + mask, + extraKeys, + zeroValue, + minValue, + i, + k, + v, + 0, + defaultEntry + ) + + check(mapNoExtraKeysBefore + (k, v) == mapNoExtraKeysAfter) + + val mapAfter = + getCurrentListMap( + _keys.updated(i, k), + _values.updated(i, ValueCellFull(v)), + mask, + extraKeys, + zeroValue, + minValue, + 0, + defaultEntry + ) + val mapBefore = + getCurrentListMap(_keys, _values, mask, extraKeys, zeroValue, minValue, 0, defaultEntry) + if ((extraKeys & 1) != 0 && (extraKeys & 2) != 0) { + // it means there is a mapping for the key 0 and the Long.MIN_VALUE + check(mapAfter == (mapNoExtraKeysAfter + (0L, zeroValue)) + (Long.MinValue, minValue)) + check(mapBefore == (mapNoExtraKeysBefore + (0L, zeroValue)) + (Long.MinValue, minValue)) + check( + mapAfter == ((mapNoExtraKeysBefore + (k, v)) + (0L, zeroValue)) + (Long.MinValue, minValue) + ) + ListLongMapLemmas.addCommutativeForDiffKeys(mapNoExtraKeysBefore, k, v, 0L, zeroValue) + ListLongMapLemmas.addCommutativeForDiffKeys( + mapNoExtraKeysBefore + (0L, zeroValue), + k, + v, + Long.MinValue, + minValue + ) + } else if ((extraKeys & 1) != 0 && (extraKeys & 2) == 0) { + // it means there is a mapping for the key 0 + check(mapAfter == mapNoExtraKeysAfter + (0L, zeroValue)) + check(mapBefore == (mapNoExtraKeysBefore + (0L, zeroValue))) + check(mapAfter == (mapNoExtraKeysBefore + (k, v)) + (0L, zeroValue)) + ListLongMapLemmas.addCommutativeForDiffKeys(mapNoExtraKeysBefore, k, v, 0L, zeroValue) + } else if ((extraKeys & 2) != 0 && (extraKeys & 1) == 0) { + // it means there is a mapping for the key Long.MIN_VALUE + check(mapAfter == mapNoExtraKeysAfter + (Long.MinValue, minValue)) + check(mapAfter == (mapNoExtraKeysBefore + (k, v)) + (Long.MinValue, minValue)) + check(mapBefore == (mapNoExtraKeysBefore + (Long.MinValue, minValue))) + ListLongMapLemmas.addCommutativeForDiffKeys( + mapNoExtraKeysBefore, + k, + v, + Long.MinValue, + minValue + ) + } else { + check(mapAfter == mapNoExtraKeysAfter) + check( + getCurrentListMap( + _keys, + _values, + mask, + extraKeys, + zeroValue, + minValue, + 0, + defaultEntry + ) + (k, v) == getCurrentListMap( + _keys.updated(i, k), + _values.updated(i, ValueCellFull(v)), + mask, + extraKeys, + zeroValue, + minValue, + 0, + defaultEntry + ) + ) + } + + } ensuring (_ => + getCurrentListMap( + _keys, + _values, + mask, + extraKeys, + zeroValue, + minValue, + 0, + defaultEntry + ) + (k, v) == getCurrentListMap( + _keys.updated(i, k), + _values.updated(i, ValueCellFull(v)), + mask, + extraKeys, + zeroValue, + minValue, + 0, + defaultEntry + ) + ) + + @opaque + @inlineOnce + @pure + @ghost + def lemmaAddValidKeyToArrayThenMapNoExtrasAddPair[V]( + _keys: Array[Long], + _values: Array[ValueCell[V]], + mask: Int, + extraKeys: Int, + zeroValue: V, + minValue: V, + i: Int, + k: Long, + v: V, + from: Int, + defaultEntry: Long => V + ): Unit = { + require(from >= 0 && from <= _keys.length) + require(validMask(mask)) + require(_values.length == mask + 1) + require(_keys.length == _values.length) + require(mask >= 0) + require(extraKeys >= 0) + require(extraKeys <= 3) + require(arrayForallSeekEntryOrOpenFound(0)(_keys, mask)) + require(arrayNoDuplicates(_keys, 0)) + + require(i >= 0 && i < _keys.length) + require(validKeyInArray(k)) + require(_keys(i) == Long.MinValue || _keys(i) == 0) + require(!arrayContainsKey(_keys, k, 0)) + require(arrayForallSeekEntryOrOpenFound(0)(_keys.updated(i, k), mask)) + require(arrayNoDuplicates(_keys.updated(i, k), 0)) + + require(i >= 0 && i < _keys.length) + require(validKeyInArray(k)) + require(_keys(i) == Long.MinValue || _keys(i) == 0) + + decreases(_keys.length - from) + + if (from > i) { + if (from + 1 <= _keys.length) { + lemmaAddValidKeyToArrayThenMapNoExtrasAddPair( + _keys, + _values, + mask, + extraKeys, + zeroValue, + minValue, + i, + k, + v, + from + 1, + defaultEntry + ) + } + + } else { + assert(from <= i) + val listmapNoExtrasBefore = + getCurrentListMapNoExtraKeys( + _keys, + _values, + mask, + extraKeys, + zeroValue, + minValue, + from, + defaultEntry + ) + val listmapNoExtrasAfter = getCurrentListMapNoExtraKeys( + _keys.updated(i, k), + _values.updated(i, ValueCellFull(v)), + mask, + extraKeys, + zeroValue, + minValue, + from, + defaultEntry + ) + if (from == i) { + lemmaAddValidKeyToArrayThenMapNoExtrasAddPair( + _keys, + _values, + mask, + extraKeys, + zeroValue, + minValue, + i, + k, + v, + from + 1, + defaultEntry + ) + assert(!validKeyInArray(_keys(from))) + check( + listmapNoExtrasAfter == + getCurrentListMapNoExtraKeys( + _keys.updated(i, k), + _values.updated(i, ValueCellFull(v)), + mask, + extraKeys, + zeroValue, + minValue, + from + 1, + defaultEntry + ) + (_keys.updated(i, k).apply(from), _values + .updated(i, ValueCellFull(v)) + .apply(from) + .get(defaultEntry(0L))) + ) + + ListLongMapLemmas.addSameAsAddTwiceSameKeyDiffValues( + getCurrentListMapNoExtraKeys( + _keys.updated(i, k), + _values.updated(i, ValueCellFull(v)), + mask, + extraKeys, + zeroValue, + minValue, + from + 1, + defaultEntry + ), + k, + _values(from).get(defaultEntry(0L)), + _values.updated(i, ValueCellFull(v)).apply(from).get(defaultEntry(0L)) + ) + + } else { + assert(from < i) + lemmaAddValidKeyToArrayThenMapNoExtrasAddPair( + _keys, + _values, + mask, + extraKeys, + zeroValue, + minValue, + i, + k, + v, + from + 1, + defaultEntry + ) + if (validKeyInArray(_keys(from))) { + check( + getCurrentListMapNoExtraKeys( + _keys.updated(i, k), + _values.updated(i, ValueCellFull(v)), + mask, + extraKeys, + zeroValue, + minValue, + from, + defaultEntry + ) == + ((getCurrentListMapNoExtraKeys( + _keys, + _values, + mask, + extraKeys, + zeroValue, + minValue, + from + 1, + defaultEntry + ) + (k, v) + (_keys(from), _values(from).get(defaultEntry(0L))))) + ) + + if (_keys(from) == k) { + lemmaArrayContainsFromImpliesContainsFromSmaller(_keys, k, from, 0) + check(false) + } + + check(_keys(from) != k) + + ListLongMapLemmas.addCommutativeForDiffKeys( + getCurrentListMapNoExtraKeys( + _keys, + _values, + mask, + extraKeys, + zeroValue, + minValue, + from + 1, + defaultEntry + ), + k, + v, + _keys(from), + _values(from).get(defaultEntry(0L)) + ) + } + } + } + + } ensuring (_ => + if (from <= i) + getCurrentListMapNoExtraKeys( + _keys.updated(i, k), + _values.updated(i, ValueCellFull(v)), + mask, + extraKeys, + zeroValue, + minValue, + from, + defaultEntry + ) == + (getCurrentListMapNoExtraKeys( + _keys, + _values, + mask, + extraKeys, + zeroValue, + minValue, + from, + defaultEntry + ) + (k, v)) + else + getCurrentListMapNoExtraKeys( + _keys.updated(i, k), + _values.updated(i, ValueCellFull(v)), + mask, + extraKeys, + zeroValue, + minValue, + from, + defaultEntry + ) == + getCurrentListMapNoExtraKeys( + _keys, + _values, + mask, + extraKeys, + zeroValue, + minValue, + from, + defaultEntry + ) + ) + + @opaque + @inlineOnce + @pure + @ghost + def lemmaChangeValueExistingKeyToArrayThenAddPairToListMap[V]( + _keys: Array[Long], + _values: Array[ValueCell[V]], + mask: Int, + extraKeys: Int, + zeroValue: V, + minValue: V, + i: Int, + k: Long, + v: V, + defaultEntry: Long => V + ): Unit = { + require(validMask(mask)) + require(_values.length == mask + 1) + require(_keys.length == _values.length) + require(mask >= 0) + require(extraKeys >= 0) + require(extraKeys <= 3) + require(arrayForallSeekEntryOrOpenFound(0)(_keys, mask)) + require(arrayNoDuplicates(_keys, 0)) + + require(i >= 0 && i < _keys.length) + require(validKeyInArray(k)) + require(_keys(i) == k) + + val mapNoExtraKeysBefore = + getCurrentListMapNoExtraKeys( + _keys, + _values, + mask, + extraKeys, + zeroValue, + minValue, + 0, + defaultEntry + ) + val mapNoExtraKeysAfter = + getCurrentListMapNoExtraKeys( + _keys, + _values.updated(i, ValueCellFull(v)), + mask, + extraKeys, + zeroValue, + minValue, + 0, + defaultEntry + ) + + lemmaChangeValueExistingKeyToArrayThenMapNoExtrasAddPair( + _keys, + _values, + mask, + extraKeys, + zeroValue, + minValue, + i, + k, + v, + 0, + defaultEntry + ) + + check(mapNoExtraKeysBefore + (k, v) == mapNoExtraKeysAfter) + + val mapAfter = + getCurrentListMap( + _keys, + _values.updated(i, ValueCellFull(v)), + mask, + extraKeys, + zeroValue, + minValue, + 0, + defaultEntry + ) + val mapBefore = + getCurrentListMap(_keys, _values, mask, extraKeys, zeroValue, minValue, 0, defaultEntry) + if ((extraKeys & 1) != 0 && (extraKeys & 2) != 0) { + // it means there is a mapping for the key 0 and the Long.MIN_VALUE + check(mapAfter == (mapNoExtraKeysAfter + (0L, zeroValue)) + (Long.MinValue, minValue)) + check(mapBefore == (mapNoExtraKeysBefore + (0L, zeroValue)) + (Long.MinValue, minValue)) + check( + mapAfter == ((mapNoExtraKeysBefore + (k, v)) + (0L, zeroValue)) + (Long.MinValue, minValue) + ) + ListLongMapLemmas.addCommutativeForDiffKeys(mapNoExtraKeysBefore, k, v, 0L, zeroValue) + ListLongMapLemmas.addCommutativeForDiffKeys( + mapNoExtraKeysBefore + (0L, zeroValue), + k, + v, + Long.MinValue, + minValue + ) + } else if ((extraKeys & 1) != 0 && (extraKeys & 2) == 0) { + // it means there is a mapping for the key 0 + check(mapAfter == mapNoExtraKeysAfter + (0L, zeroValue)) + check(mapBefore == (mapNoExtraKeysBefore + (0L, zeroValue))) + check(mapAfter == (mapNoExtraKeysBefore + (k, v)) + (0L, zeroValue)) + ListLongMapLemmas.addCommutativeForDiffKeys(mapNoExtraKeysBefore, k, v, 0L, zeroValue) + } else if ((extraKeys & 2) != 0 && (extraKeys & 1) == 0) { + // it means there is a mapping for the key Long.MIN_VALUE + check(mapAfter == mapNoExtraKeysAfter + (Long.MinValue, minValue)) + check(mapAfter == (mapNoExtraKeysBefore + (k, v)) + (Long.MinValue, minValue)) + check(mapBefore == (mapNoExtraKeysBefore + (Long.MinValue, minValue))) + ListLongMapLemmas.addCommutativeForDiffKeys( + mapNoExtraKeysBefore, + k, + v, + Long.MinValue, + minValue + ) + } else { + check(mapAfter == mapNoExtraKeysAfter) + } + + } ensuring (_ => + getCurrentListMap( + _keys, + _values, + mask, + extraKeys, + zeroValue, + minValue, + 0, + defaultEntry + ) + (k, v) == getCurrentListMap( + _keys, + _values.updated(i, ValueCellFull(v)), + mask, + extraKeys, + zeroValue, + minValue, + 0, + defaultEntry + ) + ) + + @opaque + @inlineOnce + @pure + @ghost + def lemmaChangeValueExistingKeyToArrayThenMapNoExtrasAddPair[V]( + _keys: Array[Long], + _values: Array[ValueCell[V]], + mask: Int, + extraKeys: Int, + zeroValue: V, + minValue: V, + i: Int, + k: Long, + v: V, + from: Int, + defaultEntry: Long => V + ): Unit = { + require(from >= 0 && from <= _keys.length) + require(validMask(mask)) + require(_values.length == mask + 1) + require(_keys.length == _values.length) + require(mask >= 0) + require(extraKeys >= 0) + require(extraKeys <= 3) + require(arrayForallSeekEntryOrOpenFound(0)(_keys, mask)) + require(arrayNoDuplicates(_keys, 0)) + + require(i >= 0 && i < _keys.length) + require(validKeyInArray(k)) + require(_keys(i) == k) + + decreases(_keys.length - from) + + if (from > i) { + if (from + 1 <= _keys.length) { + lemmaChangeValueExistingKeyToArrayThenMapNoExtrasAddPair( + _keys, + _values, + mask, + extraKeys, + zeroValue, + minValue, + i, + k, + v, + from + 1, + defaultEntry + ) + } + } else { + assert(from <= i) + val listmapNoExtrasBefore = + getCurrentListMapNoExtraKeys( + _keys, + _values, + mask, + extraKeys, + zeroValue, + minValue, + from, + defaultEntry + ) + val listmapNoExtrasAfter = + getCurrentListMapNoExtraKeys( + _keys, + _values.updated(i, ValueCellFull(v)), + mask, + extraKeys, + zeroValue, + minValue, + from, + defaultEntry + ) + if (from == i) { + lemmaChangeValueExistingKeyToArrayThenMapNoExtrasAddPair( + _keys, + _values, + mask, + extraKeys, + zeroValue, + minValue, + i, + k, + v, + from + 1, + defaultEntry + ) + assert(validKeyInArray(_keys(from))) + check( + listmapNoExtrasAfter == + getCurrentListMapNoExtraKeys( + _keys, + _values.updated(i, ValueCellFull(v)), + mask, + extraKeys, + zeroValue, + minValue, + from + 1, + defaultEntry + ) + (_keys(from), _values + .updated(i, ValueCellFull(v)) + .apply(from) + .get(defaultEntry(0L))) + ) + + ListLongMapLemmas.addSameAsAddTwiceSameKeyDiffValues( + getCurrentListMapNoExtraKeys( + _keys, + _values.updated(i, ValueCellFull(v)), + mask, + extraKeys, + zeroValue, + minValue, + from + 1, + defaultEntry + ), + k, + _values(from).get(defaultEntry(0L)), + _values.updated(i, ValueCellFull(v)).apply(from).get(defaultEntry(0L)) + ) + + } else { + assert(from < i) + lemmaChangeValueExistingKeyToArrayThenMapNoExtrasAddPair( + _keys, + _values, + mask, + extraKeys, + zeroValue, + minValue, + i, + k, + v, + from + 1, + defaultEntry + ) + + if (validKeyInArray(_keys(from))) { + check( + getCurrentListMapNoExtraKeys( + _keys, + _values.updated(i, ValueCellFull(v)), + mask, + extraKeys, + zeroValue, + minValue, + from, + defaultEntry + ) == + ((getCurrentListMapNoExtraKeys( + _keys, + _values, + mask, + extraKeys, + zeroValue, + minValue, + from + 1, + defaultEntry + ) + (k, v) + (_keys(from), _values(from).get(defaultEntry(0L))))) + ) + + if (_keys(from) == k) { + lemmaNoDuplicateFromThenFromBigger(_keys, 0, from) + lemmaArrayContainsFromImpliesContainsFromSmaller(_keys, k, i, from + 1) + lemmaArrayNoDuplicateThenKeysContainedNotEqual(_keys, k, from, Nil()) + check(false) + } + check(_keys(from) != k) + + ListLongMapLemmas.addCommutativeForDiffKeys( + getCurrentListMapNoExtraKeys( + _keys, + _values, + mask, + extraKeys, + zeroValue, + minValue, + from + 1, + defaultEntry + ), + k, + v, + _keys(from), + _values(from).get(defaultEntry(0L)) + ) + } + } + } + + } ensuring (_ => + if (from <= i) + getCurrentListMapNoExtraKeys( + _keys, + _values.updated(i, ValueCellFull(v)), + mask, + extraKeys, + zeroValue, + minValue, + from, + defaultEntry + ) == + (getCurrentListMapNoExtraKeys( + _keys, + _values, + mask, + extraKeys, + zeroValue, + minValue, + from, + defaultEntry + ) + (k, v)) + else + getCurrentListMapNoExtraKeys( + _keys, + _values.updated(i, ValueCellFull(v)), + mask, + extraKeys, + zeroValue, + minValue, + from, + defaultEntry + ) == + getCurrentListMapNoExtraKeys( + _keys, + _values, + mask, + extraKeys, + zeroValue, + minValue, + from, + defaultEntry + ) + ) + + @opaque + @inlineOnce + @pure + @ghost + def lemmaChangeZeroKeyThenAddPairToListMap[V]( + _keys: Array[Long], + _values: Array[ValueCell[V]], + mask: Int, + extraKeysBefore: Int, + extraKeysAfter: Int, + zeroValueBefore: V, + zeroValueAfter: V, + minValue: V, + defaultEntry: Long => V + ): Unit = { + require(validMask(mask)) + require(_values.length == mask + 1) + require(_keys.length == _values.length) + require(mask >= 0) + require(extraKeysBefore >= 0) + require(extraKeysBefore <= 3) + require(extraKeysAfter >= 0) + require(extraKeysAfter <= 3) + require((extraKeysBefore & 2) == (extraKeysAfter & 2)) // Long.MinValue key does not change + require((extraKeysAfter & 1) != 0) // 0 key must be defined after + require(arrayForallSeekEntryOrOpenFound(0)(_keys, mask)) + require(arrayNoDuplicates(_keys, 0)) + + val mapNoExtraKeysBefore = + getCurrentListMapNoExtraKeys( + _keys, + _values, + mask, + extraKeysBefore, + zeroValueBefore, + minValue, + 0, + defaultEntry + ) + val mapNoExtraKeysAfter = + getCurrentListMapNoExtraKeys( + _keys, + _values, + mask, + extraKeysAfter, + zeroValueAfter, + minValue, + 0, + defaultEntry + ) + + lemmaNoChangeToArrayThenSameMapNoExtras( + _keys, + _values, + mask, + extraKeysBefore, + extraKeysAfter, + zeroValueBefore, + zeroValueAfter, + minValue, + minValue, + 0, + defaultEntry + ) + assert(mapNoExtraKeysBefore == mapNoExtraKeysAfter) + + if ((extraKeysBefore & 2) == 0) { + // key Long.MinValue not defined + if ((extraKeysBefore & 1) == 0) { + check( + getCurrentListMap( + _keys, + _values, + mask, + extraKeysBefore, + zeroValueBefore, + minValue, + 0, + defaultEntry + ) + (0, zeroValueAfter) == getCurrentListMap( + _keys, + _values, + mask, + extraKeysAfter, + zeroValueAfter, + minValue, + 0, + defaultEntry + ) + ) + } else { + val mapBefore = + getCurrentListMap( + _keys, + _values, + mask, + extraKeysBefore, + zeroValueBefore, + minValue, + 0, + defaultEntry + ) + val mapAfter = + getCurrentListMap( + _keys, + _values, + mask, + extraKeysAfter, + zeroValueAfter, + minValue, + 0, + defaultEntry + ) + + check(mapBefore == (mapNoExtraKeysBefore + (0L, zeroValueBefore))) + check(mapAfter == (mapNoExtraKeysAfter + (0L, zeroValueAfter))) + + ListLongMapLemmas.addSameAsAddTwiceSameKeyDiffValues( + mapNoExtraKeysBefore, + 0L, + zeroValueBefore, + zeroValueAfter + ) + check(mapAfter == (mapNoExtraKeysAfter + (0L, zeroValueBefore)) + (0L, zeroValueAfter)) + } + + } else { + // key Long.MinValue defined + if ((extraKeysBefore & 1) == 0) { + val mapBefore = + getCurrentListMap( + _keys, + _values, + mask, + extraKeysBefore, + zeroValueBefore, + minValue, + 0, + defaultEntry + ) + val mapAfter = + getCurrentListMap( + _keys, + _values, + mask, + extraKeysAfter, + zeroValueAfter, + minValue, + 0, + defaultEntry + ) + + check(mapBefore == (mapNoExtraKeysBefore + (Long.MinValue, minValue))) + check( + mapAfter == ((mapNoExtraKeysBefore + (0L, zeroValueAfter)) + (Long.MinValue, minValue)) + ) + ListLongMapLemmas.addCommutativeForDiffKeys( + mapNoExtraKeysBefore, + 0L, + zeroValueAfter, + Long.MinValue, + minValue + ) + } else { + val mapBefore = + getCurrentListMap( + _keys, + _values, + mask, + extraKeysBefore, + zeroValueBefore, + minValue, + 0, + defaultEntry + ) + val mapAfter = + getCurrentListMap( + _keys, + _values, + mask, + extraKeysAfter, + zeroValueAfter, + minValue, + 0, + defaultEntry + ) + + check( + mapBefore == (mapNoExtraKeysBefore + (0L, zeroValueBefore)) + (Long.MinValue, minValue) + ) + check( + mapAfter == (mapNoExtraKeysAfter + (0L, zeroValueAfter)) + (Long.MinValue, minValue) + ) + + check( + mapBefore == (mapNoExtraKeysBefore + (Long.MinValue, minValue)) + (0L, zeroValueBefore) + ) + check( + mapAfter == (mapNoExtraKeysAfter + (Long.MinValue, minValue)) + (0L, zeroValueAfter) + ) + ListLongMapLemmas.addSameAsAddTwiceSameKeyDiffValues( + (mapNoExtraKeysBefore + (Long.MinValue, minValue)), + 0L, + zeroValueBefore, + zeroValueAfter + ) + check( + mapAfter == ((mapNoExtraKeysBefore + (Long.MinValue, minValue)) + (0L, zeroValueBefore)) + (0L, zeroValueAfter) + ) + check( + mapAfter == (mapNoExtraKeysBefore + (Long.MinValue, minValue)) + (0L, zeroValueAfter) + ) + check( + mapAfter == (mapNoExtraKeysBefore + (0L, zeroValueAfter)) + (Long.MinValue, minValue) + ) + } + } + + } ensuring (_ => + getCurrentListMap( + _keys, + _values, + mask, + extraKeysBefore, + zeroValueBefore, + minValue, + 0, + defaultEntry + ) + (0, zeroValueAfter) == getCurrentListMap( + _keys, + _values, + mask, + extraKeysAfter, + zeroValueAfter, + minValue, + 0, + defaultEntry + ) + ) + + @opaque + @inlineOnce + @pure + @ghost + def lemmaChangeLongMinValueKeyThenAddPairToListMap[V]( + _keys: Array[Long], + _values: Array[ValueCell[V]], + mask: Int, + extraKeysBefore: Int, + extraKeysAfter: Int, + zeroValue: V, + minValueBefore: V, + minValueAfter: V, + defaultEntry: Long => V + ): Unit = { + require(validMask(mask)) + require(_values.length == mask + 1) + require(_keys.length == _values.length) + require(mask >= 0) + require(extraKeysBefore >= 0) + require(extraKeysBefore <= 3) + require(extraKeysAfter >= 0) + require(extraKeysAfter <= 3) + require((extraKeysBefore & 1) == (extraKeysAfter & 1)) // zero key does not change + require((extraKeysAfter & 2) != 0) // MinValue key must be defined after + require(arrayForallSeekEntryOrOpenFound(0)(_keys, mask)) + require(arrayNoDuplicates(_keys, 0)) + + val mapNoExtraKeysBefore = + getCurrentListMapNoExtraKeys( + _keys, + _values, + mask, + extraKeysBefore, + zeroValue, + minValueBefore, + 0, + defaultEntry + ) + val mapNoExtraKeysAfter = + getCurrentListMapNoExtraKeys( + _keys, + _values, + mask, + extraKeysAfter, + zeroValue, + minValueAfter, + 0, + defaultEntry + ) + + lemmaNoChangeToArrayThenSameMapNoExtras( + _keys, + _values, + mask, + extraKeysBefore, + extraKeysAfter, + zeroValue, + zeroValue, + minValueBefore, + minValueAfter, + 0, + defaultEntry + ) + + assert(mapNoExtraKeysBefore == mapNoExtraKeysAfter) + + if ((extraKeysBefore & 1) == 0) { + // key 0 not defined + if ((extraKeysBefore & 2) == 0) { + check( + getCurrentListMap( + _keys, + _values, + mask, + extraKeysBefore, + zeroValue, + minValueBefore, + 0, + defaultEntry + ) + (Long.MinValue, minValueAfter) == getCurrentListMap( + _keys, + _values, + mask, + extraKeysAfter, + zeroValue, + minValueAfter, + 0, + defaultEntry + ) + ) + } else { + val mapBefore = + getCurrentListMap( + _keys, + _values, + mask, + extraKeysBefore, + zeroValue, + minValueBefore, + 0, + defaultEntry + ) + val mapAfter = + getCurrentListMap( + _keys, + _values, + mask, + extraKeysAfter, + zeroValue, + minValueAfter, + 0, + defaultEntry + ) + + ListLongMapLemmas.addSameAsAddTwiceSameKeyDiffValues( + mapNoExtraKeysBefore, + Long.MinValue, + minValueBefore, + minValueAfter + ) + } + + } else { + // key 0 defined + if ((extraKeysBefore & 2) == 0) { + // trivial + } else { + val mapBefore = + getCurrentListMap( + _keys, + _values, + mask, + extraKeysBefore, + zeroValue, + minValueBefore, + 0, + defaultEntry + ) + val mapAfter = + getCurrentListMap( + _keys, + _values, + mask, + extraKeysAfter, + zeroValue, + minValueAfter, + 0, + defaultEntry + ) + + ListLongMapLemmas.addSameAsAddTwiceSameKeyDiffValues( + mapNoExtraKeysBefore + (0L, zeroValue), + Long.MinValue, + minValueBefore, + minValueAfter + ) + } + } + + } ensuring (_ => + getCurrentListMap( + _keys, + _values, + mask, + extraKeysBefore, + zeroValue, + minValueBefore, + 0, + defaultEntry + ) + (Long.MinValue, minValueAfter) == getCurrentListMap( + _keys, + _values, + mask, + extraKeysAfter, + zeroValue, + minValueAfter, + 0, + defaultEntry + ) + ) + + @opaque + @inlineOnce + @pure + @ghost + def lemmaRemoveValidKeyToArrayThenRemoveKeyFromListMap[V]( + _keys: Array[Long], + _values: Array[ValueCell[V]], + mask: Int, + extraKeys: Int, + zeroValue: V, + minValue: V, + i: Int, + k: Long, + defaultEntry: Long => V + ): Unit = { + require(validMask(mask)) + require(_values.length == mask + 1) + require(_keys.length == _values.length) + require(mask >= 0) + require(extraKeys >= 0) + require(extraKeys <= 3) + require(arrayForallSeekEntryOrOpenFound(0)(_keys, mask)) + require(arrayNoDuplicates(_keys, 0)) + + require(i >= 0 && i < _keys.length) + require(validKeyInArray(k)) + require(_keys(i) == k) + require(arrayForallSeekEntryOrOpenFound(0)(_keys.updated(i, Long.MinValue), mask)) + require(arrayNoDuplicates(_keys.updated(i, Long.MinValue), 0)) + + lemmaArrayContainsFromImpliesContainsFromZero(_keys, k, i) + assert(arrayContainsKey(_keys, k, 0)) + + val mapNoExtraKeysBefore = + getCurrentListMapNoExtraKeys( + _keys, + _values, + mask, + extraKeys, + zeroValue, + minValue, + 0, + defaultEntry + ) + val mapNoExtraKeysAfter = getCurrentListMapNoExtraKeys( + _keys.updated(i, Long.MinValue), + _values.updated(i, ValueCellFull(defaultEntry(0L))), + mask, + extraKeys, + zeroValue, + minValue, + 0, + defaultEntry + ) + + lemmaRemoveValidKeyFromArrayThenMapNoExtrasRemoveKey( + _keys, + _values, + mask, + extraKeys, + zeroValue, + minValue, + i, + k, + 0, + defaultEntry + ) + + check(mapNoExtraKeysBefore - k == mapNoExtraKeysAfter) + + val mapAfter = + getCurrentListMap( + _keys.updated(i, Long.MinValue), + _values.updated(i, ValueCellFull(defaultEntry(0L))), + mask, + extraKeys, + zeroValue, + minValue, + 0, + defaultEntry + ) + val mapBefore = + getCurrentListMap(_keys, _values, mask, extraKeys, zeroValue, minValue, 0, defaultEntry) + if ((extraKeys & 1) != 0 && (extraKeys & 2) != 0) { + // it means there is a mapping for the key 0 and the Long.MIN_VALUE + check(mapAfter == (mapNoExtraKeysAfter + (0L, zeroValue)) + (Long.MinValue, minValue)) + check(mapBefore == (mapNoExtraKeysBefore + (0L, zeroValue)) + (Long.MinValue, minValue)) + check( + mapAfter == ((mapNoExtraKeysBefore - k) + (0L, zeroValue)) + (Long.MinValue, minValue) + ) + + ListLongMapLemmas.addRemoveCommutativeForDiffKeys(mapNoExtraKeysBefore, 0L, zeroValue, k) + ListLongMapLemmas.addRemoveCommutativeForDiffKeys( + mapNoExtraKeysBefore + (0L, zeroValue), + Long.MinValue, + minValue, + k + ) + } else if ((extraKeys & 1) != 0 && (extraKeys & 2) == 0) { + // it means there is a mapping for the key 0 + check(mapAfter == mapNoExtraKeysAfter + (0L, zeroValue)) + check(mapBefore == (mapNoExtraKeysBefore + (0L, zeroValue))) + check(mapAfter == (mapNoExtraKeysBefore - k) + (0L, zeroValue)) + + ListLongMapLemmas.addRemoveCommutativeForDiffKeys(mapNoExtraKeysBefore, 0L, zeroValue, k) + + } else if ((extraKeys & 2) != 0 && (extraKeys & 1) == 0) { + // it means there is a mapping for the key Long.MIN_VALUE + check(mapAfter == mapNoExtraKeysAfter + (Long.MinValue, minValue)) + check(mapAfter == (mapNoExtraKeysBefore - k) + (Long.MinValue, minValue)) + check(mapBefore == (mapNoExtraKeysBefore + (Long.MinValue, minValue))) + + ListLongMapLemmas.addRemoveCommutativeForDiffKeys( + mapNoExtraKeysBefore, + Long.MinValue, + minValue, + k + ) + } else { + check(mapAfter == mapNoExtraKeysAfter) + + } + + } ensuring (_ => + getCurrentListMap( + _keys, + _values, + mask, + extraKeys, + zeroValue, + minValue, + 0, + defaultEntry + ) - k == getCurrentListMap( + _keys.updated(i, Long.MinValue), + _values.updated(i, ValueCellFull(defaultEntry(0L))), + mask, + extraKeys, + zeroValue, + minValue, + 0, + defaultEntry + ) + ) + + @opaque + @inlineOnce + @pure + @ghost + def lemmaRemoveLongMinValueKeyThenRemoveKeyFromListMap[V]( + _keys: Array[Long], + _values: Array[ValueCell[V]], + mask: Int, + extraKeysBefore: Int, + extraKeysAfter: Int, + zeroValue: V, + minValueBefore: V, + minValueAfter: V, + defaultEntry: Long => V + ): Unit = { + require(validMask(mask)) + require(_values.length == mask + 1) + require(_keys.length == _values.length) + require(mask >= 0) + require(extraKeysBefore >= 0) + require(extraKeysBefore <= 3) + require(extraKeysAfter >= 0) + require(extraKeysAfter <= 3) + require((extraKeysBefore & 1) == (extraKeysAfter & 1)) // zero key does not change + require((extraKeysAfter & 2) == 0) // MinValue key must be removed after + require(arrayForallSeekEntryOrOpenFound(0)(_keys, mask)) + require(arrayNoDuplicates(_keys, 0)) + + val mapNoExtraKeysBefore = + getCurrentListMapNoExtraKeys( + _keys, + _values, + mask, + extraKeysBefore, + zeroValue, + minValueBefore, + 0, + defaultEntry + ) + val mapNoExtraKeysAfter = + getCurrentListMapNoExtraKeys( + _keys, + _values, + mask, + extraKeysAfter, + zeroValue, + minValueAfter, + 0, + defaultEntry + ) + + lemmaNoChangeToArrayThenSameMapNoExtras( + _keys, + _values, + mask, + extraKeysBefore, + extraKeysAfter, + zeroValue, + zeroValue, + minValueBefore, + minValueAfter, + 0, + defaultEntry + ) + + assert(mapNoExtraKeysBefore == mapNoExtraKeysAfter) + + if ((extraKeysBefore & 1) == 0) { + // key 0 not defined + if ((extraKeysBefore & 2) == 0) { + ListLongMapLemmas.removeNotPresentStillSame( + getCurrentListMap( + _keys, + _values, + mask, + extraKeysBefore, + zeroValue, + minValueBefore, + 0, + defaultEntry + ), + Long.MinValue + ) + } else { + val mapBefore = + getCurrentListMap( + _keys, + _values, + mask, + extraKeysBefore, + zeroValue, + minValueBefore, + 0, + defaultEntry + ) + val mapAfter = + getCurrentListMap( + _keys, + _values, + mask, + extraKeysAfter, + zeroValue, + minValueAfter, + 0, + defaultEntry + ) + + check(mapBefore == mapNoExtraKeysBefore + (Long.MinValue, minValueBefore)) + check(mapAfter == mapNoExtraKeysAfter) + ListLongMapLemmas.addThenRemoveForNewKeyIsSame( + mapNoExtraKeysBefore, + Long.MinValue, + minValueBefore + ) + check(mapBefore - Long.MinValue == mapAfter) + } + + } else { + // key 0 defined + if ((extraKeysBefore & 2) == 0) { + ListLongMapLemmas.removeNotPresentStillSame( + getCurrentListMap( + _keys, + _values, + mask, + extraKeysBefore, + zeroValue, + minValueBefore, + 0, + defaultEntry + ), + Long.MinValue + ) + } else { + val mapBefore = + getCurrentListMap( + _keys, + _values, + mask, + extraKeysBefore, + zeroValue, + minValueBefore, + 0, + defaultEntry + ) + val mapAfter = + getCurrentListMap( + _keys, + _values, + mask, + extraKeysAfter, + zeroValue, + minValueAfter, + 0, + defaultEntry + ) + + check( + mapBefore == (mapNoExtraKeysBefore + (0L, zeroValue)) + (Long.MinValue, minValueBefore) + ) + check(mapAfter == (mapNoExtraKeysAfter + (0L, zeroValue))) + ListLongMapLemmas.addThenRemoveForNewKeyIsSame( + mapNoExtraKeysBefore + (0L, zeroValue), + Long.MinValue, + minValueBefore + ) + check(mapBefore - Long.MinValue == mapAfter) + } + } + + } ensuring (_ => + getCurrentListMap( + _keys, + _values, + mask, + extraKeysBefore, + zeroValue, + minValueBefore, + 0, + defaultEntry + ) - Long.MinValue == getCurrentListMap( + _keys, + _values, + mask, + extraKeysAfter, + zeroValue, + minValueAfter, + 0, + defaultEntry + ) + ) + + @opaque + @inlineOnce + @pure + @ghost + def lemmaRemoveZeroKeyThenRemoveKeyFromListMap[V]( + _keys: Array[Long], + _values: Array[ValueCell[V]], + mask: Int, + extraKeysBefore: Int, + extraKeysAfter: Int, + zeroValueBefore: V, + zeroValueAfter: V, + minValue: V, + defaultEntry: Long => V + ): Unit = { + require(validMask(mask)) + require(_values.length == mask + 1) + require(_keys.length == _values.length) + require(mask >= 0) + require(extraKeysBefore >= 0) + require(extraKeysBefore <= 3) + require(extraKeysAfter >= 0) + require(extraKeysAfter <= 3) + require((extraKeysBefore & 2) == (extraKeysAfter & 2)) // MinValue key does not change + require((extraKeysAfter & 1) == 0) // 0 key must be removed after + require(arrayForallSeekEntryOrOpenFound(0)(_keys, mask)) + require(arrayNoDuplicates(_keys, 0)) + + val mapNoExtraKeysBefore = + getCurrentListMapNoExtraKeys( + _keys, + _values, + mask, + extraKeysBefore, + zeroValueBefore, + minValue, + 0, + defaultEntry + ) + val mapNoExtraKeysAfter = + getCurrentListMapNoExtraKeys( + _keys, + _values, + mask, + extraKeysAfter, + zeroValueAfter, + minValue, + 0, + defaultEntry + ) + + lemmaNoChangeToArrayThenSameMapNoExtras( + _keys, + _values, + mask, + extraKeysBefore, + extraKeysAfter, + zeroValueBefore, + zeroValueAfter, + minValue, + minValue, + 0, + defaultEntry + ) + + assert(mapNoExtraKeysBefore == mapNoExtraKeysAfter) + + if ((extraKeysBefore & 2) == 0) { + // MinValue not defined + if ((extraKeysBefore & 1) == 0) { + ListLongMapLemmas.removeNotPresentStillSame( + getCurrentListMap( + _keys, + _values, + mask, + extraKeysBefore, + zeroValueBefore, + minValue, + 0, + defaultEntry + ), + 0L + ) + } else { + val mapBefore = + getCurrentListMap( + _keys, + _values, + mask, + extraKeysBefore, + zeroValueBefore, + minValue, + 0, + defaultEntry + ) + val mapAfter = + getCurrentListMap( + _keys, + _values, + mask, + extraKeysAfter, + zeroValueAfter, + minValue, + 0, + defaultEntry + ) + + check(mapBefore == mapNoExtraKeysBefore + (0L, zeroValueBefore)) + check(mapAfter == mapNoExtraKeysAfter) + ListLongMapLemmas.addThenRemoveForNewKeyIsSame( + mapNoExtraKeysBefore, + 0L, + zeroValueBefore + ) + check(mapBefore - 0L == mapAfter) + } + + } else { + // MinValue defined + if ((extraKeysBefore & 1) == 0) { + ListLongMapLemmas.removeNotPresentStillSame( + getCurrentListMap( + _keys, + _values, + mask, + extraKeysBefore, + zeroValueBefore, + minValue, + 0, + defaultEntry + ), + 0L + ) + } else { + val mapBefore = + getCurrentListMap( + _keys, + _values, + mask, + extraKeysBefore, + zeroValueBefore, + minValue, + 0, + defaultEntry + ) + val mapAfter = + getCurrentListMap( + _keys, + _values, + mask, + extraKeysAfter, + zeroValueAfter, + minValue, + 0, + defaultEntry + ) + + check( + mapBefore == (mapNoExtraKeysBefore + (0L, zeroValueBefore)) + (Long.MinValue, minValue) + ) + check(mapAfter == (mapNoExtraKeysAfter + (Long.MinValue, minValue))) + ListLongMapLemmas.addThenRemoveForNewKeyIsSame( + mapNoExtraKeysBefore + (Long.MinValue, minValue), + 0L, + zeroValueBefore + ) + ListLongMapLemmas.addCommutativeForDiffKeys( + mapNoExtraKeysBefore, + 0L, + zeroValueBefore, + Long.MinValue, + minValue + ) + check(mapBefore - 0L == mapAfter) + } + } + + } ensuring (_ => + getCurrentListMap( + _keys, + _values, + mask, + extraKeysBefore, + zeroValueBefore, + minValue, + 0, + defaultEntry + ) - 0L == getCurrentListMap( + _keys, + _values, + mask, + extraKeysAfter, + zeroValueAfter, + minValue, + 0, + defaultEntry + ) + ) + + @opaque + @inlineOnce + @pure + @ghost + @opaque + @inlineOnce + @pure + @ghost + def lemmaRemoveValidKeyFromArrayThenMapNoExtrasRemoveKey[V]( + _keys: Array[Long], + _values: Array[ValueCell[V]], + mask: Int, + extraKeys: Int, + zeroValue: V, + minValue: V, + i: Int, + k: Long, + from: Int, + defaultEntry: Long => V + ): Unit = { + require(from >= 0 && from <= _keys.length) + require(validMask(mask)) + require(_values.length == mask + 1) + require(_keys.length == _values.length) + require(mask >= 0) + require(extraKeys >= 0) + require(extraKeys <= 3) + require(arrayForallSeekEntryOrOpenFound(0)(_keys, mask)) + require(arrayNoDuplicates(_keys, 0)) + + require(i >= 0 && i < _keys.length) + require(validKeyInArray(k)) + require(_keys(i) == k) + require(arrayForallSeekEntryOrOpenFound(0)(_keys.updated(i, Long.MinValue), mask)) + require(arrayNoDuplicates(_keys.updated(i, Long.MinValue), 0)) + + decreases(_keys.length - from) + + lemmaArrayContainsFromImpliesContainsFromZero(_keys, k, i) + + check(arrayContainsKey(_keys, k, 0)) + + if (from > i) { + if (from + 1 <= _keys.length) { + lemmaRemoveValidKeyFromArrayThenMapNoExtrasRemoveKey( + _keys, + _values, + mask, + extraKeys, + zeroValue, + minValue, + i, + k, + from + 1, + defaultEntry + ) + } + + } else { + assert(from <= i) + val listmapNoExtrasBefore = + getCurrentListMapNoExtraKeys( + _keys, + _values, + mask, + extraKeys, + zeroValue, + minValue, + from, + defaultEntry + ) + val listmapNoExtrasAfter = getCurrentListMapNoExtraKeys( + _keys.updated(i, Long.MinValue), + _values.updated(i, ValueCellFull(defaultEntry(0L))), + mask, + extraKeys, + zeroValue, + minValue, + from, + defaultEntry + ) + if (from == i) { + lemmaRemoveValidKeyFromArrayThenMapNoExtrasRemoveKey( + _keys, + _values, + mask, + extraKeys, + zeroValue, + minValue, + i, + k, + from + 1, + defaultEntry + ) + assert(_keys(from) == k) + + if ( + getCurrentListMapNoExtraKeys( + _keys, + _values, + mask, + extraKeys, + zeroValue, + minValue, + from + 1, + defaultEntry + ).contains(k) + ) { + if ((extraKeys & 1) != 0 && (extraKeys & 2) != 0) { + ListLongMapLemmas.addStillContains( + getCurrentListMapNoExtraKeys( + _keys, + _values, + mask, + extraKeys, + zeroValue, + minValue, + from + 1, + defaultEntry + ), + 0L, + zeroValue, + k + ) + ListLongMapLemmas.addStillContains( + getCurrentListMapNoExtraKeys( + _keys, + _values, + mask, + extraKeys, + zeroValue, + minValue, + from + 1, + defaultEntry + ) + (0L, zeroValue), + Long.MinValue, + minValue, + k + ) + } else if ((extraKeys & 1) != 0 && (extraKeys & 2) == 0) { + ListLongMapLemmas.addStillContains( + getCurrentListMapNoExtraKeys( + _keys, + _values, + mask, + extraKeys, + zeroValue, + minValue, + from + 1, + defaultEntry + ), + 0L, + zeroValue, + k + ) + } else if ((extraKeys & 2) != 0 && (extraKeys & 1) == 0) { + ListLongMapLemmas.addStillContains( + getCurrentListMapNoExtraKeys( + _keys, + _values, + mask, + extraKeys, + zeroValue, + minValue, + from + 1, + defaultEntry + ), + Long.MinValue, + minValue, + k + ) + } + lemmaListMapContainsThenArrayContainsFrom( + _keys, + _values, + mask, + extraKeys, + zeroValue, + minValue, + k, + from + 1, + defaultEntry + ) + check(arrayContainsKey(_keys, k, from + 1)) + lemmaNoDuplicateFromThenFromBigger(_keys, 0, from) + lemmaArrayNoDuplicateThenKeysContainedNotEqual(_keys, k, from, Nil()) + check(false) + } + ListLongMapLemmas.addThenRemoveForNewKeyIsSame( + getCurrentListMapNoExtraKeys( + _keys, + _values, + mask, + extraKeys, + zeroValue, + minValue, + from + 1, + defaultEntry + ), + _keys(from), + _values(from).get(defaultEntry(0L)) + ) + check( + getCurrentListMapNoExtraKeys( + _keys, + _values, + mask, + extraKeys, + zeroValue, + minValue, + from, + defaultEntry + ) - k == + getCurrentListMapNoExtraKeys( + _keys, + _values, + mask, + extraKeys, + zeroValue, + minValue, + from + 1, + defaultEntry + ) + ) + check( + getCurrentListMapNoExtraKeys( + _keys, + _values, + mask, + extraKeys, + zeroValue, + minValue, + from, + defaultEntry + ) - k == + getCurrentListMapNoExtraKeys( + _keys.updated(i, Long.MinValue), + _values.updated(i, ValueCellFull(defaultEntry(0L))), + mask, + extraKeys, + zeroValue, + minValue, + from + 1, + defaultEntry + ) + ) + check( + getCurrentListMapNoExtraKeys( + _keys, + _values, + mask, + extraKeys, + zeroValue, + minValue, + from, + defaultEntry + ) - k == + getCurrentListMapNoExtraKeys( + _keys.updated(i, Long.MinValue), + _values.updated(i, ValueCellFull(defaultEntry(0L))), + mask, + extraKeys, + zeroValue, + minValue, + from, + defaultEntry + ) + ) + + } else { + assert(from < i) + lemmaRemoveValidKeyFromArrayThenMapNoExtrasRemoveKey( + _keys, + _values, + mask, + extraKeys, + zeroValue, + minValue, + i, + k, + from + 1, + defaultEntry + ) + if (validKeyInArray(_keys(from))) { + check( + getCurrentListMapNoExtraKeys( + _keys.updated(i, Long.MinValue), + _values.updated(i, ValueCellFull(defaultEntry(0L))), + mask, + extraKeys, + zeroValue, + minValue, + from, + defaultEntry + ) == + ((getCurrentListMapNoExtraKeys( + _keys, + _values, + mask, + extraKeys, + zeroValue, + minValue, + from + 1, + defaultEntry + ) - k + (_keys(from), _values(from).get(defaultEntry(0L))))) + ) + + if (_keys(from) == k) { + check(arrayContainsKey(_keys, k, i)) + lemmaArrayContainsFromImpliesContainsFromSmaller(_keys, k, i, from + 1) + lemmaNoDuplicateFromThenFromBigger(_keys, 0, from) + lemmaArrayNoDuplicateThenKeysContainedNotEqual(_keys, k, from, Nil()) + check(false) + } + + check(_keys(from) != k) + + check( + getCurrentListMapNoExtraKeys( + _keys, + _values, + mask, + extraKeys, + zeroValue, + minValue, + from, + defaultEntry + ) == + getCurrentListMapNoExtraKeys( + _keys, + _values, + mask, + extraKeys, + zeroValue, + minValue, + from + 1, + defaultEntry + ) + (_keys(from), _values(from).get(defaultEntry(0L))) + ) + check( + getCurrentListMapNoExtraKeys( + _keys, + _values, + mask, + extraKeys, + zeroValue, + minValue, + from + 1, + defaultEntry + ) - k == + getCurrentListMapNoExtraKeys( + _keys.updated(i, Long.MinValue), + _values.updated(i, ValueCellFull(defaultEntry(0L))), + mask, + extraKeys, + zeroValue, + minValue, + from + 1, + defaultEntry + ) + ) + + ListLongMapLemmas.addRemoveCommutativeForDiffKeys( + getCurrentListMapNoExtraKeys( + _keys, + _values, + mask, + extraKeys, + zeroValue, + minValue, + from + 1, + defaultEntry + ), + _keys(from), + _values(from).get(defaultEntry(0L)), + k + ) + } + } + } + + } ensuring (_ => + if (from <= i) + getCurrentListMapNoExtraKeys( + _keys.updated(i, Long.MinValue), + _values.updated(i, ValueCellFull(defaultEntry(0L))), + mask, + extraKeys, + zeroValue, + minValue, + from, + defaultEntry + ) == + (getCurrentListMapNoExtraKeys( + _keys, + _values, + mask, + extraKeys, + zeroValue, + minValue, + from, + defaultEntry + ) - k) + else + getCurrentListMapNoExtraKeys( + _keys.updated(i, Long.MinValue), + _values.updated(i, ValueCellFull(defaultEntry(0L))), + mask, + extraKeys, + zeroValue, + minValue, + from, + defaultEntry + ) == + getCurrentListMapNoExtraKeys( + _keys, + _values, + mask, + extraKeys, + zeroValue, + minValue, + from, + defaultEntry + ) + ) + + @opaque + @inlineOnce + @pure + @ghost + def lemmaNoChangeToArrayThenSameMapNoExtras[V]( + _keys: Array[Long], + _values: Array[ValueCell[V]], + mask: Int, + extraKeysBefore: Int, + extraKeysAfter: Int, + zeroValueBefore: V, + zeroValueAfter: V, + minValueBefore: V, + minValueAfter: V, + from: Int, + defaultEntry: Long => V + ): Unit = { + require(validMask(mask)) + require(_values.length == mask + 1) + require(_keys.length == _values.length) + require(mask >= 0) + require(extraKeysBefore >= 0) + require(extraKeysBefore <= 3) + require(extraKeysAfter >= 0) + require(extraKeysAfter <= 3) + require(arrayForallSeekEntryOrOpenFound(0)(_keys, mask)) + require(arrayNoDuplicates(_keys, 0)) + require(from >= 0 && from <= _keys.length) + + decreases(_keys.length - from) + + if (from < _keys.length) { + lemmaNoChangeToArrayThenSameMapNoExtras( + _keys, + _values, + mask, + extraKeysBefore, + extraKeysAfter, + zeroValueBefore, + zeroValueAfter, + minValueBefore, + minValueAfter, + from + 1, + defaultEntry + ) + } + + } ensuring (_ => + getCurrentListMapNoExtraKeys( + _keys, + _values, + mask, + extraKeysBefore, + zeroValueBefore, + minValueBefore, + from, + defaultEntry + ) == + getCurrentListMapNoExtraKeys( + _keys, + _values, + mask, + extraKeysAfter, + zeroValueAfter, + minValueAfter, + from, + defaultEntry + ) + ) + + // ------------------EQUIVALENCE BETWEEN LISTMAP AND ARRAY------------------------------------------------------------------------------------------------ + // ------------------BEGIN-------------------------------------------------------------------------------------------------------------------------------- + + @opaque + @inlineOnce + @pure + @ghost + def lemmaKeyInListMapThenSameValueInArray[V]( + _keys: Array[Long], + _values: Array[ValueCell[V]], + mask: Int, + extraKeys: Int, + zeroValue: V, + minValue: V, + k: Long, + i: Int, + defaultEntry: Long => V + ): Unit = { + require(validMask(mask)) + require(_values.length == mask + 1) + require(_keys.length == _values.length) + require(mask >= 0) + + require(extraKeys >= 0) + require(extraKeys <= 3) + require(arrayForallSeekEntryOrOpenFound(0)(_keys, mask)) + require(arrayNoDuplicates(_keys, 0)) + + require( + getCurrentListMap(_keys, _values, mask, extraKeys, zeroValue, minValue, 0, defaultEntry) + .contains(k) + ) + require(inRange(i, mask)) + require(_keys(i) == k) + + if (k != 0 && k != Long.MinValue) { + lemmaKeyInListMapIsInArray( + _keys, + _values, + mask, + extraKeys, + zeroValue, + minValue, + k, + defaultEntry + ) + lemmaListMapApplyFromThenApplyFromZero( + _keys, + _values, + mask, + extraKeys, + zeroValue, + minValue, + k, + _values(i).get(defaultEntry(0L)), + i, + defaultEntry + ) + } + } ensuring (_ => + if (k == 0) + (extraKeys & 1) != 0 && getCurrentListMap( + _keys, + _values, + mask, + extraKeys, + zeroValue, + minValue, + 0, + defaultEntry + ).apply( + k + ) == zeroValue + else if (k == Long.MinValue) + (extraKeys & 2) != 0 && getCurrentListMap( + _keys, + _values, + mask, + extraKeys, + zeroValue, + minValue, + 0, + defaultEntry + ).apply( + k + ) == minValue + else + arrayContainsKey(_keys, k, 0) && getCurrentListMap( + _keys, + _values, + mask, + extraKeys, + zeroValue, + minValue, + 0, + defaultEntry + ).apply(k) == _values(i).get(defaultEntry(0L)) + ) + + @opaque + @inlineOnce + @pure + @ghost + def lemmaKeyInListMapIsInArray[V]( + _keys: Array[Long], + _values: Array[ValueCell[V]], + mask: Int, + extraKeys: Int, + zeroValue: V, + minValue: V, + k: Long, + defaultEntry: Long => V + ): Unit = { + require(validMask(mask)) + require(_values.length == mask + 1) + require(_keys.length == _values.length) + require(mask >= 0) + + require(extraKeys >= 0) + require(extraKeys <= 3) + require(arrayForallSeekEntryOrOpenFound(0)(_keys, mask)) + require(arrayNoDuplicates(_keys, 0)) + + require( + getCurrentListMap(_keys, _values, mask, extraKeys, zeroValue, minValue, 0, defaultEntry) + .contains(k) + ) + lemmaListMapContainsThenArrayContainsFrom( + _keys, + _values, + mask, + extraKeys, + zeroValue, + minValue, + k, + 0, + defaultEntry + ) + + } ensuring (_ => + if (k != 0 && k != Long.MinValue) arrayContainsKey(_keys, k, 0) + else if (k == 0) (extraKeys & 1) != 0 + else (extraKeys & 2) != 0 + ) + + @opaque + @inlineOnce + @pure + @ghost + def lemmaValidKeyInArrayIsInListMap[V]( + _keys: Array[Long], + _values: Array[ValueCell[V]], + mask: Int, + extraKeys: Int, + zeroValue: V, + minValue: V, + i: Int, + defaultEntry: Long => V + ): Unit = { + require(validMask(mask)) + require(_values.length == mask + 1) + require(_keys.length == _values.length) + require(mask >= 0) + + require(extraKeys >= 0) + require(extraKeys <= 3) + require(arrayForallSeekEntryOrOpenFound(0)(_keys, mask)) + require(arrayNoDuplicates(_keys, 0)) + + require(i >= 0 && i < _keys.length) + require(validKeyInArray(_keys(i))) + + lemmaInListMapFromThenFromZero( + _keys, + _values, + mask, + extraKeys, + zeroValue, + minValue, + i, + i, + defaultEntry + ) + + } ensuring (_ => + getCurrentListMap(_keys, _values, mask, extraKeys, zeroValue, minValue, 0, defaultEntry) + .contains(_keys(i)) + ) + + @opaque + @inlineOnce + @pure + @ghost + def lemmaArrayContainsKeyThenInListMap[V]( + _keys: Array[Long], + _values: Array[ValueCell[V]], + mask: Int, + extraKeys: Int, + zeroValue: V, + minValue: V, + k: Long, + from: Int, + defaultEntry: Long => V + ): Unit = { + require(validMask(mask)) + require(_values.length == mask + 1) + require(_keys.length == _values.length) + require(mask >= 0) + + require(extraKeys >= 0) + require(extraKeys <= 3) + require(arrayForallSeekEntryOrOpenFound(0)(_keys, mask)) + require(arrayNoDuplicates(_keys, 0)) + + require(validKeyInArray(k)) + require(from >= 0 && from < _keys.length) + require(arrayContainsKey(_keys, k, from)) + decreases(_keys.length - from) + + if (_keys(from) == k) { + lemmaValidKeyInArrayIsInListMap( + _keys, + _values, + mask, + extraKeys, + zeroValue, + minValue, + from, + defaultEntry + ) + } else { + lemmaArrayContainsFromAndNotEqualThenContainsFromPlusOne( + _keys, + _values, + mask, + extraKeys, + zeroValue, + minValue, + k, + from + ) + lemmaArrayContainsKeyThenInListMap( + _keys, + _values, + mask, + extraKeys, + zeroValue, + minValue, + k, + from + 1, + defaultEntry + ) + } + + } ensuring (_ => + getCurrentListMap(_keys, _values, mask, extraKeys, zeroValue, minValue, 0, defaultEntry) + .contains(k) + ) + + @opaque + @inlineOnce + @pure + @ghost + def lemmaListMapApplyFromThenApplyFromZero[V]( + _keys: Array[Long], + _values: Array[ValueCell[V]], + mask: Int, + extraKeys: Int, + zeroValue: V, + minValue: V, + k: Long, + v: V, + from: Int, + defaultEntry: Long => V + ): Unit = { + require(validMask(mask)) + require(_values.length == mask + 1) + require(_keys.length == _values.length) + require(mask >= 0) + + require(extraKeys >= 0) + require(extraKeys <= 3) + require(arrayForallSeekEntryOrOpenFound(0)(_keys, mask)) + require(arrayNoDuplicates(_keys, 0)) + + require(from >= 0 && from < _keys.length) + require( + getCurrentListMap(_keys, _values, mask, extraKeys, zeroValue, minValue, from, defaultEntry) + .contains(k) + ) + require( + getCurrentListMap(_keys, _values, mask, extraKeys, zeroValue, minValue, from, defaultEntry) + .apply(k) == v + ) + + decreases(from) + + if (from > 0) { + if (validKeyInArray(k)) { + if (validKeyInArray(_keys(from - 1))) { + lemmaListMapRecursiveValidKeyArray( + _keys, + _values, + mask, + extraKeys, + zeroValue, + minValue, + from - 1, + defaultEntry + ) + ListLongMapLemmas.addStillContains( + getCurrentListMap( + _keys, + _values, + mask, + extraKeys, + zeroValue, + minValue, + from, + defaultEntry + ), + _keys(from - 1), + _values(from - 1).get(defaultEntry(0L)), + k + ) + lemmaNoDuplicateFromThenFromBigger(_keys, 0, from - 1) + lemmaListMapContainsThenArrayContainsFrom( + _keys, + _values, + mask, + extraKeys, + zeroValue, + minValue, + k, + from, + defaultEntry + ) + lemmaArrayNoDuplicateThenKeysContainedNotEqual(_keys, k, from - 1, Nil()) + ListLongMapLemmas.addApplyDifferent( + getCurrentListMap( + _keys, + _values, + mask, + extraKeys, + zeroValue, + minValue, + from, + defaultEntry + ), + _keys(from - 1), + _values(from - 1).get(defaultEntry(0L)), + k + ) + } + } + lemmaListMapApplyFromThenApplyFromZero( + _keys, + _values, + mask, + extraKeys, + zeroValue, + minValue, + k, + v, + from - 1, + defaultEntry + ) + } + + } ensuring (_ => + getCurrentListMap(_keys, _values, mask, extraKeys, zeroValue, minValue, 0, defaultEntry) + .contains(k) && getCurrentListMap( + _keys, + _values, + mask, + extraKeys, + zeroValue, + minValue, + 0, + defaultEntry + ) + .apply(k) == v + ) + + @opaque + @inlineOnce + @pure + @ghost + def lemmaArrayContainsFromAndNotEqualThenContainsFromPlusOne[V]( + _keys: Array[Long], + _values: Array[ValueCell[V]], + mask: Int, + extraKeys: Int, + zeroValue: V, + minValue: V, + k: Long, + from: Int + ): Unit = { + require(validMask(mask)) + require(_values.length == mask + 1) + require(_keys.length == _values.length) + require(mask >= 0) + + require(extraKeys >= 0) + require(extraKeys <= 3) + require(arrayForallSeekEntryOrOpenFound(0)(_keys, mask)) + require(arrayNoDuplicates(_keys, 0)) + + require(from >= 0 && from < _keys.length) + require(arrayContainsKey(_keys, k, from)) + require(_keys(from) != k) + + } ensuring (_ => arrayContainsKey(_keys, k, from + 1)) + + @opaque + @inlineOnce + @pure + @ghost + def lemmaListMapRecursiveValidKeyArray[V]( + _keys: Array[Long], + _values: Array[ValueCell[V]], + mask: Int, + extraKeys: Int, + zeroValue: V, + minValue: V, + from: Int, + defaultEntry: Long => V + ): Unit = { + require(validMask(mask)) + require(_values.length == mask + 1) + require(_keys.length == _values.length) + require(mask >= 0) + + require(extraKeys >= 0) + require(extraKeys <= 3) + require(arrayForallSeekEntryOrOpenFound(0)(_keys, mask)) + require(arrayNoDuplicates(_keys, 0)) + + require(from >= 0 && from < _keys.length) + require(validKeyInArray(_keys(from))) + + if ((extraKeys & 1) != 0 && (extraKeys & 2) != 0) { + ListLongMapLemmas.addCommutativeForDiffKeys( + getCurrentListMapNoExtraKeys( + _keys, + _values, + mask, + extraKeys, + zeroValue, + minValue, + from + 1, + defaultEntry + ) + (0L, zeroValue), + Long.MinValue, + minValue, + _keys(from), + _values(from).get(defaultEntry(0L)) + ) + ListLongMapLemmas.addCommutativeForDiffKeys( + getCurrentListMapNoExtraKeys( + _keys, + _values, + mask, + extraKeys, + zeroValue, + minValue, + from + 1, + defaultEntry + ), + 0L, + zeroValue, + _keys(from), + _values(from).get(defaultEntry(0L)) + ) + } else if ((extraKeys & 1) != 0 && (extraKeys & 2) == 0) { + ListLongMapLemmas.addCommutativeForDiffKeys( + getCurrentListMapNoExtraKeys( + _keys, + _values, + mask, + extraKeys, + zeroValue, + minValue, + from + 1, + defaultEntry + ), + 0L, + zeroValue, + _keys(from), + _values(from).get(defaultEntry(0L)) + ) + } else if ((extraKeys & 2) != 0 && (extraKeys & 1) == 0) { + ListLongMapLemmas.addCommutativeForDiffKeys( + getCurrentListMapNoExtraKeys( + _keys, + _values, + mask, + extraKeys, + zeroValue, + minValue, + from + 1, + defaultEntry + ), + Long.MinValue, + minValue, + _keys(from), + _values(from).get(defaultEntry(0L)) + ) + } + + } ensuring (_ => + getCurrentListMap( + _keys, + _values, + mask, + extraKeys, + zeroValue, + minValue, + from, + defaultEntry + ) == getCurrentListMap( + _keys, + _values, + mask, + extraKeys, + zeroValue, + minValue, + from + 1, + defaultEntry + ) + (_keys(from), _values(from).get(defaultEntry(0L))) + ) + + @opaque + @inlineOnce + @pure + @ghost + def lemmaInListMapAfterAddingDiffThenInBefore[V]( + k: Long, + otherKey: Long, + value: V, + lm: ListLongMap[V] + ): Unit = { + require((lm + (otherKey, value)).contains(k)) + require(k != otherKey) + if (!lm.contains(k)) { + ListLongMapLemmas.addStillNotContains(lm, otherKey, value, k) + } + + } ensuring (_ => lm.contains(k)) + + @opaque + @inlineOnce + @pure + @ghost + def lemmaInListMapFromThenFromPlsOneIfNotEqToFstNoXMin[V]( + _keys: Array[Long], + _values: Array[ValueCell[V]], + mask: Int, + extraKeys: Int, + zeroValue: V, + minValue: V, + k: Long, + from: Int, + defaultEntry: Long => V + ): Unit = { + require(validMask(mask)) + require(_values.length == mask + 1) + require(_keys.length == _values.length) + require(mask >= 0) + + require(extraKeys >= 0) + require(extraKeys <= 3) + require(arrayForallSeekEntryOrOpenFound(0)(_keys, mask)) + require(arrayNoDuplicates(_keys, 0)) + + require((extraKeys & 2) == 0) + require(from >= 0 && from < _keys.length) + require(k != 0 && k != Long.MinValue) + require( + getCurrentListMap(_keys, _values, mask, extraKeys, zeroValue, minValue, from, defaultEntry) + .contains( + k + ) && _keys(from) != k + ) + + if (validKeyInArray(_keys(from))) { + if ((extraKeys & 1) != 0) { + lemmaInListMapAfterAddingDiffThenInBefore( + k, + 0L, + zeroValue, + getCurrentListMapNoExtraKeys( + _keys, + _values, + mask, + extraKeys, + zeroValue, + minValue, + from + 1, + defaultEntry + ) + (_keys( + from + ), _values(from).get(defaultEntry(0L))) + ) + lemmaInListMapAfterAddingDiffThenInBefore( + k, + _keys(from), + _values(from).get(defaultEntry(0L)), + getCurrentListMapNoExtraKeys( + _keys, + _values, + mask, + extraKeys, + zeroValue, + minValue, + from + 1, + defaultEntry + ) + ) + + ListLongMapLemmas.addStillContains( + getCurrentListMapNoExtraKeys( + _keys, + _values, + mask, + extraKeys, + zeroValue, + minValue, + from + 1, + defaultEntry + ), + 0L, + zeroValue, + k + ) + + } else { + lemmaInListMapAfterAddingDiffThenInBefore( + k, + _keys(from), + _values(from).get(defaultEntry(0L)), + getCurrentListMapNoExtraKeys( + _keys, + _values, + mask, + extraKeys, + zeroValue, + minValue, + from + 1, + defaultEntry + ) + ) + } + + } + + } ensuring (_ => + getCurrentListMap( + _keys, + _values, + mask, + extraKeys, + zeroValue, + minValue, + from + 1, + defaultEntry + ).contains(k) + ) + + @opaque + @inlineOnce + @pure + @ghost + def lemmaInListMapFromThenFromPlsOneIfNotEqToFstNoXZero[V]( + _keys: Array[Long], + _values: Array[ValueCell[V]], + mask: Int, + extraKeys: Int, + zeroValue: V, + minValue: V, + k: Long, + from: Int, + defaultEntry: Long => V + ): Unit = { + require(validMask(mask)) + require(_values.length == mask + 1) + require(_keys.length == _values.length) + require(mask >= 0) + + require(extraKeys >= 0) + require(extraKeys <= 3) + require(arrayForallSeekEntryOrOpenFound(0)(_keys, mask)) + require(arrayNoDuplicates(_keys, 0)) + + require((extraKeys & 1) == 0) + require(from >= 0 && from < _keys.length) + require(k != 0 && k != Long.MinValue) + require( + getCurrentListMap(_keys, _values, mask, extraKeys, zeroValue, minValue, from, defaultEntry) + .contains( + k + ) && _keys(from) != k + ) + + if (validKeyInArray(_keys(from))) { + if ((extraKeys & 2) != 0) { + + lemmaInListMapAfterAddingDiffThenInBefore( + k, + Long.MinValue, + minValue, + getCurrentListMapNoExtraKeys( + _keys, + _values, + mask, + extraKeys, + zeroValue, + minValue, + from + 1, + defaultEntry + ) + (_keys( + from + ), _values(from).get(defaultEntry(0L))) + ) + lemmaInListMapAfterAddingDiffThenInBefore( + k, + _keys(from), + _values(from).get(defaultEntry(0L)), + getCurrentListMapNoExtraKeys( + _keys, + _values, + mask, + extraKeys, + zeroValue, + minValue, + from + 1, + defaultEntry + ) + ) + + ListLongMapLemmas.addStillContains( + getCurrentListMapNoExtraKeys( + _keys, + _values, + mask, + extraKeys, + zeroValue, + minValue, + from + 1, + defaultEntry + ), + Long.MinValue, + minValue, + k + ) + + } else { + lemmaInListMapAfterAddingDiffThenInBefore( + k, + _keys(from), + _values(from).get(defaultEntry(0L)), + getCurrentListMapNoExtraKeys( + _keys, + _values, + mask, + extraKeys, + zeroValue, + minValue, + from + 1, + defaultEntry + ) + ) + } + } + + } ensuring (_ => + getCurrentListMap( + _keys, + _values, + mask, + extraKeys, + zeroValue, + minValue, + from + 1, + defaultEntry + ).contains(k) + ) + + @opaque + @inlineOnce + @pure + @ghost + def lemmaInListMapFromThenFromPlsOneIfNotEqToFstXKeys[V]( + _keys: Array[Long], + _values: Array[ValueCell[V]], + mask: Int, + extraKeys: Int, + zeroValue: V, + minValue: V, + k: Long, + from: Int, + defaultEntry: Long => V + ): Unit = { + require(validMask(mask)) + require(_values.length == mask + 1) + require(_keys.length == _values.length) + require(mask >= 0) + + require(extraKeys >= 0) + require(extraKeys <= 3) + require(arrayForallSeekEntryOrOpenFound(0)(_keys, mask)) + require(arrayNoDuplicates(_keys, 0)) + + require((extraKeys & 1) != 0 && (extraKeys & 2) != 0) + require(from >= 0 && from < _keys.length) + require(k != 0 && k != Long.MinValue) + require( + getCurrentListMap(_keys, _values, mask, extraKeys, zeroValue, minValue, from, defaultEntry) + .contains( + k + ) && _keys(from) != k + ) + + if (validKeyInArray(_keys(from))) { + lemmaInListMapAfterAddingDiffThenInBefore( + k, + Long.MinValue, + minValue, + getCurrentListMapNoExtraKeys( + _keys, + _values, + mask, + extraKeys, + zeroValue, + minValue, + from + 1, + defaultEntry + ) + (_keys( + from + ), _values(from).get(defaultEntry(0L))) + (0L, zeroValue) + ) + lemmaInListMapAfterAddingDiffThenInBefore( + k, + 0L, + zeroValue, + getCurrentListMapNoExtraKeys( + _keys, + _values, + mask, + extraKeys, + zeroValue, + minValue, + from + 1, + defaultEntry + ) + (_keys( + from + ), _values(from).get(defaultEntry(0L))) + ) + lemmaInListMapAfterAddingDiffThenInBefore( + k, + _keys(from), + _values(from).get(defaultEntry(0L)), + getCurrentListMapNoExtraKeys( + _keys, + _values, + mask, + extraKeys, + zeroValue, + minValue, + from + 1, + defaultEntry + ) + ) + + ListLongMapLemmas.addStillContains( + getCurrentListMapNoExtraKeys( + _keys, + _values, + mask, + extraKeys, + zeroValue, + minValue, + from + 1, + defaultEntry + ), + 0L, + zeroValue, + k + ) + ListLongMapLemmas.addStillContains( + getCurrentListMapNoExtraKeys( + _keys, + _values, + mask, + extraKeys, + zeroValue, + minValue, + from + 1, + defaultEntry + ) + (0L, zeroValue), + Long.MinValue, + minValue, + k + ) + } + } ensuring (_ => + getCurrentListMap( + _keys, + _values, + mask, + extraKeys, + zeroValue, + minValue, + from + 1, + defaultEntry + ).contains(k) + ) + + @opaque + @inlineOnce + @pure + @ghost + def lemmaInListMapFromThenFromPlsOneIfNotEqToFst[V]( + _keys: Array[Long], + _values: Array[ValueCell[V]], + mask: Int, + extraKeys: Int, + zeroValue: V, + minValue: V, + k: Long, + from: Int, + defaultEntry: Long => V + ): Unit = { + require(validMask(mask)) + require(_values.length == mask + 1) + require(_keys.length == _values.length) + require(mask >= 0) + + require(extraKeys >= 0) + require(extraKeys <= 3) + require(arrayForallSeekEntryOrOpenFound(0)(_keys, mask)) + require(arrayNoDuplicates(_keys, 0)) + + require(from >= 0 && from < _keys.length) + require(k != 0 && k != Long.MinValue) + require( + getCurrentListMap(_keys, _values, mask, extraKeys, zeroValue, minValue, from, defaultEntry) + .contains( + k + ) && _keys(from) != k + ) + + if (validKeyInArray(_keys(from))) { + if ((extraKeys & 1) == 0) { + lemmaInListMapFromThenFromPlsOneIfNotEqToFstNoXZero( + _keys, + _values, + mask, + extraKeys, + zeroValue, + minValue, + k, + from, + defaultEntry + ) + } else if ((extraKeys & 2) == 0) { + lemmaInListMapFromThenFromPlsOneIfNotEqToFstNoXMin( + _keys, + _values, + mask, + extraKeys, + zeroValue, + minValue, + k, + from, + defaultEntry + ) + } else { + lemmaInListMapFromThenFromPlsOneIfNotEqToFstXKeys( + _keys, + _values, + mask, + extraKeys, + zeroValue, + minValue, + k, + from, + defaultEntry + ) + } + } + + } ensuring (_ => + getCurrentListMap( + _keys, + _values, + mask, + extraKeys, + zeroValue, + minValue, + from + 1, + defaultEntry + ).contains(k) + ) + + @opaque + @inlineOnce + @pure + @ghost + def lemmaInListMapFromThenFromZero[V]( + _keys: Array[Long], + _values: Array[ValueCell[V]], + mask: Int, + extraKeys: Int, + zeroValue: V, + minValue: V, + from: Int, + i: Int, + defaultEntry: Long => V + ): Unit = { + require(validMask(mask)) + require(_values.length == mask + 1) + require(_keys.length == _values.length) + require(mask >= 0) + + require(extraKeys >= 0) + require(extraKeys <= 3) + require(arrayForallSeekEntryOrOpenFound(0)(_keys, mask)) + require(arrayNoDuplicates(_keys, 0)) + + require(from >= 0 && from < _keys.length) + require(i >= from && i < _keys.length) + require(validKeyInArray(_keys(i))) + require( + getCurrentListMap(_keys, _values, mask, extraKeys, zeroValue, minValue, from, defaultEntry) + .contains( + _keys(i) + ) + ) + + lemmaInListMapFromThenInFromSmaller( + _keys, + _values, + mask, + extraKeys, + zeroValue, + minValue, + from, + 0, + i, + defaultEntry + ) + + } ensuring (_ => + getCurrentListMap(_keys, _values, mask, extraKeys, zeroValue, minValue, 0, defaultEntry) + .contains(_keys(i)) + ) + + @opaque + @inlineOnce + @pure + @ghost + def lemmaInListMapFromThenInFromSmaller[V]( + _keys: Array[Long], + _values: Array[ValueCell[V]], + mask: Int, + extraKeys: Int, + zeroValue: V, + minValue: V, + from: Int, + newFrom: Int, + i: Int, + defaultEntry: Long => V + ): Unit = { + require(validMask(mask)) + require(_values.length == mask + 1) + require(_keys.length == _values.length) + require(mask >= 0) + + require(extraKeys >= 0) + require(extraKeys <= 3) + require(arrayForallSeekEntryOrOpenFound(0)(_keys, mask)) + require(arrayNoDuplicates(_keys, 0)) + + require(from >= 0 && from < _keys.length) + require(newFrom >= 0 && newFrom <= from) + require(i >= from && i < _keys.length) + require( + validKeyInArray(_keys(i)) && getCurrentListMap( + _keys, + _values, + mask, + extraKeys, + zeroValue, + minValue, + from, + defaultEntry + ) + .contains(_keys(i)) + ) + + decreases(from - newFrom) + if (from > newFrom) { + lemmaInListMapFromThenInFromMinusOne( + _keys, + _values, + mask, + extraKeys, + zeroValue, + minValue, + from, + i, + defaultEntry + ) + lemmaInListMapFromThenInFromSmaller( + _keys, + _values, + mask, + extraKeys, + zeroValue, + minValue, + from - 1, + newFrom, + i, + defaultEntry + ) + } + } ensuring (_ => + getCurrentListMap(_keys, _values, mask, extraKeys, zeroValue, minValue, newFrom, defaultEntry) + .contains( + _keys(i) + ) + ) + + @opaque + @inlineOnce + @pure + @ghost + def lemmaInListMapFromThenInFromMinusOne[V]( + _keys: Array[Long], + _values: Array[ValueCell[V]], + mask: Int, + extraKeys: Int, + zeroValue: V, + minValue: V, + from: Int, + i: Int, + defaultEntry: Long => V + ): Unit = { + require(validMask(mask)) + require(_values.length == mask + 1) + require(_keys.length == _values.length) + require(mask >= 0) + + require(extraKeys >= 0) + require(extraKeys <= 3) + require(arrayForallSeekEntryOrOpenFound(0)(_keys, mask)) + require(arrayNoDuplicates(_keys, 0)) + + require(from > 0 && from < _keys.length) + require(i >= from && i < _keys.length) + require( + validKeyInArray(_keys(i)) && getCurrentListMap( + _keys, + _values, + mask, + extraKeys, + zeroValue, + minValue, + from, + defaultEntry + ) + .contains(_keys(i)) + ) + + val currentLMFrom: ListLongMap[V] = + getCurrentListMap(_keys, _values, mask, extraKeys, zeroValue, minValue, from, defaultEntry) + val currentLMFromMinusOne: ListLongMap[V] = + getCurrentListMap( + _keys, + _values, + mask, + extraKeys, + zeroValue, + minValue, + from - 1, + defaultEntry + ) + if (validKeyInArray(_keys(from - 1))) { + lemmaListMapRecursiveValidKeyArray( + _keys, + _values, + mask, + extraKeys, + zeroValue, + minValue, + from - 1, + defaultEntry + ) + ListLongMapLemmas.addStillContains( + getCurrentListMap( + _keys, + _values, + mask, + extraKeys, + zeroValue, + minValue, + from, + defaultEntry + ), + _keys(from - 1), + _values(from - 1).get(defaultEntry(0L)), + _keys(i) + ) + } + } ensuring (_ => + getCurrentListMap( + _keys, + _values, + mask, + extraKeys, + zeroValue, + minValue, + from - 1, + defaultEntry + ).contains( + _keys(i) + ) + ) + + @opaque + @inlineOnce + @pure + @ghost + def lemmaListMapContainsThenArrayContainsFrom[V]( + _keys: Array[Long], + _values: Array[ValueCell[V]], + mask: Int, + extraKeys: Int, + zeroValue: V, + minValue: V, + k: Long, + from: Int, + defaultEntry: Long => V + ): Unit = { + require(validMask(mask)) + require(_values.length == mask + 1) + require(_keys.length == _values.length) + require(mask >= 0) + + require(extraKeys >= 0) + require(extraKeys <= 3) + require(arrayForallSeekEntryOrOpenFound(0)(_keys, mask)) + require(arrayNoDuplicates(_keys, 0)) + + require(from >= 0 && from < _keys.length) + require( + getCurrentListMap(_keys, _values, mask, extraKeys, zeroValue, minValue, from, defaultEntry) + .contains(k) + ) + + decreases(_keys.length - from) + val currentListMap: ListLongMap[V] = + getCurrentListMap(_keys, _values, mask, extraKeys, zeroValue, minValue, from, defaultEntry) + if (k != 0 && k != Long.MinValue) { + if (from + 1 < _keys.length) { + if (_keys(from) != k) { + lemmaInListMapFromThenFromPlsOneIfNotEqToFst( + _keys, + _values, + mask, + extraKeys, + zeroValue, + minValue, + k, + from, + defaultEntry + ) + lemmaListMapContainsThenArrayContainsFrom( + _keys, + _values, + mask, + extraKeys, + zeroValue, + minValue, + k, + from + 1, + defaultEntry + ) + } + } else { + if (validKeyInArray(_keys(from))) { + if ((extraKeys & 1) != 0 && (extraKeys & 2) != 0) { + lemmaInListMapAfterAddingDiffThenInBefore( + k, + Long.MinValue, + minValue, + (getCurrentListMapNoExtraKeys( + _keys, + _values, + mask, + extraKeys, + zeroValue, + minValue, + from, + defaultEntry + ) + (0L, zeroValue)) + ) + lemmaInListMapAfterAddingDiffThenInBefore( + k, + 0L, + zeroValue, + getCurrentListMapNoExtraKeys( + _keys, + _values, + mask, + extraKeys, + zeroValue, + minValue, + from, + defaultEntry + ) + ) + } else if ((extraKeys & 1) != 0 && (extraKeys & 2) == 0) { + lemmaInListMapAfterAddingDiffThenInBefore( + k, + 0L, + zeroValue, + getCurrentListMapNoExtraKeys( + _keys, + _values, + mask, + extraKeys, + zeroValue, + minValue, + from, + defaultEntry + ) + ) + } else if ((extraKeys & 2) != 0 && (extraKeys & 1) == 0) { + lemmaInListMapAfterAddingDiffThenInBefore( + k, + Long.MinValue, + minValue, + getCurrentListMapNoExtraKeys( + _keys, + _values, + mask, + extraKeys, + zeroValue, + minValue, + from, + defaultEntry + ) + ) + } + ListLongMapLemmas.emptyContainsNothing[V](k) + if (k != _keys(from)) { + ListLongMapLemmas.addStillNotContains( + ListLongMap.empty[V], + _keys(from), + _values(from).get(defaultEntry(0L)), + k + ) + check(false) + } + } else { + ListLongMapLemmas.emptyContainsNothing[V](k) + if ((extraKeys & 1) != 0 && (extraKeys & 2) != 0) { + ListLongMapLemmas.addStillNotContains( + getCurrentListMapNoExtraKeys( + _keys, + _values, + mask, + extraKeys, + zeroValue, + minValue, + from, + defaultEntry + ), + 0L, + zeroValue, + k + ) + ListLongMapLemmas.addStillNotContains( + getCurrentListMapNoExtraKeys( + _keys, + _values, + mask, + extraKeys, + zeroValue, + minValue, + from, + defaultEntry + ), + Long.MinValue, + minValue, + k + ) + lemmaInListMapAfterAddingDiffThenInBefore( + k, + Long.MinValue, + minValue, + (getCurrentListMapNoExtraKeys( + _keys, + _values, + mask, + extraKeys, + zeroValue, + minValue, + from, + defaultEntry + ) + (0L, zeroValue)) + ) + lemmaInListMapAfterAddingDiffThenInBefore( + k, + 0L, + zeroValue, + getCurrentListMapNoExtraKeys( + _keys, + _values, + mask, + extraKeys, + zeroValue, + minValue, + from, + defaultEntry + ) + ) + } else if ((extraKeys & 1) != 0 && (extraKeys & 2) == 0) { + ListLongMapLemmas.addStillNotContains( + getCurrentListMapNoExtraKeys( + _keys, + _values, + mask, + extraKeys, + zeroValue, + minValue, + from, + defaultEntry + ), + 0L, + zeroValue, + k + ) + lemmaInListMapAfterAddingDiffThenInBefore( + k, + 0L, + zeroValue, + getCurrentListMapNoExtraKeys( + _keys, + _values, + mask, + extraKeys, + zeroValue, + minValue, + from, + defaultEntry + ) + ) + } else if ((extraKeys & 2) != 0 && (extraKeys & 1) == 0) { + ListLongMapLemmas.addStillNotContains( + getCurrentListMapNoExtraKeys( + _keys, + _values, + mask, + extraKeys, + zeroValue, + minValue, + from, + defaultEntry + ), + Long.MinValue, + minValue, + k + ) + lemmaInListMapAfterAddingDiffThenInBefore( + k, + Long.MinValue, + minValue, + getCurrentListMapNoExtraKeys( + _keys, + _values, + mask, + extraKeys, + zeroValue, + minValue, + from, + defaultEntry + ) + ) + } + check(false) + } + } + } + } ensuring (_ => + (if (k != 0 && k != Long.MinValue) arrayContainsKey(_keys, k, from) + else if (k == 0) (extraKeys & 1) != 0 + else (extraKeys & 2) != 0) + ) + + // ------------------END---------------------------------------------------------------------------------------------------------------------------------- + // ------------------EQUIVALENCE BETWEEN LISTMAP AND ARRAY------------------------------------------------------------------------------------------------ + + // ------------------SEEKENTRY RELATED-------------------------------------------------------------------------------------------------------------------- + // ------------------BEGIN-------------------------------------------------------------------------------------------------------------------------------- + + @opaque + @inlineOnce + @pure + def lemmaArrayForallSeekEntryOrOpenFoundAlwaysTrueFor0Array(_keys: Array[Long], mask: Int, i: Int): Unit = { + require(validMask(mask)) + require(_keys.length == mask + 1) + require(_keys == Array.fill(mask + 1)(0L)) + require(i >= 0 && i <= _keys.length) + decreases(_keys.length - i) + + if (i < _keys.length) { + lemmaArrayForallSeekEntryOrOpenFoundAlwaysTrueFor0Array(_keys, mask, i + 1) + } + } ensuring (_ => arrayForallSeekEntryOrOpenFound(i)(_keys, mask)) + + @opaque + @inlineOnce + @pure + @ghost + def lemmaInListMapThenSeekEntryFinds[V]( + _keys: Array[Long], + _values: Array[ValueCell[V]], + mask: Int, + extraKeys: Int, + zeroValue: V, + minValue: V, + k: Long, + defaultEntry: Long => V + ): Unit = { + require(validMask(mask)) + require(_values.length == mask + 1) + require(_keys.length == _values.length) + require(mask >= 0) + require(extraKeys >= 0) + require(extraKeys <= 3) + require(arrayForallSeekEntryOrOpenFound(0)(_keys, mask)) + require(arrayNoDuplicates(_keys, 0)) + require( + getCurrentListMap(_keys, _values, mask, extraKeys, zeroValue, minValue, 0, defaultEntry) + .contains(k) + ) + require(validKeyInArray(k)) + + lemmaKeyInListMapIsInArray( + _keys, + _values, + mask, + extraKeys, + zeroValue, + minValue, + k, + defaultEntry + ) + assert(arrayContainsKey(_keys, k, 0)) + assert(arrayForallSeekEntryOrOpenFound(0)(_keys, mask)) + val i = arrayScanForKey(_keys, k, 0) + lemmaArrayForallSeekEntryOrOpenFoundFromSmallerThenFromBigger(_keys, mask, 0, i) + lemmaSeekEntryOrOpenFindsThenSeekEntryFinds(k, i, _keys, mask) + + } ensuring (_ => + (seekEntry(k)(_keys, mask) match { + case Found(index) => inRange(index, mask) && _keys(index) == k + case _ => false + }) + ) + + @opaque + @inlineOnce + @pure + @ghost + def lemmaNotInListMapThenSeekEntryFindsMissingBit[V]( + _keys: Array[Long], + _values: Array[ValueCell[V]], + mask: Int, + extraKeys: Int, + zeroValue: V, + minValue: V, + k: Long, + defaultEntry: Long => V + ): Unit = { + require(validMask(mask)) + require(_values.length == mask + 1) + require(_keys.length == _values.length) + require(mask >= 0) + require(extraKeys >= 0) + require(extraKeys <= 3) + require(arrayForallSeekEntryOrOpenFound(0)(_keys, mask)) + require(arrayNoDuplicates(_keys, 0)) + require(validKeyInArray(k)) + require( + !getCurrentListMap(_keys, _values, mask, extraKeys, zeroValue, minValue, 0, defaultEntry) + .contains(k) + ) + if (validKeyInArray(k)) { + if (arrayContainsKey(_keys, k, 0)) { + val i = arrayScanForKey(_keys, k, 0) + lemmaValidKeyInArrayIsInListMap( + _keys, + _values, + mask, + extraKeys, + zeroValue, + minValue, + i, + defaultEntry + ) + lemmaSeekEntryOrOpenFindsThenSeekEntryFinds(k, i, _keys, mask) + lemmaArrayForallSeekEntryOrOpenFoundFromSmallerThenFromBigger(_keys, mask, 0, i) + check(false) + } else { + seekEntry(k)(_keys, mask) match { + case Found(index) => { + // found but not in array --> Contradiction + lemmaValidKeyInArrayIsInListMap( + _keys, + _values, + mask, + extraKeys, + zeroValue, + minValue, + index, + defaultEntry + ) + check(false) + } + case _ => () + } + } + } + + } ensuring (_ => + (seekEntry(k)(_keys, mask) match { + case MissingZero(_) => true + case Undefined() => true + case _ => false + }) + ) + + @opaque + @inlineOnce + @pure + @ghost + def lemmaInListMapThenSeekEntryOrOpenFindsIt[V]( + _keys: Array[Long], + _values: Array[ValueCell[V]], + mask: Int, + extraKeys: Int, + zeroValue: V, + minValue: V, + k: Long, + defaultEntry: Long => V + ): Unit = { + require(validMask(mask)) + require(_values.length == mask + 1) + require(_keys.length == _values.length) + require(mask >= 0) + require(extraKeys >= 0) + require(extraKeys <= 3) + require(arrayForallSeekEntryOrOpenFound(0)(_keys, mask)) + require(arrayNoDuplicates(_keys, 0)) + require( + getCurrentListMap(_keys, _values, mask, extraKeys, zeroValue, minValue, 0, defaultEntry) + .contains(k) + ) + require(validKeyInArray(k)) + + lemmaKeyInListMapIsInArray( + _keys, + _values, + mask, + extraKeys, + zeroValue, + minValue, + k, + defaultEntry + ) + val i = arrayScanForKey(_keys, k, 0) + lemmaArrayForallSeekEntryOrOpenFoundFromSmallerThenFromBigger(_keys, mask, 0, i) + assert(arrayForallSeekEntryOrOpenFound(i)(_keys, mask)) + + } ensuring (_ => + (seekEntryOrOpen(k)(_keys, mask) match { + case Found(index) => inRange(index, mask) && _keys(index) == k + case _ => false + }) + ) + + @opaque + @inlineOnce + @pure + @ghost + def lemmaNotInListMapThenSeekEntryOrOpenFindsFreeOrNothing[V]( + _keys: Array[Long], + _values: Array[ValueCell[V]], + mask: Int, + extraKeys: Int, + zeroValue: V, + minValue: V, + k: Long, + defaultEntry: Long => V + ): Unit = { + require(validMask(mask)) + require(_values.length == mask + 1) + require(_keys.length == _values.length) + require(mask >= 0) + require(extraKeys >= 0) + require(extraKeys <= 3) + require(arrayForallSeekEntryOrOpenFound(0)(_keys, mask)) + require(arrayNoDuplicates(_keys, 0)) + require(validKeyInArray(k)) + require( + !getCurrentListMap(_keys, _values, mask, extraKeys, zeroValue, minValue, 0, defaultEntry) + .contains(k) + ) + + val seekEntryRes = seekEntryOrOpen(k)(_keys, mask) + seekEntryRes match { + case Found(index) => { + assert(_keys(index) == k) + assert(arrayContainsKey(_keys, k, index)) + lemmaArrayContainsFromImpliesContainsFromZero(_keys, k, index) + lemmaValidKeyInArrayIsInListMap( + _keys, + _values, + mask, + extraKeys, + zeroValue, + minValue, + index, + defaultEntry + ) + check(false) + } + case MissingZero(_) => { + if (arrayContainsKey(_keys, k, 0)) { + val i = arrayScanForKey(_keys, k, 0) + lemmaArrayContainsKeyThenInListMap( + _keys, + _values, + mask, + extraKeys, + zeroValue, + minValue, + k, + i, + defaultEntry + ) + check(false) + } + check(!arrayContainsKey(_keys, k, 0)) + } + case MissingVacant(_) => { + if (arrayContainsKey(_keys, k, 0)) { + val i = arrayScanForKey(_keys, k, 0) + lemmaArrayContainsKeyThenInListMap( + _keys, + _values, + mask, + extraKeys, + zeroValue, + minValue, + k, + i, + defaultEntry + ) + check(false) + } + check(!arrayContainsKey(_keys, k, 0)) + } + case Undefined() => () + } + + } ensuring (_ => { + val seekEntryRes = seekEntryOrOpen(k)(_keys, mask) + seekEntryRes match { + case MissingZero(index) => + inRange(index, mask) && _keys(index) == 0 && !arrayContainsKey(_keys, k, 0) + case MissingVacant(index) => + inRange(index, mask) && _keys(index) == Long.MinValue && !arrayContainsKey( + _keys, + k, + 0 + ) + case Undefined() => true + case _ => false + } + }) + + @opaque + @inlineOnce + @pure + @ghost + def lemmaSeekEntryOrOpenReturnsValidIndex[V]( + _keys: Array[Long], + _values: Array[ValueCell[V]], + mask: Int, + extraKeys: Int, + zeroValue: V, + minValue: V, + k: Long + ): Unit = { + require(validMask(mask)) + require(_values.length == mask + 1) + require(_keys.length == _values.length) + require(mask >= 0) + require(extraKeys >= 0) + require(extraKeys <= 3) + require(arrayForallSeekEntryOrOpenFound(0)(_keys, mask)) + require(arrayNoDuplicates(_keys, 0)) + require(validKeyInArray(k)) + + } ensuring (_ => + seekEntryOrOpen(k)(_keys, mask) match { + case Undefined() => true + case Found(index) => inRange(index, mask) + case MissingVacant(index) => inRange(index, mask) + case MissingZero(index) => inRange(index, mask) + case _ => false + } + ) + + @opaque + @inlineOnce + @pure + @ghost + def lemmaSeekEntryGivesInRangeIndex[V]( + _keys: Array[Long], + _values: Array[ValueCell[V]], + mask: Int, + extraKeys: Int, + zeroValue: V, + minValue: V, + k: Long + ): Unit = { + require(validMask(mask)) + require(_values.length == mask + 1) + require(_keys.length == _values.length) + require(mask >= 0) + require(extraKeys >= 0) + require(extraKeys <= 3) + require(arrayForallSeekEntryOrOpenFound(0)(_keys, mask)) + require(arrayNoDuplicates(_keys, 0)) + + require(validKeyInArray(k)) + + } ensuring (_ => + (seekEntry(k)(_keys, mask) match { + case Found(index) => inRange(index, mask) + case _ => true + }) + ) + + // ------------------END---------------------------------------------------------------------------------------------------------------------------------- + // ------------------SEEKENTRY RELATED-------------------------------------------------------------------------------------------------------------------- + + @opaque + @inlineOnce + @pure + @ghost + def lemmaPutLongMinValuePreservesForallSeekEntryOrOpenKey1Helper( + a: Array[Long], + i: Int, + j: Int, + x: Int, + index: Int, + vacantIndex: Int + )(implicit mask: Int): Unit = { + require(validMask(mask)) + require(a.length == mask + 1) + require(i >= 0) + require(i < a.length) + require(j >= 0) + require(j < a.length) + require(i != j) + require(validKeyInArray(a(i))) + require(validKeyInArray(a(j))) + require(arrayForallSeekEntryOrOpenFound(0)(a, mask)) + require(arrayNoDuplicates(a, 0)) + require(x >= 0 && x <= MAX_ITER) + require(index >= 0 && index < a.length) + require(vacantIndex >= 0 && vacantIndex < a.length) + require(a(vacantIndex) == Long.MinValue) + require( + seekKeyOrZeroReturnVacant(x, index, vacantIndex)(a(j), a, mask) == Found(j) + ) + + decreases(MAX_ITER - x) + if (a(index) == a(j)) { + // trivial + } else { + lemmaPutLongMinValuePreservesForallSeekEntryOrOpenKey1Helper( + a, + i, + j, + x + 1, + nextIndex(index, x, mask), + vacantIndex + ) + } + + } ensuring (_ => + seekKeyOrZeroReturnVacant(x, index, vacantIndex)( + a.updated(i, Long.MinValue).apply(j), + a.updated(i, Long.MinValue), + mask + ) == seekKeyOrZeroReturnVacant(x, index, vacantIndex)( + a(j), + a, + mask + ) + ) + + @opaque + @inlineOnce + @pure + @ghost + def lemmaPutLongMinValuePreservesForallSeekEntryOrOpenKey1( + a: Array[Long], + i: Int, + j: Int, + x: Int, + index: Int, + resX: Int, + resIndex: Int + )(implicit mask: Int): Unit = { + require(validMask(mask)) + require(a.length == mask + 1) + require(i >= 0) + require(i < a.length) + require(j >= 0) + require(j < a.length) + require(i != j) + require(validKeyInArray(a(i))) + require(validKeyInArray(a(j))) + require(arrayForallSeekEntryOrOpenFound(0)(a, mask)) + require(arrayNoDuplicates(a, 0)) + require(x >= 0 && x <= MAX_ITER) + require(resX >= 0 && resX <= MAX_ITER) + require(index >= 0 && index < a.length) + require(resIndex >= 0 && resIndex < a.length) + + require( + seekKeyOrZeroOrLongMinValue(0, toIndex(a(j), mask))( + a(j), + a, + mask + ) == Intermediate( + false, + resIndex, + resX + ) + ) + require( + seekKeyOrZeroOrLongMinValue(x, index)(a(j), a, mask) == Intermediate( + false, + resIndex, + resX + ) + ) + + require( + seekKeyOrZeroOrLongMinValue(0, toIndex(a(j), mask))(a(j), a, mask) == + seekKeyOrZeroOrLongMinValue( + 0, + toIndex(a.updated(i, Long.MinValue).apply(j), mask) + )( + a.updated(i, Long.MinValue).apply(j), + a.updated(i, Long.MinValue), + mask + ) + ) + + decreases(MAX_ITER - x) + lemmaArrayForallSeekEntryOrOpenFoundFromSmallerThenFromBigger(a, mask, 0, j) + + check(seekEntry(a(j))(a, mask) == Found(j)) + val newArray = a.updated(i, Long.MinValue) + assert(a(j) == newArray(j)) + + if (x == resX) { + assert(index == resIndex) + if (a(index) == a(j)) { + assert(j == index) + // trivial + } else { + assert(a(index) == Long.MinValue) + check( + seekEntryOrOpen(a(j))(a, mask) == seekKeyOrZeroReturnVacant( + x, + index, + index + )( + a(j), + a, + mask + ) + ) + check( + seekEntryOrOpen(newArray(j))( + newArray, + mask + ) == seekKeyOrZeroReturnVacant( + x, + index, + index + )(newArray(j), newArray, mask) + ) + lemmaPutLongMinValuePreservesForallSeekEntryOrOpenKey1Helper(a, i, j, x, index, index) + } + } else { + lemmaPutLongMinValuePreservesForallSeekEntryOrOpenKey1( + a, + i, + j, + x + 1, + nextIndex(index, x, mask), + resX, + resIndex + ) + } + + } ensuring (_ => + seekEntryOrOpen(a(j))(a, mask) == seekEntryOrOpen( + a.updated(i, Long.MinValue).apply(j) + )( + a.updated(i, Long.MinValue), + mask + ) + ) + + @opaque + @inlineOnce + @pure + @ghost + def lemmaPutLongMinValueIntermediateNotSameThenNewIsSmallerXAndAtI( + a: Array[Long], + i: Int, + j: Int, + x: Int, + index: Int, + intermediateBeforeX: Int, + intermediateBeforeIndex: Int, + intermediateAfterX: Int, + intermediateAfterIndex: Int, + undefinedAfter: Boolean + )(implicit mask: Int): Unit = { + require(validMask(mask)) + require(a.length == mask + 1) + require(i >= 0) + require(i < a.length) + require(j >= 0) + require(j < a.length) + require(i != j) + require(validKeyInArray(a(i))) + require(validKeyInArray(a(j))) + require(arrayForallSeekEntryOrOpenFound(0)(a, mask)) + require(arrayNoDuplicates(a, 0)) + require(x >= 0 && x <= MAX_ITER) + require(intermediateBeforeX >= 0 && intermediateBeforeX <= MAX_ITER) + require(index >= 0 && index < a.length) + require(intermediateBeforeIndex >= 0 && intermediateBeforeIndex < a.length) + + require( + seekKeyOrZeroOrLongMinValue(0, toIndex(a(j), mask))( + a(j), + a, + mask + ) == Intermediate( + false, + intermediateBeforeIndex, + intermediateBeforeX + ) + ) + require( + seekKeyOrZeroOrLongMinValue( + 0, + toIndex(a.updated(i, Long.MinValue).apply(j), mask) + )( + a.updated(i, Long.MinValue).apply(j), + a.updated(i, Long.MinValue), + mask + ) == + Intermediate(undefinedAfter, intermediateAfterIndex, intermediateAfterX) + ) + + require( + seekKeyOrZeroOrLongMinValue(x, index)(a(j), a, mask) == Intermediate( + false, + intermediateBeforeIndex, + intermediateBeforeX + ) + ) + require( + seekKeyOrZeroOrLongMinValue(x, index)( + a.updated(i, Long.MinValue).apply(j), + a.updated(i, Long.MinValue), + mask + ) == + Intermediate(undefinedAfter, intermediateAfterIndex, intermediateAfterX) + ) + + require( + Intermediate(false, intermediateBeforeIndex, intermediateBeforeX) != Intermediate( + undefinedAfter, + intermediateAfterIndex, + intermediateAfterX + ) + ) + + decreases(MAX_ITER - x) + + lemmaArrayForallSeekEntryOrOpenFoundFromSmallerThenFromBigger(a, mask, 0, j) + check(seekEntryOrOpen(a(j))(a, mask) == Found(j)) + + if (a(index) == Long.MinValue) { + check(false) + } else if (a(index) == 0) { + check(false) + } else if (a(index) == a(j)) { + check(false) + } else if (a.updated(i, Long.MinValue).apply(index) == Long.MinValue) { + check(a(index) != Long.MinValue) + check(a(index) != 0) + check(a(index) != a(j)) + check(index == intermediateAfterIndex) + check(intermediateAfterIndex == i) + check(intermediateAfterX < intermediateBeforeX) + } else { + lemmaPutLongMinValueIntermediateNotSameThenNewIsSmallerXAndAtI( + a, + i, + j, + x + 1, + nextIndex(index, x, mask), + intermediateBeforeX, + intermediateBeforeIndex, + intermediateAfterX, + intermediateAfterIndex, + undefinedAfter + ) + } + + } ensuring (_ => !undefinedAfter && intermediateAfterIndex == i && intermediateAfterX < intermediateBeforeX) + + @opaque + @inlineOnce + @pure + @ghost + def lemmaPutLongMinValuePreservesSeekKeyOrZeroReturnVacant( + a: Array[Long], + i: Int, + j: Int, + x: Int, + index: Int, + vacantBefore: Int, + vacantAfter: Int + )(implicit mask: Int): Unit = { + require(validMask(mask)) + require(a.length == mask + 1) + require(i >= 0) + require(i < a.length) + require(j >= 0) // 5 + require(j < a.length) + require(i != j) + require(validKeyInArray(a(i))) + require(validKeyInArray(a(j))) + require(arrayForallSeekEntryOrOpenFound(0)(a, mask)) + require(arrayNoDuplicates(a, 0)) + require(x >= 0 && x <= MAX_ITER) + require(vacantBefore >= 0 && vacantBefore < a.length) + require(vacantAfter >= 0 && vacantAfter < a.length) + require(a(vacantBefore) == Long.MinValue) + require(a.updated(i, Long.MinValue).apply(vacantAfter) == Long.MinValue) + require(vacantAfter == i) + require(index >= 0 && index < a.length) + require( + seekKeyOrZeroReturnVacant(x, index, vacantBefore)(a(j), a, mask) == Found(j) + ) + + decreases(MAX_ITER - x) + + if (a(index) == a(j)) { + check(j == index) + // trivial + } else { + lemmaPutLongMinValuePreservesSeekKeyOrZeroReturnVacant( + a, + i, + j, + x + 1, + nextIndex(index, x, mask), + vacantBefore, + vacantAfter + ) + } + + } ensuring (_ => + seekKeyOrZeroReturnVacant(x, index, vacantAfter)( + a.updated(i, Long.MinValue).apply(j), + a.updated(i, Long.MinValue), + mask + ) == Found(j) + ) + + @opaque + @inlineOnce + @pure + @ghost + def lemmaPutLongMinValuePreservesForallSeekEntryOrOpenKey2Helper( + a: Array[Long], + i: Int, + j: Int, + x: Int, + index: Int, + intermediateBeforeX: Int, + intermediateBeforeIndex: Int, + intermediateAfterX: Int, + intermediateAfterIndex: Int + )(implicit mask: Int): Unit = { + + require(validMask(mask)) + require(a.length == mask + 1) + require(i >= 0) + require(i < a.length) + require(j >= 0) + require(j < a.length) + require(i != j) + require(validKeyInArray(a(i))) + require(validKeyInArray(a(j))) + require(arrayForallSeekEntryOrOpenFound(0)(a, mask)) + require(arrayNoDuplicates(a, 0)) + require(x >= 0 && x <= MAX_ITER) + require(intermediateBeforeX >= 0 && intermediateBeforeX <= MAX_ITER) + require(intermediateAfterX >= 0 && intermediateAfterX <= MAX_ITER) + require(index >= 0 && index < a.length) + require(intermediateBeforeIndex >= 0 && intermediateBeforeIndex < a.length) + require(intermediateAfterIndex >= 0 && intermediateAfterIndex < a.length) + + require(intermediateAfterX < intermediateBeforeX) + require(a.updated(i, Long.MinValue).apply(intermediateAfterIndex) == Long.MinValue) + + require( + seekKeyOrZeroOrLongMinValue(0, toIndex(a(j), mask))( + a(j), + a, + mask + ) == Intermediate( + false, + intermediateBeforeIndex, + intermediateBeforeX + ) + ) + require( + seekKeyOrZeroOrLongMinValue(x, index)(a(j), a, mask) == Intermediate( + false, + intermediateBeforeIndex, + intermediateBeforeX + ) + ) + require( + seekKeyOrZeroOrLongMinValue( + 0, + toIndex(a.updated(i, Long.MinValue).apply(j), mask) + )( + a.updated(i, Long.MinValue).apply(j), + a.updated(i, Long.MinValue), + mask + ) == + Intermediate(false, intermediateAfterIndex, intermediateAfterX) + ) + + require( + if (x <= intermediateAfterX) + seekKeyOrZeroOrLongMinValue( + 0, + toIndex(a.updated(i, Long.MinValue).apply(j), mask) + )( + a.updated(i, Long.MinValue).apply(j), + a.updated(i, Long.MinValue), + mask + ) == + seekKeyOrZeroOrLongMinValue(x, index)( + a.updated(i, Long.MinValue).apply(j), + a.updated(i, Long.MinValue), + mask + ) + else + seekKeyOrZeroReturnVacant(x, index, intermediateAfterIndex)( + a.updated(i, Long.MinValue).apply(j), + a.updated(i, Long.MinValue), + mask + ) == seekEntryOrOpen( + a.updated(i, Long.MinValue).apply(j) + )(a.updated(i, Long.MinValue), mask) + ) + require(x <= intermediateBeforeX) + require(intermediateAfterIndex == i) + decreases(MAX_ITER - x) + + lemmaArrayForallSeekEntryOrOpenFoundFromSmallerThenFromBigger(a, mask, 0, j) + check(seekEntryOrOpen(a(j))(a, mask) == Found(j)) + check(a(intermediateBeforeIndex) == Long.MinValue || a(intermediateBeforeIndex) == a(j)) + + if (a(index) == a.updated(i, Long.MinValue).apply(index) && a(index) == a(j)) { + check( + seekEntryOrOpen(a(j))(a, mask) == seekEntryOrOpen( + a.updated(i, Long.MinValue).apply(j) + )( + a.updated(i, Long.MinValue), + mask + ) + ) + } else if (a(index) == Long.MinValue) { + check( + seekEntryOrOpen(a(j))(a, mask) == seekKeyOrZeroReturnVacant( + x, + index, + index + )( + a(j), + a, + mask + ) + ) + check(index == intermediateBeforeIndex) + check(x == intermediateBeforeX) + lemmaPutLongMinValuePreservesSeekKeyOrZeroReturnVacant( + a, + i, + j, + x, + index, + intermediateBeforeIndex, + intermediateAfterIndex + )(mask) + } else if (x == intermediateAfterX) { + check(index == intermediateAfterIndex) + check(a.updated(i, Long.MinValue).apply(index) == Long.MinValue) + check( + seekEntryOrOpen(a.updated(i, Long.MinValue).apply(j))( + a.updated(i, Long.MinValue), + mask + ) == seekKeyOrZeroReturnVacant(x, index, intermediateAfterIndex)( + a.updated(i, Long.MinValue).apply(j), + a.updated(i, Long.MinValue), + mask + ) + ) + lemmaPutLongMinValuePreservesForallSeekEntryOrOpenKey2Helper( + a, + i, + j, + x + 1, + nextIndex(index, x, mask), + intermediateBeforeX, + intermediateBeforeIndex, + intermediateAfterX, + intermediateAfterIndex + ) + } else { + lemmaPutLongMinValuePreservesForallSeekEntryOrOpenKey2Helper( + a, + i, + j, + x + 1, + nextIndex(index, x, mask), + intermediateBeforeX, + intermediateBeforeIndex, + intermediateAfterX, + intermediateAfterIndex + ) + + } + + } ensuring (_ => + seekEntryOrOpen(a(j))(a, mask) == seekEntryOrOpen( + a.updated(i, Long.MinValue).apply(j) + )( + a.updated(i, Long.MinValue), + mask + ) + ) + + @opaque + @inlineOnce + @pure + @ghost + def lemmaPutLongMinValuePreservesForallSeekEntryOrOpenKey2(a: Array[Long], i: Int, j: Int)(implicit + mask: Int + ): Unit = { + require(validMask(mask)) + require(a.length == mask + 1) + require(i >= 0) + require(i < a.length) + require(j >= 0) + require(j < a.length) + require(i != j) + require(validKeyInArray(a(i))) + require(validKeyInArray(a(j))) + require(arrayForallSeekEntryOrOpenFound(0)(a, mask)) + require(arrayNoDuplicates(a, 0)) + val intermediateBefore = + seekKeyOrZeroOrLongMinValue(0, toIndex(a(j), mask))(a(j), a, mask) + + lemmaArrayForallSeekEntryOrOpenFoundFromSmallerThenFromBigger(a, mask, 0, j) + check(seekEntryOrOpen(a(j))(a, mask) == Found(j)) + intermediateBefore match { + case Intermediate(undefined, intermediateBeforeIndex, intermediateBeforeX) => { + if (undefined) { + check(false) + } else { + if ( + seekKeyOrZeroOrLongMinValue(0, toIndex(a(j), mask))(a(j), a, mask) == + seekKeyOrZeroOrLongMinValue( + 0, + toIndex(a.updated(i, Long.MinValue).apply(j), mask) + )( + a.updated(i, Long.MinValue).apply(j), + a.updated(i, Long.MinValue), + mask + ) + ) { + lemmaPutLongMinValuePreservesForallSeekEntryOrOpenKey1( + a, + i, + j, + 0, + toIndex(a(j), mask), + intermediateBeforeX, + intermediateBeforeIndex + )(mask) + } else { + val intermediateAfter = seekKeyOrZeroOrLongMinValue( + 0, + toIndex(a.updated(i, Long.MinValue).apply(j), mask) + )(a.updated(i, Long.MinValue).apply(j), a.updated(i, Long.MinValue), mask) + + intermediateAfter match { + case Intermediate(undefined, intermediateAfterIndex, intermediateAfterX) => { + lemmaPutLongMinValueIntermediateNotSameThenNewIsSmallerXAndAtI( + a, + i, + j, + 0, + toIndex(a(j), mask), + intermediateBeforeX, + intermediateBeforeIndex, + intermediateAfterX, + intermediateAfterIndex, + undefined + ) + check(!undefined) + check(intermediateAfterIndex == i) + check(a.updated(i, Long.MinValue).apply(intermediateAfterIndex) == Long.MinValue) + check( + seekEntryOrOpen(a.updated(i, Long.MinValue).apply(j))( + a.updated(i, Long.MinValue), + mask + ) == + seekKeyOrZeroReturnVacant( + intermediateAfterX, + intermediateAfterIndex, + intermediateAfterIndex + )(a(j), a.updated(i, Long.MinValue), mask) + ) + lemmaPutLongMinValuePreservesForallSeekEntryOrOpenKey2Helper( + a, + i, + j, + 0, + toIndex(a(j), mask), + intermediateBeforeX, + intermediateBeforeIndex, + intermediateAfterX, + intermediateAfterIndex + )(mask) + } + case _ => check(false) + } + } + } + } + case _ => check(false) + } + + } ensuring (_ => + seekEntryOrOpen(a(j))(a, mask) == seekEntryOrOpen( + a.updated(i, Long.MinValue).apply(j) + )( + a.updated(i, Long.MinValue), + mask + ) + ) + + @opaque + @inlineOnce + @pure + @ghost + def lemmaPutLongMinValuePreservesForallSeekEntryOrOpenStartIndex( + a: Array[Long], + i: Int, + startIndex: Int + )(implicit + mask: Int + ): Unit = { + require(validMask(mask)) + require(a.length == mask + 1) + require(i >= 0) + require(i < a.length) + require(startIndex >= 0) + require(startIndex < a.length) + require(validKeyInArray(a(i))) + require(arrayNoDuplicates(a, 0)) + require(arrayForallSeekEntryOrOpenFound(0)(a, mask)) + + decreases(a.length - startIndex) + + assert(a(i) != 0 && a(i) != Long.MinValue) + val newArray = a.updated(i, Long.MinValue) + + if (startIndex != i && validKeyInArray(a(startIndex))) { + lemmaPutLongMinValuePreservesForallSeekEntryOrOpenKey2(a, i, startIndex)(mask) + lemmaArrayForallSeekEntryOrOpenFoundFromSmallerThenFromBigger(a, mask, 0, startIndex) + } + if (startIndex < a.length - 1) { + lemmaPutLongMinValuePreservesForallSeekEntryOrOpenStartIndex(a, i, startIndex + 1) + } + + } ensuring (_ => arrayForallSeekEntryOrOpenFound(startIndex)(a.updated(i, Long.MinValue), mask)) + + @opaque + @inlineOnce + @pure + @ghost + def lemmaPutLongMinValuePreservesForallSeekEntryOrOpen(a: Array[Long], i: Int)(implicit + mask: Int + ): Unit = { + require(validMask(mask)) + require(a.length == mask + 1) + require(i >= 0) + require(i < a.length) + require(validKeyInArray(a(i))) + require(arrayNoDuplicates(a, 0)) + require(arrayForallSeekEntryOrOpenFound(0)(a, mask)) + + assert(a(i) != 0 && a(i) != Long.MinValue) + lemmaPutLongMinValuePreservesForallSeekEntryOrOpenStartIndex(a, i, 0)(mask) + + } ensuring (_ => arrayForallSeekEntryOrOpenFound(0)(a.updated(i, Long.MinValue), mask)) + + @opaque + @inlineOnce + @pure + @ghost + def lemmaPutValidKeyPreservesseekKeyOrZeroReturnVacant( + a: Array[Long], + i: Int, + k: Long, + j: Int, + x: Int, + index: Int, + vacantSpotIndex: Int + )(implicit mask: Int): Unit = { + require(validMask(mask)) + require(a.length == mask + 1) + require(i >= 0) + require(i < a.length) + require(j >= 0) + require(j < a.length) + require(i != j) + require(validKeyInArray(a(j))) + require(validKeyInArray(k)) + require(!arrayContainsKey(a, k, 0)) + require( + seekEntryOrOpen(k)(a, mask) == MissingZero(i) || seekEntryOrOpen(k)( + a, + mask + ) == MissingVacant( + i + ) + ) + require(arrayForallSeekEntryOrOpenFound(0)(a, mask)) + require(arrayNoDuplicates(a, 0)) + require(index >= 0 && index < mask + 1) + require(x <= MAX_ITER && x >= 0) + require(vacantSpotIndex >= 0 && vacantSpotIndex < mask + 1) + require(a(vacantSpotIndex) == Long.MinValue) + require(a.updated(i, k).apply(vacantSpotIndex) == Long.MinValue) + require( + seekKeyOrZeroReturnVacant(x, index, vacantSpotIndex)( + a(j), + a, + mask + ) == Found(j) + ) + + decreases(MAX_ITER - x) + + val resBefore = + seekKeyOrZeroReturnVacant(x, index, vacantSpotIndex)(a(j), a, mask) + + if (x >= MAX_ITER) { + check(false) + } else if (a(index) == a(j)) { + assert(index == j) + + } else if (a(index) == 0) { + check(false) + } else { + assert(index != j) + lemmaPutValidKeyPreservesseekKeyOrZeroReturnVacant( + a, + i, + k, + j, + x + 1, + nextIndex(index, x, mask), + vacantSpotIndex + ) + check( + seekKeyOrZeroReturnVacant(x, index, vacantSpotIndex)(a(j), a, mask) == + seekKeyOrZeroReturnVacant( + x + 1, + nextIndex(index, x, mask), + vacantSpotIndex + )( + a(j), + a, + mask + ) + ) + + if ( + seekKeyOrZeroReturnVacant(x, index, vacantSpotIndex)( + a.updated(i, k).apply(j), + a.updated(i, k), + mask + ) == Undefined() + ) { + check(false) + } + if ( + seekKeyOrZeroReturnVacant(x, index, vacantSpotIndex)( + a.updated(i, k).apply(j), + a.updated(i, k), + mask + ) == Found(index) + ) { + check(a.updated(i, k).apply(index) == a(j)) + val q = a(j) + val newArray = a.updated(i, k) + assert(q == a.updated(i, k).apply(j)) + assert(q == a.updated(i, k).apply(index)) + if (j < index) { + check(arrayContainsKey(newArray, q, j)) + check(arrayContainsKey(newArray, q, index)) + lemmaArrayContainsFromImpliesContainsFromSmaller(newArray, q, index, j + 1) + check(arrayContainsKey(newArray, q, j + 1)) + check(arrayNoDuplicates(a, 0)) + lemmaPutNewValidKeyPreservesNoDuplicate(a, k, i, 0, List()) + check(arrayNoDuplicates(newArray, 0)) + lemmaNoDuplicateFromThenFromBigger(newArray, 0, j) + lemmaArrayNoDuplicateThenKeysContainedNotEqual(newArray, q, j, Nil()) + + check(false) + } + if (index < j) { + check(arrayContainsKey(newArray, q, j)) + check(arrayContainsKey(newArray, q, index)) + lemmaArrayContainsFromImpliesContainsFromSmaller(newArray, q, j, index + 1) + check(arrayContainsKey(newArray, q, index + 1)) + check(arrayNoDuplicates(a, 0)) + lemmaPutNewValidKeyPreservesNoDuplicate(a, k, i, 0, List()) + check(arrayNoDuplicates(newArray, 0)) + lemmaNoDuplicateFromThenFromBigger(newArray, 0, index) + lemmaArrayNoDuplicateThenKeysContainedNotEqual(newArray, q, index, Nil()) + + check(false) + } + check(index == j) + check(false) + } + if ( + seekKeyOrZeroReturnVacant(x, index, vacantSpotIndex)( + a.updated(i, k).apply(j), + a.updated(i, k), + mask + ) == MissingVacant(vacantSpotIndex) + ) { + check(a.updated(i, k).apply(index) == 0) + check(false) + } + + } + + } ensuring (_ => + seekKeyOrZeroReturnVacant(x, index, vacantSpotIndex)(a(j), a, mask) == + seekKeyOrZeroReturnVacant(x, index, vacantSpotIndex)( + a.updated(i, k).apply(j), + a.updated(i, k), + mask + ) + ) + + @opaque + @inlineOnce + @pure + @ghost + def lemmaPutValidKeyPreservesForallSeekEntryOrOpenKey1(a: Array[Long], i: Int, k: Long, j: Int)(implicit + mask: Int + ): Unit = { + require(validMask(mask)) + require(a.length == mask + 1) + require(i >= 0) + require(i < a.length) + require(j >= 0) + require(j < a.length) + require(i != j) + require(validKeyInArray(a(j))) + require(validKeyInArray(k)) + require(!arrayContainsKey(a, k, 0)) + require( + seekEntryOrOpen(k)(a, mask) == MissingZero(i) || seekEntryOrOpen(k)( + a, + mask + ) == MissingVacant( + i + ) + ) + require(arrayForallSeekEntryOrOpenFound(0)(a, mask)) + require(arrayNoDuplicates(a, 0)) + require( + seekKeyOrZeroOrLongMinValue(0, toIndex(a(j), mask))(a(j), a, mask) == + seekKeyOrZeroOrLongMinValue(0, toIndex(a.updated(i, k).apply(j), mask))( + a.updated(i, k).apply(j), + a.updated(i, k), + mask + ) + ) + + lemmaArrayForallSeekEntryOrOpenFoundFromSmallerThenFromBigger(a, mask, 0, j) + check(seekEntryOrOpen(a(j))(a, mask) == Found(j)) + + val intermediateBefore = + seekKeyOrZeroOrLongMinValue(0, toIndex(a(j), mask))(a(j), a, mask) + val intermediateAfter = + seekKeyOrZeroOrLongMinValue(0, toIndex(a.updated(i, k).apply(j), mask))( + a.updated(i, k).apply(j), + a.updated(i, k), + mask + ) + assert(intermediateBefore == intermediateAfter) + intermediateBefore match { + case Intermediate(undefined, index, x) if (undefined) => {} + case Intermediate(undefined, index, x) if (!undefined) => { + if (a(index) == a(j)) {} else if (a(index) == 0) { + check(seekEntryOrOpen(a(j))(a, mask) == MissingZero(index)) + check(arrayForallSeekEntryOrOpenFound(0)(a, mask)) + lemmaArrayForallSeekEntryOrOpenFoundFromSmallerThenFromBigger(a, mask, 0, j) + check(false) + } else { + check( + seekEntryOrOpen(a(j))(a, mask) == seekKeyOrZeroReturnVacant( + x, + index, + index + )( + a(j), + a, + mask + ) + ) + check( + seekEntryOrOpen(a.updated(i, k).apply(j))(a.updated(i, k), mask) == + seekKeyOrZeroReturnVacant(x, index, index)( + a.updated(i, k).apply(j), + a.updated(i, k), + mask + ) + ) + lemmaPutValidKeyPreservesseekKeyOrZeroReturnVacant( + a, + i, + k, + j, + x, + index, + index + )(mask) + + } + } + } + + } ensuring (_ => + seekEntryOrOpen(a(j))(a, mask) == seekEntryOrOpen(a.updated(i, k).apply(j))( + a.updated(i, k), + mask + ) + ) + + @opaque + @inlineOnce + @pure + @ghost + def lemmaseekKeyOrZeroOrLongMinValueFoundKeyThenSameAfterChangingI( + a: Array[Long], + i: Int, + k: Long, + j: Int, + index: Int, + x: Int, + resIndex: Int, + resX: Int + )(implicit mask: Int): Unit = { + require(validMask(mask)) + require(a.length == mask + 1) + require(i >= 0) + require(i < a.length) + require(j >= 0) + require(j < a.length) + require(i != j) + require(validKeyInArray(a(j))) + require(validKeyInArray(k)) + require(!arrayContainsKey(a, k, 0)) + require( + seekEntryOrOpen(k)(a, mask) == MissingZero(i) || seekEntryOrOpen(k)( + a, + mask + ) == MissingVacant( + i + ) + ) + require(arrayForallSeekEntryOrOpenFound(0)(a, mask)) + require(arrayNoDuplicates(a, 0)) + require(resX <= MAX_ITER) + require(x <= resX) + require(x >= 0) + require(index >= 0 && index < a.length) + require(resIndex >= 0 && resIndex < a.length) + require(a(resIndex) == a(j)) + require( + seekKeyOrZeroOrLongMinValue(x, index)(a(j), a, mask) == Intermediate( + false, + resIndex, + resX + ) + ) + require( + seekKeyOrZeroOrLongMinValue(0, toIndex(a(j), mask))( + a(j), + a, + mask + ) == seekKeyOrZeroOrLongMinValue(x, index)(a(j), a, mask) + ) + + decreases(MAX_ITER - x) + if (a(index) == 0) { + check(false) + } else if (a(index) == Long.MinValue) { + check(false) + } else if (a(index) == a(j)) { + check(index == resIndex) + } else { + lemmaseekKeyOrZeroOrLongMinValueFoundKeyThenSameAfterChangingI( + a, + i, + k, + j, + nextIndex(index, x, mask), + x + 1, + resIndex, + resX + ) + } + + } ensuring (_ => + seekKeyOrZeroOrLongMinValue(x, index)( + a.updated(i, k).apply(j), + a.updated(i, k), + mask + ) == Intermediate(false, resIndex, resX) + ) + + @opaque + @inlineOnce + @pure + @ghost + def lemmaPutValidKeyPreservesForallSeekEntryOrOpenKey2AfterFindingLongMinValueLater( + a: Array[Long], + i: Int, + k: Long, + j: Int, + index: Int, + x: Int, + vacantBefore: Int, + vacantAfter: Int + )(implicit mask: Int): Unit = { + require(validMask(mask)) + require(a.length == mask + 1) + require(i >= 0) + require(i < a.length) + require(j >= 0) + require(j < a.length) + require(i != j) + require(validKeyInArray(a(j))) + require(validKeyInArray(k)) + require(!arrayContainsKey(a, k, 0)) + require( + seekEntryOrOpen(k)(a, mask) == MissingZero(i) || seekEntryOrOpen(k)( + a, + mask + ) == MissingVacant( + i + ) + ) + require(arrayForallSeekEntryOrOpenFound(0)(a, mask)) + require(arrayNoDuplicates(a, 0)) + require(x <= MAX_ITER) + require(x >= 0) + require(index >= 0 && index < a.length) + require(vacantBefore == i) + require(a(vacantBefore) == Long.MinValue) + require(vacantAfter >= 0 && vacantAfter < a.length) + require(a(vacantAfter) == Long.MinValue) + require(vacantAfter != vacantBefore) + require( + seekEntryOrOpen(a.updated(i, k).apply(j))( + a.updated(i, k), + mask + ) == seekKeyOrZeroReturnVacant(x, index, vacantAfter)( + a.updated(i, k).apply(j), + a.updated(i, k), + mask + ) + ) + require( + seekEntryOrOpen(a(j))(a, mask) == seekKeyOrZeroReturnVacant( + x, + index, + vacantBefore + )( + a(j), + a, + mask + ) + ) + require( + seekKeyOrZeroReturnVacant(x, index, vacantBefore)(a(j), a, mask) == Found(j) + ) + + decreases(MAX_ITER - x) + + if (a(index) == a(j)) { + check( + seekKeyOrZeroReturnVacant(x, index, vacantAfter)( + a.updated(i, k).apply(j), + a.updated(i, k), + mask + ) == Found(index) + ) + check( + seekKeyOrZeroReturnVacant(x, index, vacantAfter)( + a.updated(i, k).apply(j), + a.updated(i, k), + mask + ) == Found(j) + ) + } else { + lemmaPutValidKeyPreservesForallSeekEntryOrOpenKey2AfterFindingLongMinValueLater( + a, + i, + k, + j, + nextIndex(index, x, mask), + x + 1, + vacantBefore, + vacantAfter + ) + check( + seekKeyOrZeroReturnVacant(x, index, vacantAfter)( + a.updated(i, k).apply(j), + a.updated(i, k), + mask + ) == Found(j) + ) + } + + } ensuring (_ => + seekKeyOrZeroReturnVacant(x, index, vacantAfter)( + a.updated(i, k).apply(j), + a.updated(i, k), + mask + ) == seekKeyOrZeroReturnVacant(x, index, vacantBefore)(a(j), a, mask) + ) + + @opaque + @inlineOnce + @pure + @ghost + def lemmaPutValidKeyPreservesVacantIsAtI( + a: Array[Long], + i: Int, + k: Long, + j: Int, + index: Int, + x: Int, + resIntermediateIndex: Int, + resIntermediateX: Int + )(implicit mask: Int): Unit = { + require(validMask(mask)) + require(a.length == mask + 1) + require(i >= 0) + require(i < a.length) + require(j >= 0) + require(j < a.length) + require(i != j) + require(validKeyInArray(a(j))) + require(validKeyInArray(k)) + require(!arrayContainsKey(a, k, 0)) + require( + seekEntryOrOpen(k)(a, mask) == MissingZero(i) || seekEntryOrOpen(k)( + a, + mask + ) == MissingVacant( + i + ) + ) + require(arrayForallSeekEntryOrOpenFound(0)(a, mask)) + require(arrayNoDuplicates(a, 0)) + require(x <= MAX_ITER) + require(x >= 0) + require(resIntermediateX <= MAX_ITER) + require(index >= 0 && index < a.length) + require(resIntermediateIndex >= 0 && resIntermediateIndex < a.length) + require( + seekKeyOrZeroOrLongMinValue(0, toIndex(a(j), mask))( + a(j), + a, + mask + ) == Intermediate( + false, + resIntermediateIndex, + resIntermediateX + ) + ) + require(resIntermediateIndex == i) + require(a(resIntermediateIndex) == Long.MinValue) + require( + if (x <= resIntermediateX) + seekKeyOrZeroOrLongMinValue(x, index)(a(j), a, mask) == Intermediate( + false, + resIntermediateIndex, + resIntermediateX + ) + else + seekKeyOrZeroReturnVacant(x, index, resIntermediateIndex)( + a(j), + a, + mask + ) == Found(j) + ) + require( + seekKeyOrZeroOrLongMinValue(0, toIndex(a.updated(i, k).apply(j), mask))( + a.updated(i, k).apply(j), + a.updated(i, k), + mask + ) == + seekKeyOrZeroOrLongMinValue(x, index)( + a.updated(i, k).apply(j), + a.updated(i, k), + mask + ) + ) + + decreases(MAX_ITER - x) + + lemmaArrayForallSeekEntryOrOpenFoundFromSmallerThenFromBigger(a, mask, 0, j) + check(seekEntryOrOpen(a(j))(a, mask) == Found(j)) + check( + seekKeyOrZeroReturnVacant( + resIntermediateX, + resIntermediateIndex, + resIntermediateIndex + )( + a(j), + a, + mask + ) == Found(j) + ) + + val intermediateAfter = + seekKeyOrZeroOrLongMinValue(x, index)( + a.updated(i, k).apply(j), + a.updated(i, k), + mask + ) + intermediateAfter match { + case Intermediate(undefined, indexIntermediateAfter, xIntermediateAfter) => { + if (x < xIntermediateAfter) { + val nextIndexVal = nextIndex(index, x, mask) + val nextX = x + 1 + + if (nextX <= resIntermediateX) { + check( + if (nextX <= resIntermediateX) + seekKeyOrZeroOrLongMinValue(nextX, nextIndexVal)(a(j), a, mask) == Intermediate( + false, + resIntermediateIndex, + resIntermediateX + ) + else + seekKeyOrZeroReturnVacant(nextX, nextIndexVal, resIntermediateIndex)( + a(j), + a, + mask + ) == Found(j) + ) + } else { + if ( + seekKeyOrZeroReturnVacant(x, index, resIntermediateIndex)( + a(j), + a, + mask + ) == Found(j) + ) { + check( + seekKeyOrZeroReturnVacant( + nextX, + nextIndexVal, + resIntermediateIndex + )( + a(j), + a, + mask + ) == Found(j) + ) + } else { + check( + seekKeyOrZeroReturnVacant( + nextX, + nextIndexVal, + resIntermediateIndex + )( + a(j), + a, + mask + ) == Found(j) + ) + } + } + lemmaPutValidKeyPreservesVacantIsAtI( + a, + i, + k, + j, + nextIndexVal, + nextX, + resIntermediateIndex, + resIntermediateX + ) + } else { + assert(x == xIntermediateAfter) + assert(index == indexIntermediateAfter) + assert( + seekKeyOrZeroReturnVacant(x, index, resIntermediateIndex)( + a(j), + a, + mask + ) == Found(j) + ) + if (a.updated(i, k).apply(index) == a.updated(i, k).apply(j)) { + assert(!undefined) + check( + seekEntryOrOpen(a.updated(i, k).apply(j))(a.updated(i, k), mask) == Found( + index + ) + ) + } else { + if (a.updated(i, k).apply(index) == 0) { + check( + seekEntryOrOpen(a(j))( + a, + mask + ) == seekKeyOrZeroReturnVacant( + x, + index, + resIntermediateIndex + )(a(j), a, mask) + ) + check( + seekKeyOrZeroReturnVacant(x, index, resIntermediateIndex)( + a(j), + a, + mask + ) == MissingVacant(resIntermediateIndex) + ) + check(false) + } + check(a.updated(i, k).apply(index) == Long.MinValue) + check( + seekEntryOrOpen(a.updated(i, k).apply(j))( + a.updated(i, k), + mask + ) == seekKeyOrZeroReturnVacant(x, index, index)( + a.updated(i, k).apply(j), + a.updated(i, k), + mask + ) + ) + check( + seekEntryOrOpen(a(j))( + a, + mask + ) == seekKeyOrZeroReturnVacant( + x, + index, + resIntermediateIndex + )(a(j), a, mask) + ) + check( + seekKeyOrZeroReturnVacant(x, index, resIntermediateIndex)( + a(j), + a, + mask + ) == Found(j) + ) + + lemmaPutValidKeyPreservesForallSeekEntryOrOpenKey2AfterFindingLongMinValueLater( + a, + i, + k, + j, + index, + x, + resIntermediateIndex, + index + ) + } + } + } + case _ => check(false) + } + + } ensuring (_ => seekEntryOrOpen(a.updated(i, k).apply(j))(a.updated(i, k), mask) == Found(j)) + + @opaque + @inlineOnce + @pure + @ghost + def lemmaseekKeyOrZeroOrLongMinValueThenChangedAtReturnedIndex( + a: Array[Long], + i: Int, + k: Long, + j: Int, + index: Int, + x: Int, + resIndex: Int, + resX: Int + )(implicit mask: Int): Unit = { + require(validMask(mask)) + require(a.length == mask + 1) + require(i >= 0) + require(i < a.length) + require(j >= 0) + require(j < a.length) + require(i != j) + require(validKeyInArray(a(j))) + require(validKeyInArray(k)) + require(!arrayContainsKey(a, k, 0)) + require( + seekEntryOrOpen(k)(a, mask) == MissingVacant(i) || seekEntryOrOpen(k)( + a, + mask + ) == MissingZero( + i + ) + ) + require(arrayForallSeekEntryOrOpenFound(0)(a, mask)) + require(arrayNoDuplicates(a, 0)) + require(x <= resX) + require(x >= 0) + require(resX <= MAX_ITER) + require(index >= 0 && index < a.length) + require(resIndex >= 0 && resIndex < a.length) + require( + seekKeyOrZeroOrLongMinValue(0, toIndex(a(j), mask))( + a(j), + a, + mask + ) == Intermediate( + false, + resIndex, + resX + ) + ) + require( + seekKeyOrZeroOrLongMinValue(x, index)(a(j), a, mask) == Intermediate( + false, + resIndex, + resX + ) + ) + require( + seekKeyOrZeroOrLongMinValue(0, toIndex(a(j), mask))( + a(j), + a, + mask + ) != seekKeyOrZeroOrLongMinValue( + 0, + toIndex(a.updated(i, k).apply(j), mask) + )( + a.updated(i, k).apply(j), + a.updated(i, k), + mask + ) + ) + require( + seekKeyOrZeroOrLongMinValue(x, index)( + a(j), + a, + mask + ) != seekKeyOrZeroOrLongMinValue(x, index)( + a.updated(i, k).apply(j), + a.updated(i, k), + mask + ) + ) + + decreases(MAX_ITER - x) + + if (index == resIndex) { + assert(x == resX) + // trivial + } else { + assert(index != i) + lemmaseekKeyOrZeroOrLongMinValueThenChangedAtReturnedIndex( + a, + i, + k, + j, + nextIndex(index, x, mask), + x + 1, + resIndex, + resX + ) + + } + } ensuring (_ => resIndex == i) + + @opaque + @inlineOnce + @pure + @ghost + def lemmaPutValidKeyPreservesForallSeekEntryOrOpenKey2(a: Array[Long], i: Int, k: Long, j: Int)(implicit + mask: Int + ): Unit = { + require(validMask(mask)) + require(a.length == mask + 1) + require(i >= 0) + require(i < a.length) + require(j >= 0) + require(j < a.length) + require(i != j) + require(validKeyInArray(a(j))) + require(validKeyInArray(k)) + require(!arrayContainsKey(a, k, 0)) + require( + seekEntryOrOpen(k)(a, mask) == MissingZero(i) || seekEntryOrOpen(k)( + a, + mask + ) == MissingVacant( + i + ) + ) + require(arrayForallSeekEntryOrOpenFound(0)(a, mask)) + require(arrayNoDuplicates(a, 0)) + + lemmaArrayForallSeekEntryOrOpenFoundFromSmallerThenFromBigger(a, mask, 0, j) + check(seekEntryOrOpen(a(j))(a, mask) == Found(j)) + val newArray = a.updated(i, k) + + if ( + seekKeyOrZeroOrLongMinValue(0, toIndex(a(j), mask))(a(j), a, mask) == + seekKeyOrZeroOrLongMinValue(0, toIndex(newArray.apply(j), mask))( + newArray.apply(j), + newArray, + mask + ) + ) { + lemmaPutValidKeyPreservesForallSeekEntryOrOpenKey1(a, i, k, j)(mask) + check( + seekEntryOrOpen(a(j))(a, mask) == seekEntryOrOpen(newArray.apply(j))( + newArray, + mask + ) + ) + } else { + val intermediateBefore = + seekKeyOrZeroOrLongMinValue(0, toIndex(a(j), mask))(a(j), a, mask) + intermediateBefore match { + case Intermediate(undefined, index, x) if (undefined) => { + check(seekEntryOrOpen(a(j))(a, mask) == Undefined()) + lemmaArrayForallSeekEntryOrOpenFoundFromSmallerThenFromBigger(a, mask, 0, j) + check(seekEntryOrOpen(a(j))(a, mask) == Found(j)) + check(false) + } + case Intermediate(undefinedBefore, indexBefore, xBefore) if (!undefinedBefore) => { + check(!undefinedBefore) + check(xBefore < MAX_ITER) + check(a(indexBefore) == a(j) || a(indexBefore) == 0 || a(indexBefore) == Long.MinValue) + if (a(indexBefore) == a(j)) { + lemmaseekKeyOrZeroOrLongMinValueFoundKeyThenSameAfterChangingI( + a, + i, + k, + j, + toIndex(a(j), mask), + 0, + indexBefore, + xBefore + )(mask) + check(false) + } + check(a(indexBefore) == 0 || a(indexBefore) == Long.MinValue) + if (a(indexBefore) == 0) { + check(false) + } + check(a(indexBefore) == Long.MinValue) + + lemmaseekKeyOrZeroOrLongMinValueThenChangedAtReturnedIndex( + a, + i, + k, + j, + toIndex(a(j), mask), + 0, + indexBefore, + xBefore + )(mask) + lemmaPutValidKeyPreservesVacantIsAtI( + a, + i, + k, + j, + toIndex(a(j), mask), + 0, + indexBefore, + xBefore + )(mask) + } + case _ => { + check(false) + check( + seekEntryOrOpen(a(j))(a, mask) == seekEntryOrOpen( + newArray.apply(j) + )(newArray, mask) + ) + } + } + } + + } ensuring (_ => + seekEntryOrOpen(a(j))(a, mask) == seekEntryOrOpen(a.updated(i, k).apply(j))( + a.updated(i, k), + mask + ) + ) + + @opaque + @inlineOnce + @pure + @ghost + def lemmaPutValidKeyAtRightPlaceThenFindsHelper1( + a: Array[Long], + i: Int, + k: Long, + x: Int, + index: Int, + resIndex: Int, + resX: Int + )(implicit mask: Int): Unit = { + require(validMask(mask)) + require(a.length == mask + 1) + require(i >= 0) + require(i < a.length) + require(validKeyInArray(k)) + require(!arrayContainsKey(a, k, 0)) + require(seekEntryOrOpen(k)(a, mask) == MissingZero(i)) + require(arrayForallSeekEntryOrOpenFound(0)(a, mask)) + require( + seekKeyOrZeroOrLongMinValue(0, toIndex(k, mask))( + k, + a, + mask + ) == Intermediate( + false, + resIndex, + resX + ) + ) + require(a(resIndex) == 0) + require(resIndex == i) + require(x <= resX) + require(x >= 0) + require(index >= 0 && index < a.length) + require( + seekKeyOrZeroOrLongMinValue(x, index)(k, a, mask) == Intermediate( + false, + resIndex, + resX + ) + ) + + decreases(MAX_ITER - x) + assert(resX < MAX_ITER) + assert(a(index) != k) + if (x == resX) { + if (resIndex != index) { + if (a(index) == 0 || a(index) == Long.MinValue) { + check( + seekKeyOrZeroOrLongMinValue(x, index)(k, a, mask) == Intermediate( + false, + index, + resX + ) + ) + check(false) + } else { + check( + seekKeyOrZeroOrLongMinValue(x, index)(k, a, mask) == + seekKeyOrZeroOrLongMinValue( + x + 1, + nextIndex(index, x, mask) + )(k, a, mask) + ) + seekKeyOrZeroOrLongMinValue( + x + 1, + nextIndex(index, x, mask) + )(k, a, mask) match { + case Intermediate(undefined, tempIndex, tempX) => assert(tempX >= x + 1) + case _ => check(false) + } + check(false) + } + } + check(index == resIndex) + check(a(index) == 0) + } else { + check( + seekKeyOrZeroOrLongMinValue(x, index)( + k, + a, + mask + ) == seekKeyOrZeroOrLongMinValue( + x + 1, + nextIndex(index, x, mask) + )(k, a, mask) + ) + check( + seekKeyOrZeroOrLongMinValue(x, index)( + k, + a.updated(i, k), + mask + ) == seekKeyOrZeroOrLongMinValue( + x + 1, + nextIndex(index, x, mask) + )( + k, + a.updated(i, k), + mask + ) + ) + lemmaPutValidKeyAtRightPlaceThenFindsHelper1( + a, + i, + k, + x + 1, + nextIndex(index, x, mask), + resIndex, + resX + ) + } + + } ensuring (_ => + seekKeyOrZeroOrLongMinValue(x, index)( + k, + a.updated(i, k), + mask + ) == seekKeyOrZeroOrLongMinValue(x, index)(k, a, mask) + ) + + def lemmaPutValidKeyAtRightPlaceThenFindsHelper2( + a: Array[Long], + i: Int, + k: Long, + x: Int, + index: Int, + resIndex: Int, + resX: Int + )(implicit mask: Int): Unit = { + require(validMask(mask)) + require(a.length == mask + 1) + require(i >= 0) + require(i < a.length) + require(validKeyInArray(k)) + require(!arrayContainsKey(a, k, 0)) + require(seekEntryOrOpen(k)(a, mask) == MissingVacant(i)) + require(arrayForallSeekEntryOrOpenFound(0)(a, mask)) + require( + seekKeyOrZeroOrLongMinValue(0, toIndex(k, mask))( + k, + a, + mask + ) == Intermediate( + false, + resIndex, + resX + ) + ) + require(a(resIndex) == Long.MinValue) + require(resIndex == i) + require(x <= resX) + require(x >= 0) + require(index >= 0 && index < a.length) + require( + seekKeyOrZeroOrLongMinValue(x, index)(k, a, mask) == Intermediate( + false, + resIndex, + resX + ) + ) + decreases(MAX_ITER - x) + assert(resX < MAX_ITER) + assert(a(index) != k) + if (x == resX) { + if (resIndex != index) { + if (a(index) == 0 || a(index) == Long.MinValue) { + check( + seekKeyOrZeroOrLongMinValue(x, index)(k, a, mask) == Intermediate( + false, + index, + resX + ) + ) + check(false) + } else { + seekKeyOrZeroOrLongMinValue( + x + 1, + nextIndex(index, x, mask) + )(k, a, mask) match { + case Intermediate(undefined, tempIndex, tempX) => assert(tempX >= x + 1) + case _ => check(false) + } + check(false) + } + } + check(index == resIndex) + check(a(index) == Long.MinValue) + } else { + lemmaPutValidKeyAtRightPlaceThenFindsHelper2( + a, + i, + k, + x + 1, + nextIndex(index, x, mask), + resIndex, + resX + ) + } + + } ensuring (_ => + seekKeyOrZeroOrLongMinValue(x, index)( + k, + a.updated(i, k), + mask + ) == seekKeyOrZeroOrLongMinValue(x, index)(k, a, mask) + ) + + @opaque + @inlineOnce + @pure + @ghost + def lemmaPutValidKeyAtRightPlaceThenFinds(a: Array[Long], i: Int, k: Long)(implicit + mask: Int + ): Unit = { + require(validMask(mask)) + require(a.length == mask + 1) + require(i >= 0) + require(i < a.length) + require(validKeyInArray(k)) + require(!arrayContainsKey(a, k, 0)) + require( + seekEntryOrOpen(k)(a, mask) == MissingZero(i) || seekEntryOrOpen(k)( + a, + mask + ) == MissingVacant( + i + ) + ) + require(arrayForallSeekEntryOrOpenFound(0)(a, mask)) + + val intermediateBefore = + seekKeyOrZeroOrLongMinValue(0, toIndex(k, mask))(k, a, mask) + val intermediateAfter = + seekKeyOrZeroOrLongMinValue(0, toIndex(k, mask))(k, a.updated(i, k), mask) + + if (seekEntryOrOpen(k)(a, mask) == MissingZero(i)) { + intermediateBefore match { + case Intermediate(undefined, index, x) if (undefined) => check(false) + case Intermediate(undefined, index, x) if (a(index) == Long.MinValue) => check(false) + case Intermediate(undefined, index, x) if (a(index) == k) => check(false) + case Intermediate(undefined, index, x) => { + check(a(index) == 0) + check(index == i) + lemmaPutValidKeyAtRightPlaceThenFindsHelper1(a, i, k, 0, toIndex(k, mask), index, x)( + mask + ) + check(a.updated(i, k).apply(index) == k) + } + case _ => check(false) + } + check(intermediateAfter == intermediateBefore) + } else { + assert(seekEntryOrOpen(k)(a, mask) == MissingVacant(i)) + intermediateBefore match { + case Intermediate(undefined, index, x) if (undefined) => check(false) + case Intermediate(undefined, index, x) if (a(index) == Long.MinValue) => { + check(index == i) + lemmaPutValidKeyAtRightPlaceThenFindsHelper2(a, i, k, 0, toIndex(k, mask), index, x)( + mask + ) + + } + case Intermediate(undefined, index, x) if (a(index) == k) => check(false) + case Intermediate(undefined, index, x) => check(false) + case _ => check(false) + } + + } + + } ensuring (_ => seekEntryOrOpen(k)(a.updated(i, k), mask) == Found(i)) + + @opaque + @inlineOnce + @pure + @ghost + def lemmaPutValidKeyPreservesForallSeekEntryOrOpenStartIndex( + a: Array[Long], + i: Int, + k: Long, + startIndex: Int + )(implicit + mask: Int + ): Unit = { + require(validMask(mask)) + require(a.length == mask + 1) + require(i >= 0) + require(i < a.length) + require(startIndex >= 0) + require(startIndex < a.length) + require(validKeyInArray(k)) + require(arrayNoDuplicates(a, 0)) + require(!arrayContainsKey(a, k, 0)) + require( + seekEntryOrOpen(k)(a, mask) == MissingZero(i) || seekEntryOrOpen(k)( + a, + mask + ) == MissingVacant( + i + ) + ) + require(arrayForallSeekEntryOrOpenFound(0)(a, mask)) + + require(arrayForallSeekEntryOrOpenFound(0)(a, mask)) + + decreases(a.length - startIndex) + + val newArray = a.updated(i, k) + if (startIndex == i) { + if (startIndex < a.length - 1) { + lemmaPutValidKeyPreservesForallSeekEntryOrOpenStartIndex(a, i, k, startIndex + 1) + } + lemmaPutValidKeyAtRightPlaceThenFinds(a, i, k)(mask) + check(seekEntryOrOpen(k)(newArray, mask) == Found(i)) + } else { + if (validKeyInArray(a(startIndex))) { + lemmaPutNewValidKeyPreservesNoDuplicate(a, k, i, 0, Nil()) + lemmaPutValidKeyPreservesForallSeekEntryOrOpenKey2(a, i, k, startIndex)(mask) + lemmaArrayForallSeekEntryOrOpenFoundFromSmallerThenFromBigger(a, mask, 0, startIndex) + if (startIndex < a.length - 1) { + lemmaPutValidKeyPreservesForallSeekEntryOrOpenStartIndex(a, i, k, startIndex + 1) + } + + } else { + if (startIndex < a.length - 1) { + lemmaPutValidKeyPreservesForallSeekEntryOrOpenStartIndex(a, i, k, startIndex + 1) + } + } + } + } ensuring (_ => arrayForallSeekEntryOrOpenFound(startIndex)(a.updated(i, k), mask)) + + @opaque + @inlineOnce + @pure + @ghost + def lemmaPutValidKeyPreservesForallSeekEntryOrOpen(k: Long, a: Array[Long], i: Int)(implicit + mask: Int + ): Unit = { + require(validMask(mask)) + require(a.length == mask + 1) + require(i >= 0) + require(i < a.length) + require(validKeyInArray(k)) + require(!arrayContainsKey(a, k, 0)) + require( + seekEntryOrOpen(k)(a, mask) == MissingZero(i) || seekEntryOrOpen(k)( + a, + mask + ) == MissingVacant( + i + ) + ) + require(arrayForallSeekEntryOrOpenFound(0)(a, mask)) + require(arrayNoDuplicates(a, 0)) + + lemmaPutValidKeyPreservesForallSeekEntryOrOpenStartIndex(a, i, k, 0)(mask) + + } ensuring (_ => arrayForallSeekEntryOrOpenFound(0)(a.updated(i, k), mask)) + + @opaque + @inlineOnce + @pure + @ghost + def lemmaSeekEntryOrOpenFindsThenSeekEntryFinds( + k: Long, + i: Int, + a: Array[Long], + mask: Int + ): Unit = { + require(validMask(mask)) + require(a.length == mask + 1) + require(arrayForallSeekEntryOrOpenFound(0)(a, mask)) + require(validKeyInArray(k)) + require(seekEntryOrOpen(k)(a, mask) == Found(i)) + + } ensuring (_ => + (seekEntry(k)(a, mask) match { + case Found(index) => index == i + case _ => false + }) + ) + + @opaque + @inlineOnce + @pure + @ghost + def lemmaSeekEntryOrOpenMissThenSeekEntryMiss( + k: Long, + i: Int, + a: Array[Long], + mask: Int + ): Unit = { + require(validMask(mask)) + require(a.length == mask + 1) + require(arrayForallSeekEntryOrOpenFound(0)(a, mask)) + require(validKeyInArray(k)) + require(seekEntryOrOpen(k)(a, mask) match { + case Found(_) => false + case _ => true + }) + + } ensuring (_ => + seekEntry(k)(a, mask) match { + case Found(_) => false + case _ => true + } + ) + + // ARRAY UTILITY FUNCTIONS ---------------------------------------------------------------------------------------- + + @pure + @ghost + def validKeyInArray(l: Long): Boolean = { + l != 0 && l != Long.MinValue + } + + @pure + @ghost + def arrayCountValidKeys( + a: Array[Long], + from: Int, + to: Int + ): Int = { + require(from <= to && from >= 0 && to <= a.length) + require(a.length < Integer.MAX_VALUE) + + decreases(a.length - from) + if (from >= to) { + 0 + } else { + if (validKeyInArray(a(from))) { + 1 + arrayCountValidKeys(a, from + 1, to) + } else { + arrayCountValidKeys(a, from + 1, to) + } + } + } ensuring (res => res >= 0 && res <= a.length - from) + + @pure + @ghost + def arrayContainsKey(a: Array[Long], k: Long, from: Int): Boolean = { + require(from >= 0) + require(from < a.length) + require(a.length < Integer.MAX_VALUE) + + decreases(a.length - from) + if (a(from) == k) { + true + } else if (from + 1 < a.length) { + arrayContainsKey(a, k, from + 1) + } else { + false + } + } + + @pure + @ghost + def arrayScanForKey(a: Array[Long], k: Long, from: Int): Int = { + require(from >= 0 && from < a.length && a.length < Integer.MAX_VALUE) + require(arrayContainsKey(a, k, from)) + decreases(a.length - from) + + if (a(from) == k) from + else arrayScanForKey(a, k, from + 1) + } ensuring (res => res >= 0 && res < a.length && a(res) == k) + + @pure + @ghost + def arrayNoDuplicates(a: Array[Long], from: Int, acc: List[Long] = Nil[Long]()): Boolean = { + require(from >= 0 && from <= a.length) + require(a.length < Integer.MAX_VALUE) + require(ListOps.noDuplicate(acc)) + require(!acc.contains(0) && !acc.contains(Long.MinValue)) + decreases(a.length - from) + + if (from >= a.length) true + else if (validKeyInArray(a(from)) && acc.contains(a(from))) false + else if (validKeyInArray(a(from))) arrayNoDuplicates(a, from + 1, Cons(a(from), acc)) + else arrayNoDuplicates(a, from + 1, acc) + } + + /** Return true iff the two arrays contain the same elements from the index "from" included to the index "to" not included + * + * @param a1 + * @param a2 + * @param from + * @param to + * @return + */ + @pure + @ghost + def arraysEqualsFromTo(a1: Array[Long], a2: Array[Long], from: Int, to: Int): Boolean = { + require( + a1.length == a2.length && from >= 0 && from <= to && to <= a1.length && a1.length < Integer.MAX_VALUE + ) + + decreases(to + 1 - from) + if (from >= to) { + true + } else if (a1(from) != a2(from)) { + false + } else { + arraysEqualsFromTo(a1, a2, from + 1, to) + } + } + + // --------------------- ARRAY RELATED LEMMAS ------------------------------------------ + + @opaque + @inlineOnce + @pure + @ghost + def lemmaArrayNoDuplicateRemoveOneThenNotContain(a: Array[Long], i: Int, k: Long): Unit = { + require(i >= 0) + require(i < a.length) + require(a.length < Integer.MAX_VALUE) + require(arrayNoDuplicates(a, 0)) + require(validKeyInArray(k)) + require(a(i) == k) + + if (arrayContainsKey(a.updated(i, Long.MinValue), k, 0)) { + val j = arrayScanForKey(a.updated(i, Long.MinValue), k, 0) + assert(j != i) + check(a.updated(i, Long.MinValue).apply(j) == k) + if (j > i) { + check(arrayContainsKey(a, k, j)) + lemmaNoDuplicateFromThenFromBigger(a, 0, i) + lemmaArrayContainsFromImpliesContainsFromSmaller(a, k, j, i + 1) + lemmaArrayNoDuplicateThenKeysContainedNotEqual(a, k, i, Nil()) + check(false) + } else if (i > j) { + check(arrayContainsKey(a, k, i)) + lemmaNoDuplicateFromThenFromBigger(a, 0, j) + lemmaArrayContainsFromImpliesContainsFromSmaller(a, k, i, j + 1) + lemmaArrayNoDuplicateThenKeysContainedNotEqual(a, k, j, Nil()) + check(false) + } + + } + + } ensuring (_ => !arrayContainsKey(a.updated(i, Long.MinValue), k, 0)) + + @opaque + @inlineOnce + @pure + @ghost + def lemmaArrayContainsFromImpliesContainsFromZero( + a: Array[Long], + k: Long, + from: Int + ): Unit = { + require(from >= 0) + require(from < a.length) + require(a.length < Integer.MAX_VALUE) + require(arrayContainsKey(a, k, from)) + + decreases(from) + if (from > 0) { + lemmaArrayContainsFromImpliesContainsFromZero(a, k, from - 1) + } + } ensuring (_ => arrayContainsKey(a, k, 0)) + + @opaque + @inlineOnce + @pure + @ghost + def lemmaArrayContainsFromImpliesContainsFromSmaller( + a: Array[Long], + k: Long, + from: Int, + newFrom: Int + ): Unit = { + require(from >= 0) + require(from < a.length) + require(newFrom >= 0) + require(newFrom <= from) + require(a.length < Integer.MAX_VALUE) + require(arrayContainsKey(a, k, from)) + + decreases(from) + if (from > newFrom) { + lemmaArrayContainsFromImpliesContainsFromSmaller(a, k, from - 1, newFrom) + } + } ensuring (_ => arrayContainsKey(a, k, newFrom)) + + @opaque + @inlineOnce + @pure + @ghost + def lemmaAddKeyNoContainsInAccStillNoDuplicate( + a: Array[Long], + k: Long, + from: Int, + acc: List[Long], + newAcc: List[Long] + ): Unit = { + require(a.length < Integer.MAX_VALUE) + require(from >= 0) + require(from < a.length) + require(ListOps.noDuplicate(acc)) + require(ListOps.noDuplicate(newAcc)) + require(!acc.contains(0) && !acc.contains(Long.MinValue)) + require(!arrayContainsKey(a, k, from)) + require(!acc.contains(k)) + require(validKeyInArray(k)) + require(arrayNoDuplicates(a, from, acc)) + require(ListSpecs.subseq(acc, newAcc)) + require(newAcc.contains(k)) + require(newAcc - k == acc) + require(!newAcc.contains(0) && !newAcc.contains(Long.MinValue)) + decreases(a.length - from) + + if (from + 1 < a.length) { + if (validKeyInArray(a(from))) { + lemmaAddKeyNoContainsInAccStillNoDuplicate( + a, + k, + from + 1, + a(from) :: acc, + a(from) :: newAcc + ) + } else { + lemmaAddKeyNoContainsInAccStillNoDuplicate(a, k, from + 1, acc, newAcc) + } + } + } ensuring (_ => arrayNoDuplicates(a, from, newAcc)) + + @opaque + @inlineOnce + @pure + @ghost + def lemmaListMinusENotContainedEqualsList(e: Long, l: List[Long]): Unit = { + require(!l.contains(e)) + decreases(l) + + l match { + case head :: tl => lemmaListMinusENotContainedEqualsList(e, tl) + case Nil() => + } + } ensuring (_ => l - e == l) + + @opaque + @inlineOnce + @pure + @ghost + def lemmaLMinusHeadEqualsTail(head: Long, tail: List[Long]): Unit = { + require(!tail.contains(head)) + assert(!tail.content.contains(head)) + + val l = head :: tail + l match { + case hd :: tl => lemmaListMinusENotContainedEqualsList(head, tl) + case Nil() => () + } + + } ensuring (_ => (head :: tail) - head == tail) + + @opaque + @inlineOnce + @pure + @ghost + def lemmaPutNewValidKeyPreservesNoDuplicate( + a: Array[Long], + k: Long, + i: Int, + from: Int, + acc: List[Long] + ): Unit = { + require(a.length < Integer.MAX_VALUE) + require(from >= 0) + require(from < a.length) + require(ListOps.noDuplicate(acc)) + require(!acc.contains(0) && !acc.contains(Long.MinValue)) + require((from > i && acc.contains(k)) || (from <= i && !acc.contains(k))) + require(arrayNoDuplicates(a, 0)) + require(arrayNoDuplicates(a, from, acc)) + require(i >= 0 && i < a.length) + require(validKeyInArray(k)) + require(!arrayContainsKey(a, k, 0)) + decreases(a.length - from) + + if (from + 1 < a.length) { + if (from == i) { + val newArray = a.updated(i, k) + check(arrayContainsKey(newArray, k, from)) + + if (arrayContainsKey(a, k, from + 1)) { + lemmaArrayContainsFromImpliesContainsFromZero(a, k, from) + check(false) + } + if (validKeyInArray(a(from))) { + lemmaListSubSeqRefl(acc) + lemmaArrayNoDuplicateWithAnAccThenWithSubSeqAcc(a, a(from) :: acc, acc, from + 1) + } else {} + check(arrayNoDuplicates(a, from + 1, acc)) + lemmaListSubSeqRefl(acc) + lemmaLMinusHeadEqualsTail(k, acc) + lemmaAddKeyNoContainsInAccStillNoDuplicate(a, k, from + 1, acc, k :: acc) + lemmaPutNewValidKeyPreservesNoDuplicate(a, k, i, from + 1, k :: acc) + } else { + if (validKeyInArray(a(from))) { + if (a(from) == k) { + check(arrayContainsKey(a, k, from)) + lemmaArrayContainsFromImpliesContainsFromZero(a, k, from) + check(false) + } + assert(a(from) != k) + lemmaPutNewValidKeyPreservesNoDuplicate(a, k, i, from + 1, a(from) :: acc) + } else { + lemmaPutNewValidKeyPreservesNoDuplicate(a, k, i, from + 1, acc) + } + } + } + } ensuring (_ => arrayNoDuplicates(a.updated(i, k), from, acc)) + + @opaque + @inlineOnce + @pure + @ghost + def lemmaPutNonValidKeyPreservesNoDuplicate( + a: Array[Long], + l: Long, + i: Int, + from: Int, + acc: List[Long] + ): Unit = { + require(a.length < Integer.MAX_VALUE) + require(from >= 0) + require(from < a.length) + require(ListOps.noDuplicate(acc)) + require(!acc.contains(0) && !acc.contains(Long.MinValue)) + require(arrayNoDuplicates(a, 0)) + require(arrayNoDuplicates(a, from, acc)) + require(i >= 0 && i < a.length) + require(!validKeyInArray(l)) + decreases(a.length - from) + + if (from + 1 < a.length) { + if (validKeyInArray(a(from))) { + lemmaListSubSeqRefl(acc) + lemmaArrayNoDuplicateWithAnAccThenWithSubSeqAcc(a, a(from) :: acc, acc, from + 1) + } + if (from == i) { + lemmaPutNonValidKeyPreservesNoDuplicate(a, l, i, from + 1, acc) + } else { + if (validKeyInArray(a(from))) { + lemmaPutNonValidKeyPreservesNoDuplicate(a, l, i, from + 1, a(from) :: acc) + } else { + lemmaPutNonValidKeyPreservesNoDuplicate(a, l, i, from + 1, acc) + } + } + } + } ensuring (_ => arrayNoDuplicates(a.updated(i, l), from, acc)) + + @opaque + @inlineOnce + @pure + @ghost + def lemmaArrayNoDuplicateThenKeysContainedNotEqual( + a: Array[Long], + k: Long, + from: Int, + acc: List[Long] + ): Unit = { + require(a.length < Integer.MAX_VALUE) + require(validKeyInArray(k)) + require(from >= 0) + require(from + 1 < a.length) + require(from < a.length) + require(ListOps.noDuplicate(acc)) + require(!acc.contains(0) && !acc.contains(Long.MinValue)) + require(arrayContainsKey(a, k, from + 1)) + require(arrayNoDuplicates(a, from, acc)) + + if (validKeyInArray(a(from))) { + check(arrayNoDuplicates(a, from + 1, Cons(a(from), acc))) + lemmaArrayNoDuplicateWithAnAccThenWithSubSeqAcc(a, acc, Nil(), from) + check(arrayNoDuplicates(a, from + 1, Cons(a(from), Nil()))) + lemmaArrayNoDuplicateFromNotContainsKeysInAcc(a, from + 1, a(from), Cons(a(from), Nil())) + check(!arrayContainsKey(a, a(from), from + 1)) + } + + } ensuring (_ => a(from) != k) + + @opaque + @inlineOnce + @pure + @ghost + def lemmaArrayNoDuplicateFromNotContainsKeysInAcc( + a: Array[Long], + from: Int, + k: Long, + acc: List[Long] + ): Unit = { + require(a.length < Integer.MAX_VALUE) + require(from >= 0) + require(from < a.length) + require(ListOps.noDuplicate(acc)) + require(!acc.contains(0) && !acc.contains(Long.MinValue)) + require(arrayNoDuplicates(a, from, acc)) + require(acc.contains(k)) + + decreases(a.length - from) + + if (from != a.length - 1) { + if (validKeyInArray(a(from))) { + lemmaListSubSeqRefl(Cons(a(from), acc)) + ListSpecs.subseqTail(Cons(a(from), acc), Cons(a(from), acc)) + check(ListSpecs.subseq(acc, Cons(a(from), acc))) + lemmaArrayNoDuplicateWithAnAccThenWithSubSeqAcc(a, Cons(a(from), acc), acc, from + 1) + } + lemmaArrayNoDuplicateFromNotContainsKeysInAcc(a, from + 1, k, acc) + } + + } ensuring (_ => !arrayContainsKey(a, k, from)) + + @opaque + @inlineOnce + @pure + @ghost + def lemmaListSubSeqRefl(l: List[Long]): Unit = { + decreases(l) + l match { + case head :: tl => { + assert(head == head) + lemmaListSubSeqRefl(tl) + } + case Nil() => + } + + } ensuring (_ => ListSpecs.subseq(l, l)) + + @opaque + @inlineOnce + @pure + @ghost + def lemmaArrayNoDuplicateWithAnAccThenWithSubSeqAcc( + a: Array[Long], + acc: List[Long], + newAcc: List[Long], + from: Int + ): Unit = { + require(a.length < Integer.MAX_VALUE) + require(from >= 0) + require(from <= a.length) + require(ListOps.noDuplicate(acc)) + require(!acc.contains(0) && !acc.contains(Long.MinValue)) + require(!newAcc.contains(0) && !newAcc.contains(Long.MinValue)) + require(ListSpecs.subseq(newAcc, acc)) + require({ + ListSpecs.noDuplicateSubseq(newAcc, acc) + arrayNoDuplicates(a, from, acc) + }) + decreases(a.length - from) + + if (from < a.length) { + val k = a(from) + if (validKeyInArray(k)) { + if (!acc.contains(k)) { + lemmaArrayNoDuplicateWithAnAccThenWithSubSeqAcc( + a, + Cons(k, acc), + Cons(k, newAcc), + from + 1 + ) + ListSpecs.noDuplicateSubseq(Cons(k, newAcc), Cons(k, acc)) + } + } else { + lemmaArrayNoDuplicateWithAnAccThenWithSubSeqAcc(a, acc, newAcc, from + 1) + } + } + + } ensuring (_ => arrayNoDuplicates(a, from, newAcc)) + + @opaque + @inlineOnce + @pure + @ghost + def lemmaNoDuplicateFromThenFromBigger(a: Array[Long], from: Int, newFrom: Int): Unit = { + require(a.length < Integer.MAX_VALUE) + require(from >= 0) + require(from <= a.length) + require(newFrom >= from && newFrom <= a.length) + require(arrayNoDuplicates(a, from)) + + decreases(newFrom - from) + + if (from != newFrom) { + assert(from < a.length) + if (newFrom != a.length) { + if (validKeyInArray(a(from))) { + lemmaArrayNoDuplicateWithAnAccThenWithSubSeqAcc( + a, + Cons(a(from), Nil()), + Nil(), + from + 1 + ) + } + lemmaNoDuplicateFromThenFromBigger(a, from + 1, newFrom) + } + } + + } ensuring (_ => arrayNoDuplicates(a, newFrom)) + + @opaque + @inlineOnce + @pure + @ghost + def lemmaArrayForallSeekEntryOrOpenFoundFromSmallerThenFromBigger( + a: Array[Long], + mask: Int, + from: Int, + newFrom: Int + ): Unit = { + require(validMask(mask)) + require(a.length == mask + 1) + require(from >= 0 && from <= a.length) + require(newFrom >= from && newFrom <= a.length) + require(arrayForallSeekEntryOrOpenFound(from)(a, mask)) + + decreases(newFrom - from) + + if (from < newFrom) { + lemmaArrayForallSeekEntryOrOpenFoundFromSmallerThenFromBigger(a, mask, from + 1, newFrom) + } + } ensuring (_ => arrayForallSeekEntryOrOpenFound(newFrom)(a, mask)) + + @opaque + @inlineOnce + @pure + @ghost + def lemmaCountingValidKeysAtTheEnd(a: Array[Long], from: Int, to: Int): Unit = { + require(a.length < Integer.MAX_VALUE && from >= 0 && to > from && to <= a.length) + + decreases(to - from) + if (from + 1 < to) { + lemmaCountingValidKeysAtTheEnd(a, from + 1, to) + } + } ensuring (_ => + if (validKeyInArray(a(to - 1))) + arrayCountValidKeys(a, from, to - 1) + 1 == arrayCountValidKeys(a, from, to) + else + arrayCountValidKeys(a, from, to - 1) == arrayCountValidKeys(a, from, to) + ) + + @opaque + @inlineOnce + @pure + @ghost + def lemmaKnownPivotPlusOneIsPivot(a: Array[Long], from: Int, to: Int, pivot: Int): Unit = { + require( + a.length < Integer.MAX_VALUE && from >= 0 && to > from && to <= a.length && pivot >= from && pivot < to - 1 && + LongMapFixedSize.isPivot(a, from, to, pivot) + ) + + lemmaCountingValidKeysAtTheEnd(a, from, pivot + 1) + + } ensuring (_ => LongMapFixedSize.isPivot(a, from, to, pivot + 1)) + + @pure + @ghost + def isPivot(a: Array[Long], from: Int, to: Int, pivot: Int): Boolean = { + require( + a.length < Integer.MAX_VALUE && from >= 0 && to > from && to <= a.length && pivot >= from && pivot < to + ) + arrayCountValidKeys(a, from, pivot) + arrayCountValidKeys( + a, + pivot, + to + ) == arrayCountValidKeys(a, from, to) + } + + @opaque + @inlineOnce + @pure + @ghost + def lemmaSumOfNumOfKeysOfSubArraysIsEqualToWholeFromTo( + a: Array[Long], + from: Int, + to: Int, + pivot: Int, + knownPivot: Int + ): Unit = { + require( + a.length < Integer.MAX_VALUE && from >= 0 && to >= from && to <= a.length && + pivot >= from && pivot < to && + knownPivot <= pivot && knownPivot >= from && + isPivot(a, from, to, knownPivot) + ) + + decreases(pivot - knownPivot) + if (knownPivot != pivot) { + lemmaKnownPivotPlusOneIsPivot(a, from, to, knownPivot) + lemmaSumOfNumOfKeysOfSubArraysIsEqualToWholeFromTo(a, from, to, pivot, knownPivot + 1) + } + } ensuring (_ => isPivot(a, from, to, pivot)) + + @opaque + @inlineOnce + @pure + @ghost + def lemmaSumOfNumOfKeysOfSubArraysIsEqualToWhole( + a: Array[Long], + from: Int, + to: Int, + pivot: Int + ): Unit = { + require( + a.length < Integer.MAX_VALUE && from >= 0 && to >= from && to <= a.length && pivot >= from && pivot <= to + ) + + if (pivot < to) { + lemmaSumOfNumOfKeysOfSubArraysIsEqualToWholeFromTo(a, from, to, pivot, from) + } + + } ensuring (_ => + arrayCountValidKeys(a, from, pivot) + arrayCountValidKeys( + a, + pivot, + to + ) == arrayCountValidKeys(a, from, to) + ) + + @opaque + @inlineOnce + @pure + @ghost + def lemmaRemoveValidKeyAndNumKeysToImpliesToALength( + a: Array[Long], + i: Int, + k: Long, + to: Int + ): Unit = { + require(i >= 0 && i < a.length) + require(validKeyInArray(a(i))) + require(!validKeyInArray(k)) + require(a.length < Integer.MAX_VALUE) + require(to >= 0 && to <= a.length && to > i) + require( + (arrayCountValidKeys(a.updated(i, k), i + 1, to) == arrayCountValidKeys( + a, + i + 1, + to + )) + ) + decreases(a.length + 1 - to) + + if (to != a.length) { + if (validKeyInArray(a(to))) { + lemmaValidKeyIncreasesNumOfKeys(a, i + 1, to) + lemmaValidKeyIncreasesNumOfKeys(a.updated(i, k), i + 1, to) + } else { + lemmaNotValidKeyDoesNotIncreaseNumOfKeys(a, i + 1, to) + lemmaNotValidKeyDoesNotIncreaseNumOfKeys(a.updated(i, k), i + 1, to) + } + lemmaRemoveValidKeyAndNumKeysToImpliesToALength(a, i, k, to + 1) + } + } ensuring (_ => + arrayCountValidKeys(a.updated(i, k), i + 1, a.length) == arrayCountValidKeys( + a, + i + 1, + a.length + ) + ) + + @opaque + @inlineOnce + @pure + @ghost + def lemmaRemoveValidKeyAndNumKeysFromImpliesFromZero( + a: Array[Long], + i: Int, + k: Long, + from: Int + ): Unit = { + require(i >= 0 && i < a.length) + require(validKeyInArray(a(i))) + require(!validKeyInArray(k)) + require(a.length < Integer.MAX_VALUE) + require(from >= 0 && from <= a.length && i >= from) + require( + (arrayCountValidKeys(a.updated(i, k), from, i + 1) == arrayCountValidKeys( + a, + from, + i + 1 + ) - 1) + ) + + decreases(from) + + if (from > 0) { + lemmaSumOfNumOfKeysOfSubArraysIsEqualToWhole(a, from - 1, i + 1, from) + lemmaSumOfNumOfKeysOfSubArraysIsEqualToWhole(a.updated(i, k), from - 1, i + 1, from) + lemmaRemoveValidKeyAndNumKeysFromImpliesFromZero(a, i, k, from - 1) + } + + } ensuring (_ => { + arrayCountValidKeys(a.updated(i, k), 0, i + 1) == arrayCountValidKeys( + a, + 0, + i + 1 + ) - 1 + }) + + @opaque + @inlineOnce + @pure + @ghost + def lemmaRemoveValidKeyDecreasesNumberOfValidKeysInArray( + a: Array[Long], + i: Int, + k: Long + ): Unit = { + require( + i >= 0 && i < a.length && validKeyInArray(a(i)) && !validKeyInArray( + k + ) && a.length < Integer.MAX_VALUE + ) + + lemmaRemoveValidKeyAndNumKeysFromImpliesFromZero(a, i, k, i) + lemmaRemoveValidKeyAndNumKeysToImpliesToALength(a, i, k, i + 1) + lemmaSumOfNumOfKeysOfSubArraysIsEqualToWhole(a, 0, a.length, i + 1) + lemmaSumOfNumOfKeysOfSubArraysIsEqualToWhole(a.updated(i, k), 0, a.length, i + 1) + + } ensuring (_ => + arrayCountValidKeys(a.updated(i, k), 0, a.length) == arrayCountValidKeys( + a, + 0, + a.length + ) - 1 + ) + + @opaque + @inlineOnce + @pure + @ghost + def lemmaValidKeyIncreasesNumOfKeys(a: Array[Long], from: Int, to: Int): Unit = { + require( + a.length < Integer.MAX_VALUE && from >= 0 && to >= from && to < a.length && validKeyInArray( + a(to) + ) + ) + + lemmaSumOfNumOfKeysOfSubArraysIsEqualToWhole(a, from, to + 1, to) + + } ensuring (_ => arrayCountValidKeys(a, from, to + 1) == arrayCountValidKeys(a, from, to) + 1) + + @opaque + @inlineOnce + @pure + @ghost + def lemmaNotValidKeyDoesNotIncreaseNumOfKeys(a: Array[Long], from: Int, to: Int): Unit = { + require( + a.length < Integer.MAX_VALUE && from >= 0 && to >= from && to < a.length && !validKeyInArray( + a(to) + ) + ) + + lemmaSumOfNumOfKeysOfSubArraysIsEqualToWhole(a, from, to + 1, to) + + } ensuring (_ => arrayCountValidKeys(a, from, to + 1) == arrayCountValidKeys(a, from, to)) + + @opaque + @inlineOnce + @pure + @ghost + def lemmaAddValidKeyAndNumKeysToImpliesToALength( + a: Array[Long], + i: Int, + k: Long, + to: Int + ): Unit = { + require(i >= 0 && i < a.length) + require(!validKeyInArray(a(i)) && validKeyInArray(k)) + require(a.length < Integer.MAX_VALUE) + require(to >= 0 && to <= a.length && to > i) + require( + (arrayCountValidKeys(a.updated(i, k), i + 1, to) == arrayCountValidKeys( + a, + i + 1, + to + )) + ) + decreases(a.length + 1 - to) + + if (to != a.length) { + if (validKeyInArray(a(to))) { + lemmaValidKeyIncreasesNumOfKeys(a, i + 1, to) + lemmaValidKeyIncreasesNumOfKeys(a.updated(i, k), i + 1, to) + } else { + lemmaNotValidKeyDoesNotIncreaseNumOfKeys(a, i + 1, to) + lemmaNotValidKeyDoesNotIncreaseNumOfKeys(a.updated(i, k), i + 1, to) + } + lemmaAddValidKeyAndNumKeysToImpliesToALength(a, i, k, to + 1) + } + } ensuring (_ => + arrayCountValidKeys(a.updated(i, k), i + 1, a.length) == arrayCountValidKeys( + a, + i + 1, + a.length + ) + ) + + @opaque + @inlineOnce + @pure + @ghost + def lemmaAddValidKeyAndNumKeysFromImpliesFromZero( + a: Array[Long], + i: Int, + k: Long, + from: Int + ): Unit = { + require(i >= 0 && i < a.length) + require(!validKeyInArray(a(i))) + require(validKeyInArray(k)) + require(a.length < Integer.MAX_VALUE) + require(from >= 0 && from <= a.length && i >= from) + require( + (arrayCountValidKeys(a.updated(i, k), from, i + 1) == arrayCountValidKeys( + a, + from, + i + 1 + ) + 1) + ) + + decreases(from) + + if (from > 0) { + lemmaSumOfNumOfKeysOfSubArraysIsEqualToWhole(a, from - 1, i + 1, from) + lemmaSumOfNumOfKeysOfSubArraysIsEqualToWhole(a.updated(i, k), from - 1, i + 1, from) + lemmaAddValidKeyAndNumKeysFromImpliesFromZero(a, i, k, from - 1) + } + + } ensuring (_ => { + arrayCountValidKeys(a.updated(i, k), 0, i + 1) == arrayCountValidKeys( + a, + 0, + i + 1 + ) + 1 + }) + + @opaque + @inlineOnce + @pure + @ghost + def lemmaAddValidKeyIncreasesNumberOfValidKeysInArray(a: Array[Long], i: Int, k: Long): Unit = { + require( + i >= 0 && i < a.length && !validKeyInArray(a(i)) && validKeyInArray( + k + ) && a.length < Integer.MAX_VALUE + ) + + lemmaAddValidKeyAndNumKeysFromImpliesFromZero(a, i, k, i) + lemmaAddValidKeyAndNumKeysToImpliesToALength(a, i, k, i + 1) + lemmaSumOfNumOfKeysOfSubArraysIsEqualToWhole(a, 0, a.length, i + 1) + lemmaSumOfNumOfKeysOfSubArraysIsEqualToWhole(a.updated(i, k), 0, a.length, i + 1) + + } ensuring (_ => + arrayCountValidKeys(a.updated(i, k), 0, a.length) == arrayCountValidKeys( + a, + 0, + a.length + ) + 1 + ) + + @opaque + @inlineOnce + @pure + @ghost + def lemmaValidKeyAtIImpliesCountKeysIsOne(a: Array[Long], i: Int): Unit = { + require(i >= 0 && i < a.length && validKeyInArray(a(i)) && a.length < Integer.MAX_VALUE) + + } ensuring (_ => arrayCountValidKeys(a, i, i + 1) == 1) + + @opaque + @inlineOnce + @pure + @ghost + def lemmaArrayEqualsFromToReflexivity(a: Array[Long], from: Int, to: Int): Unit = { + require(from >= 0 && from < to && to <= a.length && a.length < Integer.MAX_VALUE) + decreases(to - from) + if (from + 1 < to) { + lemmaArrayEqualsFromToReflexivity(a, from + 1, to) + } + } ensuring (_ => arraysEqualsFromTo(a, snapshot(a), from, to)) + + @opaque + @inlineOnce + @pure + @ghost + def lemmaValidKeyIndexImpliesArrayContainsKey(a: Array[Long], k: Long, i: Int): Unit = { + require(a.length < Integer.MAX_VALUE) + require(i >= 0 && i < a.length) + require(a(i) == k) + + LongMapFixedSize.lemmaArrayContainsFromImpliesContainsFromZero(a, k, i) + } ensuring (_ => arrayContainsKey(a, k, 0)) + + @opaque + @inlineOnce + @pure + @ghost + def lemmaArrayCountValidKeysOfFilled0ArrayIs0(a: Array[Long], i: Int, size: Int): Unit = { + require(a.length < Integer.MAX_VALUE) + require(i >= 0 && i <= a.length) + require(a.length == size) + require(a == Array.fill(size)(0L)) + decreases(size - i) + if (i != size) { + assert(a(i) == 0) + lemmaArrayCountValidKeysOfFilled0ArrayIs0(a, i + 1, size) + } + + } ensuring (_ => arrayCountValidKeys(a, i, size) == 0) + + @opaque + @inlineOnce + @pure + @ghost + def lemmaArrayNoDuplicatesInAll0Array(a: Array[Long], from: Int, size: Int, acc: List[Long] = Nil[Long]()): Unit = { + require(a.length == size) + require(size < Integer.MAX_VALUE) + require(a == Array.fill(size)(0L)) + require(from >= 0 && from <= a.length) + require(!acc.contains(0) && !acc.contains(Long.MinValue)) + require(ListOps.noDuplicate(acc)) + decreases(size - from) + if (from < size) { + lemmaArrayNoDuplicatesInAll0Array(a, from + 1, size, acc) + } + } ensuring (res => arrayNoDuplicates(a, from, acc)) + + } + +} diff --git a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/ListUtils.scala b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/ListUtils.scala index fc7a6fc3..ab2ae285 100644 --- a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/ListUtils.scala +++ b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/ListUtils.scala @@ -1,5 +1,6 @@ /** Author: Samuel Chassot */ +package ch.epfl.lexer import stainless.annotation._ import stainless.collection._ diff --git a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedLexer.scala b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedLexer.scala index 728b1788..d1e4f2c2 100644 --- a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedLexer.scala +++ b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedLexer.scala @@ -1,6 +1,8 @@ /** Author: Samuel Chassot */ +package ch.epfl.lexer + import stainless.annotation._ import stainless.collection._ import stainless.equations._ diff --git a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedRegex.scala b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedRegex.scala index 89fa613e..955d935c 100644 --- a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedRegex.scala +++ b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedRegex.scala @@ -1,24 +1,63 @@ /** Author: Samuel Chassot */ +package ch.epfl.lexer + import stainless.equations._ import stainless.lang._ import stainless.collection._ import stainless.annotation._ import stainless.proof._ -import scala.runtime.Statics +import ch.epfl.chassot.MutableLongMap._ +import stainless.lang.StaticChecks._ trait IDGiver[C] { def id(c: C): Long val MAX_ID = Int.MaxValue @law def smallEnough(c: C): Boolean = id(c) >= 0 && id(c) <= MAX_ID - @law def uniqueness(c1: C, c2: C): Boolean = if(id(c1) == id(c2)) then c1 == c2 else true + @law def uniqueness(c1: C, c2: C): Boolean = if (id(c1) == id(c2)) then c1 == c2 else true } +object Memoisation { + import VerifiedRegex._ + import VerifiedRegexMatcher._ + final case class Cache[C](@ghost ids: List[Long], idToRegexes: LongMap[List[Regex[C]]], idToDerivatives: LongMap[List[(C, Regex[C])]]) { + @ghost def validContains: Boolean = ids.forall(idToRegexes.contains) && ids.forall(idToDerivatives.contains) + @ghost def validContent: Boolean = ids.forall(id => { + idToRegexes.apply(id) match { + case Nil() => false + case Cons(hd, Nil()) => idToDerivatives.apply(id).forall((c, reg) => derivativeStep(hd, c) == reg) + case Cons(hd, tl) => true + } + }) + @ghost def valid: Boolean = validContains && validContent + + private def getDerivativeFromList(l: List[(C, Regex[C])], a: C) : Option[Regex[C]] = l match + case Cons(h, t) if h._1 == a => Some(h._2) + case Cons(_, t) => getDerivativeFromList(t, a) + case Nil() => None() + + + def getDerivativeAndUpdate(r: Regex[C], a: C)(implicit idGiver: IDGiver[C]): Option[Regex[C]] = { + require(validRegex(r)) + require(valid) + val id = hashId(r) + idToRegexes.apply(id) match { + case Cons(h, Nil()) => + // Good case: the regex is in the ids list, and no collision happened + getDerivativeFromList(idToDerivatives.apply(id), a) + case Cons(hd, tl) => + // Collision happened, i.e., 2 different regexes got the same id, abort + None() + case Nil() => + // The regex was never cached + None() + } + } ensuring (res => valid && (res.isEmpty || res.get == derivativeStep(r, a))) + } +} object VerifiedRegex { abstract sealed class Regex[C] {} - val INT_MAX_VALUE: BigInt = 2147483647 - val INT_MAX_VALUE_L: Long = 2147483647L def validRegex[C](r: Regex[C]): Boolean = r match { case ElementMatch(c) => true @@ -48,40 +87,24 @@ object VerifiedRegex { }) ) - def regexDepthLong[C](r: Regex[C]): Long = { - require(regexDepth(r) < INT_MAX_VALUE) - decreases(r) - r match { - case ElementMatch(c) => 1L - case Star(r) => 1L + regexDepthLong(r) - case Union(rOne, rTwo) => 1L + Utils.maxLong(regexDepthLong(rOne), regexDepthLong(rTwo)) - case Concat(rOne, rTwo) => 1L + Utils.maxLong(regexDepthLong(rOne), regexDepthLong(rTwo)) - case EmptyExpr() => 1L - case EmptyLang() => 1L - } - } ensuring (res => - res > 0 && (r match { - case Union(rOne, rTwo) => res > regexDepthLong(rOne) && res > regexDepthLong(rTwo) - case Concat(rOne, rTwo) => res > regexDepthLong(rOne) && res > regexDepthLong(rTwo) - case Star(r) => res > regexDepthLong(r) - case _ => res == 1L - }) - ) - - def getUniqueId[C](r: Regex[C])(implicit idC: IDGiver[C]): Long = { + def hashId[C](r: Regex[C])(implicit idC: IDGiver[C]): Long = { require(regexDepth(r) <= 30) decreases(r) r match { case ElementMatch(c) => - assert(idC.smallEnough(c)) 2L * idC.id(c) - case Star(r) => 3L * getUniqueId(r) - case Union(rOne, rTwo) => 5L * (getUniqueId(rOne) + getUniqueId(rTwo)) - case Concat(rOne, rTwo) => 7L * (getUniqueId(rOne) + getUniqueId(rTwo)) - case EmptyExpr() => 11L - case EmptyLang() => 13L + case Star(r) => + 3L * hashId(r) + case Union(rOne, rTwo) => + 5L * hashId(rOne) + 5L * hashId(rTwo) + case Concat(rOne, rTwo) => + 7L * hashId(rOne) + 7L * hashId(rTwo) + case EmptyExpr() => + 11L + case EmptyLang() => + 13L } - } ensuring (res => res >= 0 && res < 12L) + } case class ElementMatch[C](c: C) extends Regex[C] case class Star[C](reg: Regex[C]) extends Regex[C] @@ -192,6 +215,7 @@ object VerifiedRegex { object VerifiedRegexMatcher { import VerifiedRegex._ import ListUtils._ + import Memoisation._ def derivativeStep[C](r: Regex[C], a: C): Regex[C] = { require(validRegex(r)) @@ -210,6 +234,15 @@ object VerifiedRegexMatcher { res } ensuring (res => validRegex(res)) + // def derivativeStepMem[C](r: Regex[C], a: C)(cache: Cache[C]): Regex[C] = { + // require(validRegex(r)) + // require(cache.valid) + // decreases(r) + // val id = hashId(r) + + // res + // } ensuring (res => res == derivativeStep(r, a)) + def derivative[C](r: Regex[C], input: List[C]): Regex[C] = { require(validRegex(r)) input match { @@ -306,7 +339,7 @@ object VerifiedRegexMatcher { } } - } ensuring (matchR(r, s) == matchRSpec(r, s)) + } ensuring (_ => matchR(r, s) == matchRSpec(r, s)) /** Enumerate all cuts in s and returns one that works, i.e., r1 matches s1 and r2 matches s2 Specifically, it is the right most one, i.e., s2 is the largest, if multiple exists Returns None is no valid cut * exists @@ -381,7 +414,7 @@ object VerifiedRegexMatcher { require(validRegex(r)) longestMatchIsAcceptedByMatchOrIsEmptyRec(r, r, Nil(), input) - } ensuring (findLongestMatchInner(r, Nil(), input)._1.isEmpty || matchR(r, findLongestMatchInner(r, Nil(), input)._1)) + } ensuring (_ => findLongestMatchInner(r, Nil(), input)._1.isEmpty || matchR(r, findLongestMatchInner(r, Nil(), input)._1)) def longestMatchNoBiggerStringMatch[C](baseR: Regex[C], input: List[C], returnP: List[C], bigger: List[C]): Unit = { require(validRegex(baseR)) @@ -399,7 +432,7 @@ object VerifiedRegexMatcher { } } - } ensuring (bigger == returnP || !matchR(baseR, bigger)) + } ensuring (_ => bigger == returnP || !matchR(baseR, bigger)) def lemmaIfMatchRThenLongestMatchFromThereReturnsAtLeastThis[C](baseR: Regex[C], r: Regex[C], input: List[C], testedP: List[C]): Unit = { require(validRegex(baseR)) @@ -412,7 +445,7 @@ object VerifiedRegexMatcher { assert(matchR(r, Nil())) assert(nullable(r)) - } ensuring (findLongestMatchInner(r, testedP, input)._1.size >= testedP.size) + } ensuring (_ => findLongestMatchInner(r, testedP, input)._1.size >= testedP.size) def lemmaKnownAcceptedStringThenFromSmallPAtLeastThat[C](baseR: Regex[C], r: Regex[C], input: List[C], testedP: List[C], knownP: List[C]): Unit = { require(validRegex(baseR)) @@ -440,7 +473,7 @@ object VerifiedRegexMatcher { check(findLongestMatchInner(r, testedP, input)._1.size >= knownP.size) } - } ensuring (findLongestMatchInner(r, testedP, input)._1.size >= knownP.size) + } ensuring (_ => findLongestMatchInner(r, testedP, input)._1.size >= knownP.size) def longestMatchIsAcceptedByMatchOrIsEmptyRec[C](baseR: Regex[C], r: Regex[C], testedP: List[C], input: List[C]): Unit = { require(validRegex(baseR)) @@ -483,7 +516,7 @@ object VerifiedRegexMatcher { } } - } ensuring (findLongestMatchInner(r, testedP, input)._1.isEmpty || matchR(baseR, findLongestMatchInner(r, testedP, input)._1)) + } ensuring (_ => findLongestMatchInner(r, testedP, input)._1.isEmpty || matchR(baseR, findLongestMatchInner(r, testedP, input)._1)) def lemmaMatchRIsSameAsWholeDerivativeAndNil[C](r: Regex[C], input: List[C]): Unit = { require(validRegex(r)) @@ -491,7 +524,7 @@ object VerifiedRegexMatcher { case Cons(hd, tl) => lemmaMatchRIsSameAsWholeDerivativeAndNil(derivativeStep(r, hd), tl) case Nil() => () } - } ensuring (matchR(r, input) == matchR(derivative(r, input), Nil())) + } ensuring (_ => matchR(r, input) == matchR(derivative(r, input), Nil())) def lemmaDerivativeOnLWithANewCharIsANewDerivativeStep[C](baseR: Regex[C], r: Regex[C], input: List[C], c: C): Unit = { require(validRegex(baseR)) @@ -502,25 +535,25 @@ object VerifiedRegexMatcher { case Nil() => () } - } ensuring (derivative(baseR, input ++ List(c)) == derivativeStep(r, c)) + } ensuring (_ => derivative(baseR, input ++ List(c)) == derivativeStep(r, c)) // Basic lemmas def lemmaIfAcceptEmptyStringThenNullable[C](r: Regex[C], s: List[C]): Unit = { require(validRegex(r)) require(s.isEmpty) require(matchR(r, s)) - } ensuring (nullable(r)) + } ensuring (_ => nullable(r)) def lemmaRegexAcceptsStringThenDerivativeAcceptsTail[C](r: Regex[C], s: List[C]): Unit = { require(validRegex(r)) require(matchR(r, s)) - } ensuring (if (s.isEmpty) nullable(r) else matchR(derivativeStep(r, s.head), s.tail)) + } ensuring (_ => if (s.isEmpty) nullable(r) else matchR(derivativeStep(r, s.head), s.tail)) // EmptyString Lemma def lemmaRegexEmptyStringAcceptsTheEmptyString[C](r: EmptyExpr[C]): Unit = { require(validRegex(r)) - } ensuring (matchR(r, List())) + } ensuring (_ => matchR(r, List())) // Single Character Lemma def lemmaElementRegexAcceptsItsCharacterAndOnlyIt[C]( @@ -530,7 +563,7 @@ object VerifiedRegexMatcher { ): Unit = { require(validRegex(r) && r == ElementMatch(c)) require(c != d) - } ensuring (matchR(r, List(c)) && !matchR(r, List(d))) + } ensuring (_ => matchR(r, List(c)) && !matchR(r, List(d))) def lemmaElementRegexDoesNotAcceptMultipleCharactersString[C]( r: ElementMatch[C], @@ -539,7 +572,7 @@ object VerifiedRegexMatcher { ): Unit = { require(validRegex(r) && r == ElementMatch(c)) require(!s.isEmpty) - } ensuring (!matchR(r, Cons(c, s))) + } ensuring (_ => !matchR(r, Cons(c, s))) // Union lemmas def lemmaRegexAcceptsStringThenUnionWithAnotherAcceptsToo[C]( @@ -557,7 +590,7 @@ object VerifiedRegexMatcher { } case Nil() => assert(matchR(Union(r1, r2), s)) } - } ensuring (matchR(Union(r1, r2), s)) + } ensuring (_ => matchR(Union(r1, r2), s)) def lemmaRegexUnionAcceptsThenOneOfTheTwoAccepts[C](r1: Regex[C], r2: Regex[C], s: List[C]): Unit = { require(validRegex(r1) && validRegex(r2)) @@ -569,7 +602,7 @@ object VerifiedRegexMatcher { } case Nil() => } - } ensuring (matchR(r1, s) || matchR(r2, s)) + } ensuring (_ => matchR(r1, s) || matchR(r2, s)) def lemmaReversedUnionAcceptsSameString[C]( r1: Regex[C], @@ -586,7 +619,7 @@ object VerifiedRegexMatcher { } case Nil() => assert(matchR(Union(r1, r2), s)) } - } ensuring (matchR(Union(r2, r1), s)) + } ensuring (_ => matchR(Union(r2, r1), s)) // Concat lemmas @@ -618,7 +651,7 @@ object VerifiedRegexMatcher { } case Nil() => () } - } ensuring (matchR(Concat(r2, r1), s)) + } ensuring (_ => matchR(Concat(r2, r1), s)) def lemmaTwoRegexMatchThenConcatMatchesConcatString[C]( r1: Regex[C], @@ -658,7 +691,7 @@ object VerifiedRegexMatcher { lemmaRegexConcatWithNullableAcceptsSameStr(r2, r1, s2) } - } ensuring (matchR(Concat(r1, r2), s1 ++ s2)) + } ensuring (_ => matchR(Concat(r1, r2), s1 ++ s2)) def lemmaFindSeparationIsDefinedThenConcatMatches[C](r1: Regex[C], r2: Regex[C], s1: List[C], s2: List[C], s: List[C]): Unit = { require(validRegex(r1)) @@ -669,7 +702,7 @@ object VerifiedRegexMatcher { lemmaTwoRegexMatchThenConcatMatchesConcatString(r1, r2, s1, s2) - } ensuring (matchR(Concat(r1, r2), s1 ++ s2)) + } ensuring (_ => matchR(Concat(r1, r2), s1 ++ s2)) def lemmaR1MatchesS1AndR2MatchesS2ThenFindSeparationFindsAtLeastThem[C]( r1: Regex[C], @@ -718,7 +751,7 @@ object VerifiedRegexMatcher { } } - } ensuring (findConcatSeparation(r1, r2, s1Rec, s2Rec, s).isDefined) + } ensuring (_ => findConcatSeparation(r1, r2, s1Rec, s2Rec, s).isDefined) def lemmaConcatAcceptsStringThenFindSeparationIsDefined[C](r1: Regex[C], r2: Regex[C], s: List[C]): Unit = { require(validRegex(r1)) @@ -759,12 +792,12 @@ object VerifiedRegexMatcher { } } - } ensuring (findConcatSeparation(r1, r2, Nil(), s, s).isDefined) + } ensuring (_ => findConcatSeparation(r1, r2, Nil(), s, s).isDefined) // Star lemmas def lemmaStarAcceptsEmptyString[C](r: Star[C]): Unit = { require(validRegex(r)) - } ensuring (matchR(r, List())) + } ensuring (_ => matchR(r, List())) def lemmaStarApp[C](r: Regex[C], s1: List[C], s2: List[C]): Unit = { require(validRegex(Star(r))) @@ -778,7 +811,7 @@ object VerifiedRegexMatcher { } case Nil() => () } - } ensuring (matchR(Star(r), s1 ++ s2)) + } ensuring (_ => matchR(Star(r), s1 ++ s2)) def lemmaStarAppConcat[C](r: Regex[C], s: List[C]): Unit = { require(validRegex(Star(r))) @@ -795,7 +828,7 @@ object VerifiedRegexMatcher { } case Nil() => () } - } ensuring (s.isEmpty || matchR(Concat(r, Star(r)), s)) + } ensuring (_ => s.isEmpty || matchR(Concat(r, Star(r)), s)) // usedCharacters lemmas --------------------------------------------------------------------------------------------------- @@ -814,7 +847,7 @@ object VerifiedRegexMatcher { case Nil() => check(false) } - } ensuring (!matchR(r, s)) + } ensuring (_ => !matchR(r, s)) def lemmaRegexCannotMatchAStringStartingWithACharItDoesNotContain[C](r: Regex[C], s: List[C], c: C): Unit = { require(validRegex(r)) @@ -828,7 +861,7 @@ object VerifiedRegexMatcher { check(false) } - } ensuring (!matchR(r, s)) + } ensuring (_ => !matchR(r, s)) def lemmaRegexCannotMatchAStringStartingWithACharWhichIsNotInFirstChars[C](r: Regex[C], s: List[C], c: C): Unit = { require(validRegex(r)) @@ -842,7 +875,7 @@ object VerifiedRegexMatcher { check(false) } - } ensuring (!matchR(r, s)) + } ensuring (_ => !matchR(r, s)) // not used def lemmaRIsNotNullableDerivativeStepIsThenUsedCharContainsC[C](r: Regex[C], c: C): Unit = { @@ -885,7 +918,7 @@ object VerifiedRegexMatcher { } } - } ensuring (usedCharacters(r).contains(c)) + } ensuring (_ => usedCharacters(r).contains(c)) // DONE def lemmaDerivativeAfterDerivativeStepIsNullableThenUsedCharsContainsHead[C](r: Regex[C], c: C, tl: List[C]): Unit = { @@ -990,7 +1023,7 @@ object VerifiedRegexMatcher { } } - } ensuring (usedCharacters(r).contains(c)) + } ensuring (_ => usedCharacters(r).contains(c)) def lemmaDerivativeStepDoesNotAddCharToUsedCharacters[C](r: Regex[C], c: C, cNot: C): Unit = { decreases(r) @@ -1020,7 +1053,7 @@ object VerifiedRegexMatcher { } } - } ensuring (!usedCharacters(derivativeStep(r, c)).contains(cNot)) + } ensuring (_ => !usedCharacters(derivativeStep(r, c)).contains(cNot)) def lemmaEmptyLangDerivativeIsAFixPoint[C](r: Regex[C], s: List[C]): Unit = { require(r == EmptyLang[C]()) @@ -1029,7 +1062,7 @@ object VerifiedRegexMatcher { case Nil() => () } - } ensuring (derivative(r, s) == r) + } ensuring (_ => derivative(r, s) == r) def lemmaUsedCharsContainsAllFirstChars[C](r: Regex[C], c: C): Unit = { require(validRegex(r)) @@ -1057,7 +1090,7 @@ object VerifiedRegexMatcher { case Concat(rOne, rTwo) if !nullable(rOne) => lemmaUsedCharsContainsAllFirstChars(rOne, c) } - } ensuring (usedCharacters(r).contains(c)) + } ensuring (_ => usedCharacters(r).contains(c)) def lemmaDerivAfterDerivStepIsNullableThenFirstCharsContainsHead[C](r: Regex[C], c: C, tl: List[C]): Unit = { require(validRegex(r)) @@ -1157,7 +1190,7 @@ object VerifiedRegexMatcher { } } - } ensuring (firstChars(r).contains(c)) + } ensuring (_ => firstChars(r).contains(c)) } object Utils { diff --git a/lexers/regex/verifiedlexer/src/main/scala/test.worksheet.sc b/lexers/regex/verifiedlexer/src/main/scala/test.worksheet.sc new file mode 100644 index 00000000..b028c0a9 --- /dev/null +++ b/lexers/regex/verifiedlexer/src/main/scala/test.worksheet.sc @@ -0,0 +1,178 @@ +import scala.collection.immutable.LazyList.cons +object Utils { + def maxBigInt(a: BigInt, b: BigInt): BigInt = if (a >= b) a else b + def maxLong(a: Long, b: Long): Long = if (a >= b) a else b +} + +trait IDGiver[C] { + def id(c: C): Long + val MAX_ID = Int.MaxValue + // @law def smallEnough(c: C): Boolean = id(c) >= 0 && id(c) <= MAX_ID + // @law def uniqueness(c1: C, c2: C): Boolean = if (id(c1) == id(c2)) then c1 == c2 else true +} + +abstract sealed class Regex[C] {} +case class ElementMatch[C](c: C) extends Regex[C] +case class Star[C](reg: Regex[C]) extends Regex[C] +case class Union[C](regOne: Regex[C], regTwo: Regex[C]) extends Regex[C] +case class Concat[C](regOne: Regex[C], regTwo: Regex[C]) extends Regex[C] + +/** Regex that accepts only the empty string: represents the language {""} + */ +case class EmptyExpr[C]() extends Regex[C] + +/** Regex that accepts nothing: represents the empty language + */ +case class EmptyLang[C]() extends Regex[C] + +val INT_MAX_VALUE: BigInt = 2147483647 +val INT_MAX_VALUE_L: Long = 2147483647L + +def nullable[C](r: Regex[C]): Boolean = { + r match { + case EmptyExpr() => true + case EmptyLang() => false + case ElementMatch(c) => false + case Star(r) => true + case Union(rOne, rTwo) => nullable(rOne) || nullable(rTwo) + case Concat(rOne, rTwo) => nullable(rOne) && nullable(rTwo) + } + } + + def isEmptyExpr[C](r: Regex[C]): Boolean = { + r match { + case EmptyExpr() => true + case _ => false + } + } + def isEmptyLang[C](r: Regex[C]): Boolean = { + r match { + case EmptyLang() => true + case _ => false + } + } + def isElementMatch[C](r: Regex[C]): Boolean = { + r match { + case ElementMatch(_) => true + case _ => false + } + } + def elementMatchIsChar[C](r: Regex[C], c: C): Boolean = { + require(isElementMatch(r)) + r match { + case ElementMatch(cc) => c == cc + } + } + def isStar[C](r: Regex[C]): Boolean = { + r match { + case Star(_) => true + case _ => false + } + } + def isUnion[C](r: Regex[C]): Boolean = { + r match { + case Union(_, _) => true + case _ => false + } + } + def unionInnersEquals[C](r: Regex[C], r1: Regex[C], r2: Regex[C]): Boolean = { + require(isUnion(r)) + r match { + case Union(rOne, rTwo) => r1 == rOne && r2 == rTwo + } + } + + def isConcat[C](r: Regex[C]): Boolean = { + r match { + case Concat(_, _) => true + case _ => false + } + } + +def validRegex[C](r: Regex[C]): Boolean = r match { + case ElementMatch(c) => true + case Star(r) => !nullable(r) && !isEmptyLang(r) && validRegex(r) + case Union(rOne, rTwo) => validRegex(rOne) && validRegex(rTwo) + case Concat(rOne, rTwo) => validRegex(rOne) && validRegex(rTwo) + case EmptyExpr() => true + case EmptyLang() => true +} + +def regexDepth[C](r: Regex[C]): BigInt = { + // decreases(r) + r match { + case ElementMatch(c) => BigInt(1) + case Star(r) => BigInt(1) + regexDepth(r) + case Union(rOne, rTwo) => BigInt(1) + Utils.maxBigInt(regexDepth(rOne), regexDepth(rTwo)) + case Concat(rOne, rTwo) => BigInt(1) + Utils.maxBigInt(regexDepth(rOne), regexDepth(rTwo)) + case EmptyExpr() => BigInt(1) + case EmptyLang() => BigInt(1) + } +} ensuring (res => + res > 0 && (r match { + case Union(rOne, rTwo) => res > regexDepth(rOne) && res > regexDepth(rTwo) + case Concat(rOne, rTwo) => res > regexDepth(rOne) && res > regexDepth(rTwo) + case Star(r) => res > regexDepth(r) + case _ => res == BigInt(1) + }) +) + +def regexDepthLong[C](r: Regex[C]): Long = { + require(regexDepth(r) < INT_MAX_VALUE) + // decreases(r) + r match { + case ElementMatch(c) => 1L + case Star(r) => 1L + regexDepthLong(r) + case Union(rOne, rTwo) => 1L + Utils.maxLong(regexDepthLong(rOne), regexDepthLong(rTwo)) + case Concat(rOne, rTwo) => 1L + Utils.maxLong(regexDepthLong(rOne), regexDepthLong(rTwo)) + case EmptyExpr() => 1L + case EmptyLang() => 1L + } +} ensuring (res => + res > 0 && (r match { + case Union(rOne, rTwo) => res > regexDepthLong(rOne) && res > regexDepthLong(rTwo) + case Concat(rOne, rTwo) => res > regexDepthLong(rOne) && res > regexDepthLong(rTwo) + case Star(r) => res > regexDepthLong(r) + case _ => res == 1L + }) +) + +def getUniqueId[C](r: Regex[C])(implicit idC: IDGiver[C]): Long = { + require(regexDepth(r) <= 30) + // decreases(r) + r match { + case ElementMatch(c) => + // assert(idC.smallEnough(c)) + 2L * idC.id(c) + case Star(r) => 3L + getUniqueId(r) + case Union(rOne, rTwo) => 5L + (getUniqueId(rOne) + getUniqueId(rTwo)) + case Concat(rOne, rTwo) => 7L + (getUniqueId(rOne) + getUniqueId(rTwo)) + case EmptyExpr() => 11L + case EmptyLang() => 13L + } +} ensuring (res => res >= 0) + +object CharIDGiver extends IDGiver[Char] { + def id(c: Char): Long = c.toLong +} + +def constructRegex(l: List[Char]): Regex[Char] = { + l match { + case Nil => EmptyExpr() + case head :: Nil => ElementMatch(head) + case head :: tail => Concat(ElementMatch(head), constructRegex(tail)) + } +} + +getUniqueId(Star(ElementMatch('a')))(CharIDGiver) +// Regexc representing the regex (abcd + gejho)* +val r: Regex[Char] = Concat(Star(Union(Concat(Concat(Concat(ElementMatch('a'), ElementMatch('b')), ElementMatch('c')), ElementMatch('d')), Concat(Concat(ElementMatch('g'), ElementMatch('e')), Concat(ElementMatch('j'), ElementMatch('h'))))), ElementMatch('o')) +getUniqueId(r)(CharIDGiver) +regexDepth(r) + +val r21 = constructRegex(List('a', 'b', 'c', 'd', 'g', 'e', 'j', 'h', 'o')) +val r22 = constructRegex(List('y', 'v', 'n', 'b', 's', 'l', 'u', 't', 'i', 'o', 'n')) +val r23 = constructRegex(List('a', 'b', 'c', 'd', 'g', 'e', 'j', 'h', 'o', 'y', 'v', 'n', 'b', 's', 'l', 'u', 't', 'i', 'o', 'n')) +val r2 = Star(Concat(r23, Star(Union(Union(r21, r22), r23)))) +getUniqueId(r2)(CharIDGiver) +regexDepth(r2) \ No newline at end of file diff --git a/lexers/regex/verifiedlexer/verify.sh b/lexers/regex/verifiedlexer/verify.sh index 511c4a3e..9bfe91bd 100755 --- a/lexers/regex/verifiedlexer/verify.sh +++ b/lexers/regex/verifiedlexer/verify.sh @@ -1 +1 @@ -stainless-dotty --config-file=stainless.conf --watch src/main/scala/ch/epfl/lexer/VerifiedRegex.scala src/main/scala/ch/epfl/lexer/ListUtils.scala $1 \ No newline at end of file +stainless-dotty --config-file=stainless.conf --watch src/main/scala/ch/epfl/lexer/VerifiedRegex.scala src/main/scala/ch/epfl/lexer/ListUtils.scala src/main/scala/ch/epfl/chassot/* $1 \ No newline at end of file From 0a56ac6d92378cf56147bc804a7b3f600c1cf70c Mon Sep 17 00:00:00 2001 From: Samuel Chassot Date: Mon, 5 Feb 2024 18:08:37 +0100 Subject: [PATCH 07/78] for now issue with illegal capturing --- .../scala/ch/epfl/lexer/VerifiedRegex.scala | 24 ++++++++++++------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedRegex.scala b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedRegex.scala index 955d935c..585ca17f 100644 --- a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedRegex.scala +++ b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedRegex.scala @@ -21,16 +21,24 @@ trait IDGiver[C] { object Memoisation { import VerifiedRegex._ import VerifiedRegexMatcher._ - final case class Cache[C](@ghost ids: List[Long], idToRegexes: LongMap[List[Regex[C]]], idToDerivatives: LongMap[List[(C, Regex[C])]]) { - @ghost def validContains: Boolean = ids.forall(idToRegexes.contains) && ids.forall(idToDerivatives.contains) - @ghost def validContent: Boolean = ids.forall(id => { - idToRegexes.apply(id) match { + @ghost + @pure + def validIdToDerivatives[C](_idToRegexes: LongMap[List[Regex[C]]], _idToDerivatives: LongMap[List[(C, Regex[C])]], id: Long): Boolean = _idToRegexes.apply(id) match { case Nil() => false - case Cons(hd, Nil()) => idToDerivatives.apply(id).forall((c, reg) => derivativeStep(hd, c) == reg) + case Cons(hd, Nil()) => _idToDerivatives.apply(id).forall((c, reg) => derivativeStep(hd, c) == reg) case Cons(hd, tl) => true } - }) - @ghost def valid: Boolean = validContains && validContent + final case class Cache[C](@ghost var ids: List[Long], idToRegexes: LongMap[List[Regex[C]]], idToDerivatives: LongMap[List[(C, Regex[C])]]) { + @ghost + @pure + def validContains: Boolean = ids.forall(idToRegexes.contains) && ids.forall(idToDerivatives.contains) + @ghost + @pure + def validContent: Boolean = ids.forall(id => validIdToDerivatives(idToRegexes, idToDerivatives, id)) + + @ghost + @pure + def valid: Boolean = validContains && validContent private def getDerivativeFromList(l: List[(C, Regex[C])], a: C) : Option[Regex[C]] = l match case Cons(h, t) if h._1 == a => Some(h._2) @@ -38,7 +46,7 @@ object Memoisation { case Nil() => None() - def getDerivativeAndUpdate(r: Regex[C], a: C)(implicit idGiver: IDGiver[C]): Option[Regex[C]] = { + def getDerivative(r: Regex[C], a: C)(implicit idGiver: IDGiver[C]): Option[Regex[C]] = { require(validRegex(r)) require(valid) val id = hashId(r) From 10ccdeef419dcd40e27677e4591871a5e7512075 Mon Sep 17 00:00:00 2001 From: Samuel Chassot Date: Wed, 7 Feb 2024 13:25:36 +0100 Subject: [PATCH 08/78] to specific, not working but almost --- .../ch/epfl/chassot/MutableLongMap.scala | 2 +- .../scala/ch/epfl/lexer/VerifiedRegex.scala | 134 ++++++++++++++---- 2 files changed, 110 insertions(+), 26 deletions(-) diff --git a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/chassot/MutableLongMap.scala b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/chassot/MutableLongMap.scala index ba51bf7b..3b0d9c61 100644 --- a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/chassot/MutableLongMap.scala +++ b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/chassot/MutableLongMap.scala @@ -305,7 +305,7 @@ object MutableLongMap { @pure @ghost - private def map: ListLongMap[V] = { + def map: ListLongMap[V] = { require(valid) underlying.v.map } diff --git a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedRegex.scala b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedRegex.scala index 585ca17f..611d13ab 100644 --- a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedRegex.scala +++ b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedRegex.scala @@ -4,11 +4,12 @@ package ch.epfl.lexer import stainless.equations._ -import stainless.lang._ +import stainless.lang.{ghost => ghostExpr, *} import stainless.collection._ import stainless.annotation._ import stainless.proof._ import ch.epfl.chassot.MutableLongMap._ +import ch.epfl.chassot.ListLongMap import stainless.lang.StaticChecks._ trait IDGiver[C] { @@ -21,46 +22,130 @@ trait IDGiver[C] { object Memoisation { import VerifiedRegex._ import VerifiedRegexMatcher._ - @ghost + + @ghost @pure - def validIdToDerivatives[C](_idToRegexes: LongMap[List[Regex[C]]], _idToDerivatives: LongMap[List[(C, Regex[C])]], id: Long): Boolean = _idToRegexes.apply(id) match { + def validIdToDerivatives[C](_idToRegexes: ListLongMap[List[Regex[C]]], _idToDerivatives: ListLongMap[List[(C, Regex[C])]], id: Long): Boolean = { + // require(_idToRegexes.contains(id)) + // require(_idToDerivatives.contains(id)) + if(!_idToRegexes.contains(id) || !_idToDerivatives.contains(id)){ + false + } else { + _idToRegexes.apply(id) match { case Nil() => false - case Cons(hd, Nil()) => _idToDerivatives.apply(id).forall((c, reg) => derivativeStep(hd, c) == reg) + case Cons(hd, Nil()) => _idToDerivatives.apply(id).forall((c, reg) => validRegex(hd) && derivativeStep(hd, c) == reg) case Cons(hd, tl) => true } - final case class Cache[C](@ghost var ids: List[Long], idToRegexes: LongMap[List[Regex[C]]], idToDerivatives: LongMap[List[(C, Regex[C])]]) { + } + } + + final case class Cache[C](@ghost val ids: List[Long], idToRegexes: LongMap[List[Regex[C]]], idToDerivatives: LongMap[List[(C, Regex[C])]]) { + require(idToRegexes.valid) + require(idToDerivatives.valid) + @ghost @pure - def validContains: Boolean = ids.forall(idToRegexes.contains) && ids.forall(idToDerivatives.contains) - @ghost + def validContains: Boolean = + val idToRegMap = idToRegexes.map + ids.forall(i => idToRegMap.contains(i) && !idToRegMap.apply(i).isEmpty) && ids.forall(idToDerivatives.map.contains) + @ghost @pure - def validContent: Boolean = ids.forall(id => validIdToDerivatives(idToRegexes, idToDerivatives, id)) - - @ghost + def validContent: Boolean = + val idToRegMap = idToRegexes.map + val idToDerivMap = idToDerivatives.map + ids.forall(id => validIdToDerivatives(idToRegMap, idToDerivMap, id)) + + @ghost @pure def valid: Boolean = validContains && validContent - private def getDerivativeFromList(l: List[(C, Regex[C])], a: C) : Option[Regex[C]] = l match + private def getDerivativeFromList(l: List[(C, Regex[C])], a: C): Option[Regex[C]] = l match case Cons(h, t) if h._1 == a => Some(h._2) - case Cons(_, t) => getDerivativeFromList(t, a) - case Nil() => None() - + case Cons(_, t) => getDerivativeFromList(t, a) + case Nil() => None() + /** Checks whether the regex is in the cache Meaning that it has been cached and no collision happened + * + * @param r + */ + @pure + def cacheContains(r: Regex[C])(implicit idGiver: IDGiver[C]): Boolean = { + require(validRegex(r)) + require(valid) + val id = hashId(r) + if (idToRegexes.contains(id)) { + idToRegexes.apply(id) match { + case Cons(hd, Nil()) => + // Good case: the regex is in the ids list, and no collision happened + hd == r + case Cons(hd, tl) => + // Collision happened, i.e., 2 different regexes got the same id, abort + false + case Nil() => + // The regex was never cached + false + } + } else { + // The regex was never cached + ghostExpr(if (ids.contains(id)) { + assert(validContent) + val idToRegMap = idToRegexes.map + ListSpecs.forallContained(ids, i => idToRegMap.contains(i) && !idToRegMap.apply(i).isEmpty, id) + assert(idToRegexes.map.contains(id)) + + }) + assert(!ids.contains(id)) + false + } + } ensuring(res => !res || ids.contains(hashId(r))) + + @pure def getDerivative(r: Regex[C], a: C)(implicit idGiver: IDGiver[C]): Option[Regex[C]] = { require(validRegex(r)) require(valid) + require(cacheContains(r)) val id = hashId(r) - idToRegexes.apply(id) match { - case Cons(h, Nil()) => - // Good case: the regex is in the ids list, and no collision happened - getDerivativeFromList(idToDerivatives.apply(id), a) - case Cons(hd, tl) => - // Collision happened, i.e., 2 different regexes got the same id, abort - None() - case Nil() => - // The regex was never cached - None() + val res = getDerivativeFromList(idToDerivatives.apply(id), a) + assert(valid) + if(res.isEmpty){ + ghostExpr({check(valid && (res.isEmpty || res.get == derivativeStep(r, a)))}) + } else{ + assert(valid) + ghostExpr({ + assert(cacheContains(r)) + assert(validContent) + val idToRegMap = idToRegexes.map + val idToDerivMap = idToDerivatives.map + ListSpecs.forallContained(ids, id => idToDerivMap.contains(id), id) + ListSpecs.forallContained(ids, i => idToRegMap.contains(i) && !idToRegMap.apply(i).isEmpty, id) + + assert(idToRegMap.contains(id)) + assert(idToDerivMap.contains(id)) + assert(idToRegexes.contains(id)) + assert(ids.forall(id => validIdToDerivatives(idToRegMap, idToDerivMap, id))) + ListSpecs.forallContained(ids, id => validIdToDerivatives(idToRegMap, idToDerivMap, id), id) + assert(ids.contains(id)) + assert(valid) + check(validIdToDerivatives(idToRegexes.map, idToDerivatives.map, id)) + idToRegexes.apply(id) match { + case Nil() => check(false) + case Cons(hd, Nil()) => + check(idToDerivMap.apply(id).forall((c, reg) => validRegex(hd) && derivativeStep(hd, c) == reg)) + check(r == hd) + assert(r == hd) + check(idToDerivMap.apply(id).forall((c, reg) => validRegex(r) && derivativeStep(r, c) == reg)) + case Cons(hd, tl) => + // cacheContains(r) is false in this case + check(false) + } + check(idToDerivMap.apply(id).forall((c, reg) => validRegex(r) && derivativeStep(r, c) == reg)) + ListSpecs.forallContained(idToDerivMap.apply(id), (c, reg) => validRegex(r) && derivativeStep(r, c) == reg, (a, res.get)) + }) + assert(res.get == derivativeStep(r, a)) + ghostExpr({check(valid && (res.isEmpty || res.get == derivativeStep(r, a)))}) } + res + } ensuring (res => valid && (res.isEmpty || res.get == derivativeStep(r, a))) } } @@ -96,7 +181,6 @@ object VerifiedRegex { ) def hashId[C](r: Regex[C])(implicit idC: IDGiver[C]): Long = { - require(regexDepth(r) <= 30) decreases(r) r match { case ElementMatch(c) => From b84d765b18367762b0393f77b6e3201d45fa89d3 Mon Sep 17 00:00:00 2001 From: Samuel Chassot Date: Thu, 7 Mar 2024 13:54:08 +0100 Subject: [PATCH 09/78] working on memoised regex --- .../scala/ch/epfl/chassot/ListLongMap.scala | 26 + .../main/scala/ch/epfl/chassot/ListMap.scala | 1265 +++++++++++++++ .../ch/epfl/chassot/MutableHashMap.scala | 1377 +++++++++++++++++ .../main/scala/ch/epfl/lexer/ListUtils.scala | 11 +- .../scala/ch/epfl/lexer/VerifiedRegex.scala | 203 +-- 5 files changed, 2757 insertions(+), 125 deletions(-) create mode 100644 lexers/regex/verifiedlexer/src/main/scala/ch/epfl/chassot/ListMap.scala create mode 100644 lexers/regex/verifiedlexer/src/main/scala/ch/epfl/chassot/MutableHashMap.scala diff --git a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/chassot/ListLongMap.scala b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/chassot/ListLongMap.scala index 2ab9ad17..bb01e80a 100644 --- a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/chassot/ListLongMap.scala +++ b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/chassot/ListLongMap.scala @@ -503,6 +503,7 @@ object TupleListOps { } }.ensuring(_ => containsKey(l, key)) + @opaque @inlineOnce def lemmaContainsKeyImpliesGetValueByKeyDefined[B]( @@ -518,6 +519,22 @@ object TupleListOps { } }.ensuring(_ => getValueByKey(l, key).isDefined) + @opaque + @inlineOnce + def lemmaGetValueByKeyImpliesContainsTuple[B]( + l: List[(Long, B)], + key: Long, + v: B + ): Unit = { + require(invariantList(l) && getValueByKey(l, key) == Some[B](v)) + decreases(l) + l match { + case Cons(head, tl) if (head._1 != key) => + lemmaGetValueByKeyImpliesContainsTuple(tl, key, v) + case _ => () + } + }.ensuring(_ => l.contains((key, v))) + @opaque @inlineOnce def lemmaForallGetValueByKeySameWithASmallerHead[B]( @@ -943,6 +960,15 @@ object ListLongMapLemmas { }.ensuring(_ => lm.get(a) == Some[B](b)) + @opaque + @inlineOnce + def lemmaGetValueImpliesTupleContained[B](lm: ListLongMap[B], a: Long, b: B): Unit = { + require(lm.contains(a)) + require(lm.get(a) == Some[B](b)) + + TupleListOps.lemmaGetValueByKeyImpliesContainsTuple(lm.toList, a, b) + } ensuring (_ => lm.toList.contains((a, b))) + @opaque def keysOfSound[B](@induct lm: ListLongMap[B], value: B): Unit = { // trivial by postcondition of getKeysOf diff --git a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/chassot/ListMap.scala b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/chassot/ListMap.scala new file mode 100644 index 00000000..a889ee70 --- /dev/null +++ b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/chassot/ListMap.scala @@ -0,0 +1,1265 @@ +/** Author: Samuel Chassot + */ + +package ch.epfl.chassot + +import stainless.annotation._ +import stainless.collection._ +import stainless.equations._ +import stainless.lang._ +import stainless.proof.check +import scala.annotation.tailrec +import scala.collection.immutable + +// Uncomment the following import to run benchmarks +// import OptimisedChecks.* + +trait Ordering[T]: + def compare(x: T, y: T): Int + + @law def inverse(x: T, y: T): Boolean = + sign(compare(x, y)) == -sign(compare(y, x)) + + @law def transitive(x: T, y: T, z: T): Boolean = + if (compare(x, y) > 0 && compare(y, z) > 0) then compare(x, z) > 0 else if (compare(x, y) < 0 && compare(y, z) < 0) then compare(x, z) < 0 else true + + @law def consistent(x: T, y: T, z: T): Boolean = + if compare(x, y) == 0 then sign(compare(x, z)) == sign(compare(y, z)) else true + + @law def equalsMeansEquals(x: T, y: T): Boolean = + (compare(x, y) == 0) == (x == y) + + final def sign(x: Int): BigInt = + if x < 0 then -1 else if x > 0 then 1 else 0 + +end Ordering + +case class ListMap[K, B](toList: List[(K, B)], ordd: Ordering[K]) { + require(TupleListOpsGenK.isStrictlySorted(toList)(ordd)) + + def isEmpty: Boolean = toList.isEmpty + + def head: (K, B) = { + require(!isEmpty) + toList.head + } + + def size: Int = { + require(toList.size < Integer.MAX_VALUE) + TupleListOpsGenK.intSize(toList)(ordd) + } + + @pure + def nKeys: Int = { + require(toList.size < Integer.MAX_VALUE) + TupleListOpsGenK.intSizeKeys(TupleListOpsGenK.getKeysList(toList)(ordd))(ordd) + } + + def tail: ListMap[K, B] = { + require(!isEmpty) + ListMap(toList.tail, ordd) + } + + def contains(key: K): Boolean = { + val res = TupleListOpsGenK.containsKey(toList, key)(ordd) + if (res) { + TupleListOpsGenK.lemmaContainsKeyImpliesGetValueByKeyDefined(toList, key)(ordd) + } + res + + }.ensuring(res => !res || this.get(key).isDefined) + + @inline + def get(key: K): Option[B] = { + TupleListOpsGenK.getValueByKey(toList, key)(ordd) + } + + @inline + def keysOf(value: B): List[K] = { + TupleListOpsGenK.getKeysOf(toList, value)(ordd) + } + + def keys(): List[K] = { + TupleListOpsGenK.getKeysList(toList)(ordd) + } + + def apply(key: K): B = { + require(contains(key)) + get(key).get + } + + def +(keyValue: (K, B)): ListMap[K, B] = { + val newList = + TupleListOpsGenK.insertStrictlySorted(toList, keyValue._1, keyValue._2)(ordd) + + TupleListOpsGenK.lemmaContainsTupThenGetReturnValue( + newList, + keyValue._1, + keyValue._2 + )(ordd) + ListMap(newList, ordd) + + }.ensuring(res => + res.contains(keyValue._1) && res.get(keyValue._1) == Some[B]( + keyValue._2 + ) && res.toList.contains(keyValue) + ) + + def ++(keyValues: List[(K, B)]): ListMap[K, B] = { + decreases(keyValues) + keyValues match { + case Nil() => this + case Cons(keyValue, rest) => (this + keyValue) ++ rest + } + } + def -(key: K): ListMap[K, B] = { + ListMap(TupleListOpsGenK.removeStrictlySorted(toList, key)(ordd), ordd) + }.ensuring(res => !res.contains(key)) + + def --(keys: List[K]): ListMap[K, B] = { + decreases(keys) + keys match { + case Nil() => this + case Cons(key, rest) => (this - key) -- rest + } + } + @inline + def forall(p: ((K, B)) => Boolean): Boolean = { + toList.forall(p) + } +} + +object TupleListOpsGenK { + + extension [K](k: K) def >(other: K)(implicit ord: Ordering[K]): Boolean = ord.compare(k, other) > 0 + extension [K](k: K) def <(other: K)(implicit ord: Ordering[K]): Boolean = ord.compare(k, other) < 0 + extension [K](k: K) def >=(other: K)(implicit ord: Ordering[K]): Boolean = ord.compare(k, other) >= 0 + extension [K](k: K) def <=(other: K)(implicit ord: Ordering[K]): Boolean = ord.compare(k, other) <= 0 + + // @inline + def invariantList[K, B](l: List[(K, B)])(implicit ord: Ordering[K]): Boolean = { + isStrictlySorted(l) + } + + def getKeysList[K, B](l: List[(K, B)])(implicit ord: Ordering[K]): List[K] = { + require(invariantList(l)) + decreases(l) + l match { + case Cons(head, tl) => Cons(head._1, getKeysList(tl)) + case Nil() => Nil[K]() + } + }.ensuring(res => isStrictlySortedK(res) && res.length == l.length) + + @pure + def intSizeKeys[K](l: List[K])(implicit ord: Ordering[K]): Int = { + require(l.length < Integer.MAX_VALUE) + decreases(l) + + l match { + case Cons(head, tl) => 1 + intSizeKeys(tl) + case Nil() => 0 + } + } + + def intSize[K, B](l: List[(K, B)])(implicit ord: Ordering[K]): Int = { + decreases(l) + l match { + case Cons(head, tl) => { + val s1 = intSize(tl) + if (s1 < Integer.MAX_VALUE) { + 1 + s1 + } else { + 0 + } + } + + case Nil() => 0 + } + }.ensuring(res => res >= 0) + + def getKeysOf[K, B](l: List[(K, B)], value: B)(implicit ord: Ordering[K]): List[K] = { + require(invariantList(l)) + decreases(l) + + l match { + case Cons(head, tl) if (head._2 == value) => { + if (!getKeysOf(tl, value).isEmpty) { + lemmaForallGetValueByKeySameWithASmallerHead( + tl, + getKeysOf(tl, value), + value, + head + ) + + } + Cons(head._1, getKeysOf(tl, value)) + } + case Cons(head, tl) if (head._2 != value) => { + val r = getKeysOf(tl, value) + if (!getKeysOf(tl, value).isEmpty) { + lemmaForallGetValueByKeySameWithASmallerHead( + tl, + getKeysOf(tl, value), + value, + head + ) + } + getKeysOf(tl, value) + } + case Nil() => Nil[K]() + } + + }.ensuring(res => res.forall(getValueByKey(l, _) == Some[B](value))) + + def filterByValue[K, B](l: List[(K, B)], value: B)(implicit ord: Ordering[K]): List[(K, B)] = { + require(invariantList(l)) + decreases(l) + + l match { + case Cons(head, tl) if (head._2 == value) => + check(ord.equalsMeansEquals(head._1, head._1)) + val res = head :: filterByValue(tl, value) + filterByValue(tl, value) match { + case Cons(a, _) => + lemmaInTailThenBigger(head, tl, a) + case _ => () + } + res + case Cons(head, tl) if (head._2 != value) => + val res = filterByValue(tl, value) + filterByValue(tl, value) match { + case Cons(a, _) => + lemmaInTailThenBigger(head, tl, a) + check(ord.inverse(head._1, a._1)) + case _ => () + } + res + case Nil() => Nil[(K, B)]() + } + }.ensuring(res => + invariantList(res) && res.forall(_._2 == value) && + (if (l.isEmpty) res.isEmpty else res.isEmpty || res.head._1 >= l.head._1 && l.contains(res.head)) + ) + + def getValueByKey[K, B](l: List[(K, B)], key: K)(implicit ord: Ordering[K]): Option[B] = { + require(invariantList(l)) + decreases(l) + + l match { + case Cons(head, tl) if (head._1 == key) => Some[B](head._2) + case Cons(head, tl) if (head._1 != key) => getValueByKey(tl, key) + case Nil() => None[B]() + } + + } + + def insertStrictlySorted[K, B]( + l: List[(K, B)], + newKey: K, + newValue: B + )(implicit ord: Ordering[K]): List[(K, B)] = { + require(invariantList(l)) + decreases(l) + + l match { + case Cons(a, _) => { + check(ord.equalsMeansEquals(a._1, newKey)) + check(ord.inverse(a._1, newKey)) + } + case _ => () + } + + l match { + case Cons(head, tl) if (head._1 < newKey) => + head :: insertStrictlySorted(tl, newKey, newValue) + case Cons(head, tl) if (head._1 == newKey) => (newKey, newValue) :: tl + case Cons(head, tl) if (head._1 > newKey) => + (newKey, newValue) :: Cons(head, tl) + case Nil() => (newKey, newValue) :: Nil() + } + }.ensuring(res => + invariantList(res) && containsKey(res, newKey) && res.contains( + (newKey, newValue) + ) + ) + + def removeStrictlySorted[K, B]( + l: List[(K, B)], + key: K + )(implicit ord: Ordering[K]): List[(K, B)] = { + require(invariantList(l)) + decreases(l) + + l match { + case Cons(a, Cons(b, Nil())) => { + check(ord.inverse(a._1, b._1)) + } + case Cons(a, Cons(b, Cons(c, _))) => { + check(ord.inverse(a._1, b._1)) + check(ord.inverse(b._1, c._1)) + check(ord.inverse(a._1, c._1)) + + check(ord.transitive(c._1, b._1, a._1)) + check(ord.transitive(a._1, b._1, c._1)) + check(ord.consistent(c._1, b._1, a._1)) + check(ord.consistent(c._1, a._1, b._1)) + check(ord.consistent(a._1, b._1, c._1)) + check(ord.consistent(a._1, c._1, b._1)) + check(ord.consistent(b._1, c._1, a._1)) + check(ord.consistent(b._1, a._1, c._1)) + } + case _ => () + } + + l match { + case Cons(head, tl) if (head._1 == key) => tl + case Cons(head, tl) if (head._1 != key) => + head :: removeStrictlySorted(tl, key)(ord) + case Nil() => Nil[(K, B)]() + } + }.ensuring(res => invariantList(res) && !containsKey(res, key)) + + def isStrictlySorted[K, B](l: List[(K, B)])(implicit ord: Ordering[K]): Boolean = { + decreases(l) + l match { + case Nil() => true + case Cons(_, Nil()) => true + case Cons(h1, Cons(h2, _)) if (h1._1 >= h2._1) => false + case Cons(_, t) => isStrictlySorted(t) + } + } + + def isStrictlySortedK[K](l: List[K])(implicit ord: Ordering[K]): Boolean = { + decreases(l) + l match { + case Nil() => true + case Cons(_, Nil()) => true + case Cons(h1, Cons(h2, _)) if (h1 >= h2) => false + case Cons(_, t) => isStrictlySortedK(t) + } + } + + def containsKey[K, B](l: List[(K, B)], key: K)(implicit ord: Ordering[K]): Boolean = { + require(invariantList(l)) + decreases(l) + l match { + case Cons(a, Cons(b, Nil())) => { + check(ord.equalsMeansEquals(a._1, key)) + check(ord.inverse(a._1, b._1)) + check(ord.inverse(a._1, key)) + check(ord.transitive(a._1, b._1, key)) + check(ord.transitive(a._1, key, b._1)) + check(ord.transitive(key, a._1, b._1)) + } + case Cons(a, Cons(b, Cons(c, _))) => { + check(ord.equalsMeansEquals(a._1, key)) + check(ord.inverse(a._1, b._1)) + check(ord.inverse(b._1, c._1)) + check(ord.inverse(a._1, c._1)) + + check(ord.transitive(c._1, b._1, a._1)) + check(ord.transitive(a._1, b._1, c._1)) + check(ord.consistent(c._1, b._1, a._1)) + check(ord.consistent(c._1, a._1, b._1)) + check(ord.consistent(a._1, b._1, c._1)) + check(ord.consistent(a._1, c._1, b._1)) + check(ord.consistent(b._1, c._1, a._1)) + check(ord.consistent(b._1, a._1, c._1)) + } + case Cons(a, _) => { + check(ord.inverse(a._1, key)) + check(ord.equalsMeansEquals(a._1, key)) + } + case _ => () + } + + l match { + case Cons(head, tl) if (head._1 == key) => true + case Cons(head, tl) if (head._1 > key) => false + case Cons(head, tl) if (head._1 < key) => containsKey(tl, key) + case Nil() => false + + } + } + + // ----------- LEMMAS ----------------------------------------------------- + + @opaque + @inlineOnce + def lemmaGetValueByKeyImpliesContainsTuple[K, B](l: List[(K, B)], key: K, v: B)(implicit ord: Ordering[K]): Unit = { + require(invariantList(l)) + require(getValueByKey(l, key) == Some[B](v)) + decreases(l) + l match { + case Cons(head, tl) if (head._1 != key) => + lemmaGetValueByKeyImpliesContainsTuple(tl, key, v) + case _ => () + } + }.ensuring(_ => l.contains((key, v))) + + @opaque + @inlineOnce + def lemmaInTailThenBigger[K, B]( + head: (K, B), + tail: List[(K, B)], + test: (K, B) + )(implicit ord: Ordering[K]): Unit = { + require(invariantList(Cons(head, tail))) + require(tail.contains(test)) + decreases(tail) + + tail match { + case Cons(hd, tl) if (hd._1 != test._1) => + check(ord.transitive(head._1, hd._1, tl.head._1)) + lemmaInTailThenBigger(head, tl, test) + case _ => () + } + }.ensuring(_ => head._1 < test._1) + + @opaque + @inlineOnce + def lemmaInsertAndRemoveStrictlySortedCommutative[K, B]( + l: List[(K, B)], + key1: K, + v1: B, + key2: K + )(implicit ord: Ordering[K]): Unit = { + require(key1 != key2) + require(invariantList(l)) + decreases(l) + + l match { + case Cons(head, tl) => { + lemmaInsertAndRemoveStrictlySortedCommutative(tl, key1, v1, key2) + } + case _ => () + } + + }.ensuring(_ => + insertStrictlySorted( + removeStrictlySorted(l, key2), + key1, + v1 + ) == removeStrictlySorted( + insertStrictlySorted(l, key1, v1), + key2 + ) + ) + + @opaque + @inlineOnce + def lemmaInsertStrictlySortedThenRemoveIsSame[K, B]( + l: List[(K, B)], + key1: K, + v1: B + )(implicit ord: Ordering[K]): Unit = { + require(invariantList(l)) + require(!containsKey(l, key1)) + decreases(l) + + l match { + case Cons(head, tl) => { + lemmaTailStillNotContainsKey(l, key1) + lemmaInsertStrictlySortedThenRemoveIsSame(tl, key1, v1) + } + case _ => () + } + + }.ensuring(_ => removeStrictlySorted(insertStrictlySorted(l, key1, v1), key1) == l) + + @opaque + @inlineOnce + def lemmaRemoveThenInsertStrictlySortedIsSameAsInsert[K, B]( + l: List[(K, B)], + key1: K, + v1: B + )(implicit ord: Ordering[K]): Unit = { + require(invariantList(l)) + decreases(l) + + l match { + case Cons(head, tl) => { + lemmaRemoveThenInsertStrictlySortedIsSameAsInsert(tl, key1, v1) + } + case _ => () + } + + }.ensuring(_ => insertStrictlySorted(removeStrictlySorted(l, key1), key1, v1) == insertStrictlySorted(l, key1, v1)) + + @opaque + @inlineOnce + def lemmaInsertStrictlySortedCommutative[K, B]( + l: List[(K, B)], + key1: K, + v1: B, + key2: K, + v2: B + )(implicit ord: Ordering[K]): Unit = { + require(key1 != key2) + require(invariantList(l)) + decreases(l) + + l match { + case Cons(head, tl) if (head._1 < key1 && head._1 < key2) => { + lemmaInsertStrictlySortedCommutative(tl, key1, v1, key2, v2) + } + case _ => () + } + + }.ensuring(_ => + insertStrictlySorted( + insertStrictlySorted(l, key1, v1), + key2, + v2 + ) == insertStrictlySorted( + insertStrictlySorted(l, key2, v2), + key1, + v1 + ) + ) + + @opaque + @inlineOnce + def lemmaRemoveStrictlySortedCommutative[K, B]( + l: List[(K, B)], + key1: K, + key2: K + )(implicit ord: Ordering[K]): Unit = { + require(invariantList(l)) + decreases(l) + + l match { + case Cons(head, tl) => { + lemmaRemoveStrictlySortedCommutative(tl, key1, key2) + } + case _ => () + } + + }.ensuring(_ => + removeStrictlySorted( + removeStrictlySorted(l, key1), + key2 + ) == removeStrictlySorted( + removeStrictlySorted(l, key2), + key1 + ) + ) + + @opaque + @inlineOnce + def lemmaRemoveStrictlySortedNotPresentPreserves[K, B]( + l: List[(K, B)], + key: K + )(implicit ord: Ordering[K]): Unit = { + require(invariantList(l)) + require(!containsKey(l, key)) + decreases(l) + + l match { + case Cons(head, tl) => { + lemmaTailStillNotContainsKey(l, key) + assert(!containsKey(tl, key)) + lemmaRemoveStrictlySortedNotPresentPreserves(tl, key) + } + case _ => () + } + + }.ensuring(_ => removeStrictlySorted(l, key) == l) + + @opaque + @inlineOnce + def lemmaInsertStrictlySortedErasesIfSameKey[K, B]( + l: List[(K, B)], + key1: K, + v1: B, + v2: B + )(implicit ord: Ordering[K]): Unit = { + require(invariantList(l)) + decreases(l) + + l match { + case Cons(head, tl) if (head._1 < key1) => { + lemmaInsertStrictlySortedErasesIfSameKey(tl, key1, v1, v2) + } + case _ => () + } + + }.ensuring(_ => + insertStrictlySorted( + insertStrictlySorted(l, key1, v1), + key1, + v2 + ) == insertStrictlySorted( + l, + key1, + v2 + ) + ) + + @opaque + @inlineOnce + def lemmaAddNewKeyIncrementSize[K, B]( + l: List[(K, B)], + key: K, + value: B + )(implicit ord: Ordering[K]): Unit = { + require(invariantList(l)) + require(!containsKey(l, key)) + decreases(l) + + val inserted = insertStrictlySorted(l, key, value) + l match { + case Cons(head, tl) if (head._1 < key) => { + lemmaAddNewKeyIncrementSize(tl, key, value) + + } + case Cons(head, tl) if (head._1 == key) => check(false) + case _ => + } + + }.ensuring(_ => insertStrictlySorted(l, key, value).length == l.length + 1) + + @opaque + @inlineOnce + def lemmaAddExistingKeyPreservesSize[K, B]( + l: List[(K, B)], + key: K, + value: B + )(implicit ord: Ordering[K]): Unit = { + decreases(l) + require(invariantList(l)) + require(containsKey(l, key)) + + val inserted = insertStrictlySorted(l, key, value) + l match { + case Cons(head, tl) if (head._1 < key) => { + lemmaAddExistingKeyPreservesSize(tl, key, value) + } + case Cons(head, tl) if (head._1 == key) => { + assert(inserted == Cons((key, value), tl)) + } + case _ => + } + + }.ensuring(_ => insertStrictlySorted(l, key, value).length == l.length) + + @opaque + @inlineOnce + def lemmaGetValueByKeyIsDefinedImpliesContainsKey[K, B]( + l: List[(K, B)], + key: K + )(implicit ord: Ordering[K]): Unit = { + require(invariantList(l) && getValueByKey(l, key).isDefined) + decreases(l) + l match { + case Cons(head, tl) if (head._1 != key) => + lemmaGetValueByKeyIsDefinedImpliesContainsKey(tl, key) + lemmaAddHeadStillContainsKey(tl, head._1, head._2, key) + case _ => () + } + }.ensuring(_ => containsKey(l, key)) + + @opaque + @inlineOnce + def lemmaContainsKeyImpliesGetValueByKeyDefined[K, B]( + l: List[(K, B)], + key: K + )(implicit ord: Ordering[K]): Unit = { + require(invariantList(l)) + require(containsKey(l, key)) + decreases(l) + l match { + case Cons(head, tl) if (head._1 != key) => + lemmaContainsKeyImpliesGetValueByKeyDefined(tl, key) + case _ => () + } + }.ensuring(_ => getValueByKey(l, key).isDefined) + + @opaque + @inlineOnce + def lemmaForallGetValueByKeySameWithASmallerHead[K, B]( + l: List[(K, B)], + keys: List[K], + value: B, + newHead: (K, B) + )(implicit ord: Ordering[K]): Unit = { + require(invariantList(l)) + require(!l.isEmpty) + require(keys.forall(getValueByKey(l, _) == Some[B](value))) + require(newHead._1 < l.head._1) + decreases(keys) + + keys match { + case Cons(head, tl) => { + lemmaGetValueByKeyIsDefinedImpliesContainsKey(l, head) + lemmaAddHeadStillContainsKey(l, newHead._1, newHead._2, head) + lemmaContainsKeyImpliesGetValueByKeyDefined(Cons(newHead, l), head) + lemmaForallGetValueByKeySameWithASmallerHead(l, tl, value, newHead) + } + case _ => () + } + + }.ensuring(_ => keys.forall(k => getValueByKey(Cons(newHead, l), k) == Some[B](value))) + + @opaque + @inlineOnce + def lemmaAddHeadStillContainsKey[K, B]( + l: List[(K, B)], + key: K, + value: B, + test: K + )(implicit ord: Ordering[K]): Unit = { + require(invariantList(l)) + require(containsKey(l, test)) + require(key < l.head._1) + decreases(l) + + l match { + case Cons(head, tl) if (head._1 < test) => + check(ord.transitive(key, head._1, tl.head._1)) + lemmaAddHeadStillContainsKey(tl, key, value, test) + case _ => () + } + + }.ensuring(_ => containsKey(Cons((key, value), l), test)) + + @opaque + @inlineOnce + def lemmaTailStillNotContainsKey[K, B]( + l: List[(K, B)], + test: K + )(implicit ord: Ordering[K]): Unit = { + require(invariantList(l)) + require(!containsKey(l, test)) + require(!l.isEmpty) + decreases(l) + + l match { + case Cons(head, Nil()) => () + case Cons(head, tl) if (head._1 != test) => + if (containsKey(tl, test)) { + lemmaAddHeadStillContainsKey(tl, head._1, head._2, test) + check(false) + } + lemmaTailStillNotContainsKey(tl, test) + case _ => () + } + + }.ensuring(_ => !containsKey(l.tail, test)) + + @opaque + @inlineOnce + def lemmaInsertStrictlySortedDoesNotModifyOtherKeyValues[K, B]( + l: List[(K, B)], + newKey: K, + newValue: B, + otherKey: K + )(implicit ord: Ordering[K]): Unit = { + require(invariantList(l) && newKey != otherKey) + decreases(l) + + l match { + case Cons(head, tl) if (head._1 != otherKey) => + lemmaInsertStrictlySortedDoesNotModifyOtherKeyValues( + tl, + newKey, + newValue, + otherKey + ) + case _ => () + } + + }.ensuring(_ => + containsKey( + insertStrictlySorted(l, newKey, newValue), + otherKey + ) == containsKey(l, otherKey) && + getValueByKey( + insertStrictlySorted(l, newKey, newValue), + otherKey + ) == getValueByKey( + l, + otherKey + ) + ) + + @opaque + @inlineOnce + def lemmaInsertStrictlySortedDoesNotModifyOtherKeysNotContained[K, B]( + l: List[(K, B)], + newKey: K, + newValue: B, + otherKey: K + )(implicit ord: Ordering[K]): Unit = { + require(invariantList(l)) + require(!containsKey(l, otherKey)) + require(otherKey != newKey) + decreases(l) + + l match { + case Cons(head, tl) => + lemmaTailStillNotContainsKey(l, otherKey) + assert(!containsKey(tl, otherKey)) + lemmaInsertStrictlySortedDoesNotModifyOtherKeysNotContained( + tl, + newKey, + newValue, + otherKey + ) + case _ => () + } + }.ensuring(_ => !containsKey(insertStrictlySorted(l, newKey, newValue), otherKey)) + + @opaque + @inlineOnce + def lemmaInsertStrictlySortedDoesNotModifyOtherKeysContained[K, B]( + l: List[(K, B)], + newKey: K, + newValue: B, + otherKey: K + )(implicit ord: Ordering[K]): Unit = { + require(invariantList(l) && containsKey(l, otherKey) && otherKey != newKey) + decreases(l) + + l match { + case Cons(head, tl) if (head._1 != otherKey) => + lemmaInsertStrictlySortedDoesNotModifyOtherKeysContained( + tl, + newKey, + newValue, + otherKey + ) + case _ => () + } + }.ensuring(_ => containsKey(insertStrictlySorted(l, newKey, newValue), otherKey)) + + @opaque + @inlineOnce + def lemmaInsertStrictlySortedNotContainedContent[K, B]( + l: List[(K, B)], + newKey: K, + newValue: B + )(implicit ord: Ordering[K]): Unit = { + require(invariantList(l)) + require(!containsKey(l, newKey)) + decreases(l) + + l match { + case Cons(head, tl) => { + lemmaTailStillNotContainsKey(l, newKey) + lemmaInsertStrictlySortedNotContainedContent(tl, newKey, newValue) + } + case _ => () + } + + } ensuring (_ => + l.content ++ Set((newKey, newValue)) == insertStrictlySorted( + l, + newKey, + newValue + ).content + ) + + @opaque + @inlineOnce + def lemmaNotContainsKeyThenNotContainsTuple[K, B]( + l: List[(K, B)], + key: K, + value: B + )(implicit ord: Ordering[K]): Unit = { + require(invariantList(l)) + require(!containsKey(l, key)) + decreases(l) + l match { + case Cons(head, tl) => + lemmaTailStillNotContainsKey(l, key) + lemmaNotContainsKeyThenNotContainsTuple(tl, key, value) + case _ => () + } + + }.ensuring(_ => !l.contains((key, value))) + + @opaque + @inlineOnce + def lemmaContainsTupleThenContainsKey[K, B]( + l: List[(K, B)], + key: K, + value: B + )(implicit ord: Ordering[K]): Unit = { + require(invariantList(l)) + require(l.contains((key, value))) + decreases(l) + + l match { + case Cons(a, Cons(b, _)) => + check(ord.transitive(a._1, b._1, key)) + case _ => () + } + + l match { + case Cons(head, tl) if (head != (key, value)) => + lemmaContainsTupleThenContainsKey(tl, key, value) + case _ => () + } + }.ensuring(_ => containsKey(l, key)) + + @opaque + @inlineOnce + def lemmaContainsTupThenGetReturnValue[K, B]( + l: List[(K, B)], + key: K, + value: B + )(implicit ord: Ordering[K]): Unit = { + require(invariantList(l) && containsKey(l, key) && l.contains((key, value))) + decreases(l) + + l match { + case head :: Nil() => () + case Cons(head, tl) if (head._1 == key) => + lemmaNotContainsKeyThenNotContainsTuple(tl, key, value) + case Cons(head, tl) => lemmaContainsTupThenGetReturnValue(tl, key, value) + case Nil() => () + } + }.ensuring(_ => getValueByKey(l, key) == Some[B](value)) +} + +object ListMap { + // def apply[K, B](l: List[(K, B)])(using ord: Ordering[K]): ListMap[K, B] = ListMap(l, ord, ()) + def empty[K, B](implicit ord: Ordering[K]): ListMap[K, B] = ListMap[K, B](List.empty[(K, B)], ord) +} + +object ListMapLemmas { + import ListSpecs._ + + @opaque + @inlineOnce + def removeNotPresentStillSame[K, B](lm: ListMap[K, B], a: K): Unit = { + require(!lm.contains(a)) + TupleListOpsGenK.lemmaRemoveStrictlySortedNotPresentPreserves(lm.toList, a)(lm.ordd) + }.ensuring(_ => lm - a == lm) + + @opaque + @inlineOnce + def addSameAsAddTwiceSameKeyDiffValues[K, B]( + lm: ListMap[K, B], + a: K, + b1: B, + b2: B + ): Unit = { + TupleListOpsGenK.lemmaInsertStrictlySortedErasesIfSameKey(lm.toList, a, b1, b2)(lm.ordd) + }.ensuring(_ => lm + (a, b2) == (lm + (a, b1) + (a, b2))) + + @opaque + @inlineOnce + def addRemoveCommutativeForDiffKeys[K, B]( + lm: ListMap[K, B], + a1: K, + b1: B, + a2: K + ): Unit = { + require(a1 != a2) + TupleListOpsGenK.lemmaInsertAndRemoveStrictlySortedCommutative( + lm.toList, + a1, + b1, + a2 + )(lm.ordd) + }.ensuring(_ => lm + (a1, b1) - a2 == lm - a2 + (a1, b1)) + + @opaque + @inlineOnce + def addThenRemoveForNewKeyIsSame[K, B]( + lm: ListMap[K, B], + a1: K, + b1: B + ): Unit = { + require(!lm.contains(a1)) + TupleListOpsGenK.lemmaInsertStrictlySortedThenRemoveIsSame(lm.toList, a1, b1)(lm.ordd) + }.ensuring(_ => lm + (a1, b1) - a1 == lm) + + @opaque + @inlineOnce + def removeThenAddForSameKeyIsSameAsAdd[K, B]( + lm: ListMap[K, B], + a1: K, + b1: B + ): Unit = { + TupleListOpsGenK.lemmaRemoveThenInsertStrictlySortedIsSameAsInsert(lm.toList, a1, b1)(lm.ordd) + }.ensuring(_ => lm - a1 + (a1, b1) == lm + (a1, b1)) + + @opaque + @inlineOnce + def removeCommutative[K, B](lm: ListMap[K, B], a1: K, a2: K): Unit = { + TupleListOpsGenK.lemmaRemoveStrictlySortedCommutative(lm.toList, a1, a2)(lm.ordd) + }.ensuring(_ => lm - a1 - a2 == lm - a2 - a1) + + @opaque + @inlineOnce + def addCommutativeForDiffKeys[K, B]( + lm: ListMap[K, B], + a1: K, + b1: B, + a2: K, + b2: B + ): Unit = { + require(a1 != a2) + TupleListOpsGenK.lemmaInsertStrictlySortedCommutative(lm.toList, a1, b1, a2, b2)(lm.ordd) + }.ensuring(_ => lm + (a1, b1) + (a2, b2) == lm + (a2, b2) + (a1, b1)) + + @opaque + @inlineOnce + def addValidProp[K, B]( + lm: ListMap[K, B], + p: ((K, B)) => Boolean, + a: K, + b: B + ): Unit = { + require(lm.forall(p) && p(a, b)) + decreases(lm.toList.size) + + if (!lm.isEmpty) + addValidProp(lm.tail, p, a, b) + + }.ensuring { _ => + val nlm = lm + (a, b) + nlm.forall(p) + } + + @opaque + @inlineOnce + def removeValidProp[K, B]( + lm: ListMap[K, B], + p: ((K, B)) => Boolean, + a: K + ): Unit = { + require(lm.forall(p)) + decreases(lm.toList.size) + if (!lm.isEmpty) + removeValidProp(lm.tail, p, a) + + }.ensuring { _ => + val nlm = lm - a + nlm.forall(p) + } + + @opaque + @inlineOnce + def insertAllValidProp[K, B]( + lm: ListMap[K, B], + kvs: List[(K, B)], + p: ((K, B)) => Boolean + ): Unit = { + require(lm.forall(p) && kvs.forall(p)) + decreases(kvs) + + if (!kvs.isEmpty) { + addValidProp(lm, p, kvs.head._1, kvs.head._2) + insertAllValidProp(lm + kvs.head, kvs.tail, p) + } + + }.ensuring { _ => + val nlm = lm ++ kvs + nlm.forall(p) + } + + @opaque + @inlineOnce + def removeAllValidProp[K, B]( + lm: ListMap[K, B], + l: List[K], + p: ((K, B)) => Boolean + ): Unit = { + require(lm.forall(p)) + decreases(l) + + if (!l.isEmpty) { + removeValidProp(lm, p, l.head) + removeAllValidProp(lm - l.head, l.tail, p) + } + + }.ensuring { _ => + val nlm = lm -- l + nlm.forall(p) + } + + @opaque + @inlineOnce + def addApplyDifferent[K, B]( + lm: ListMap[K, B], + a: K, + b: B, + a0: K + ): Unit = { + require(lm.contains(a0) && a0 != a) + assert(TupleListOpsGenK.containsKey(lm.toList, a0)(lm.ordd)) + TupleListOpsGenK.lemmaInsertStrictlySortedDoesNotModifyOtherKeyValues( + lm.toList, + a, + b, + a0 + )(lm.ordd) + TupleListOpsGenK.lemmaContainsKeyImpliesGetValueByKeyDefined(lm.toList, a0)(lm.ordd) + + }.ensuring(_ => (lm + (a -> b)).apply(a0) == lm(a0)) + + @opaque + @inlineOnce + def addStillContains[K, B]( + lm: ListMap[K, B], + a: K, + b: B, + a0: K + ): Unit = { + require(lm.contains(a0)) + + if (a != a0) + TupleListOpsGenK.lemmaInsertStrictlySortedDoesNotModifyOtherKeysContained( + lm.toList, + a, + b, + a0 + )(lm.ordd) + + }.ensuring(_ => (lm + (a, b)).contains(a0)) + + @opaque + @inlineOnce + def addStillNotContains[K, B]( + lm: ListMap[K, B], + a: K, + b: B, + a0: K + ): Unit = { + require(!lm.contains(a0) && a != a0) + + TupleListOpsGenK.lemmaInsertStrictlySortedDoesNotModifyOtherKeysNotContained( + lm.toList, + a, + b, + a0 + )(lm.ordd) + + }.ensuring(_ => !(lm + (a, b)).contains(a0)) + + @opaque + @inlineOnce + def applyForall[K, B]( + lm: ListMap[K, B], + p: ((K, B)) => Boolean, + k: K + ): Unit = { + require(lm.forall(p) && lm.contains(k)) + decreases(lm.toList.size) + + if (!lm.isEmpty && lm.toList.head._1 != k) + applyForall(lm.tail, p, k) + + }.ensuring(_ => p(k, lm(k))) + + @opaque + @inlineOnce + def getForall[K, B]( + lm: ListMap[K, B], + p: ((K, B)) => Boolean, + k: K + ): Unit = { + require(lm.forall(p)) + decreases(lm.toList.size) + + if (!lm.isEmpty && lm.toList.head._1 != k) + getForall(lm.tail, p, k) + + }.ensuring(_ => lm.get(k).forall(v => p(k, v))) + + @opaque + @inlineOnce + def uniqueImage[K, B](lm: ListMap[K, B], a: K, b: B): Unit = { + require(lm.toList.contains((a, b))) + + TupleListOpsGenK.lemmaContainsTupleThenContainsKey(lm.toList, a, b)(lm.ordd) + TupleListOpsGenK.lemmaContainsTupThenGetReturnValue(lm.toList, a, b)(lm.ordd) + + }.ensuring(_ => lm.get(a) == Some[B](b)) + + @opaque + @inlineOnce + def lemmaContainsAllItsOwnKeys[K, B](lm: ListMap[K, B]): Unit = { + decreases(lm.toList.size) + lm.toList match + case Cons(h, t) => { + check(ListMap(t, lm.ordd) + (h._1, h._2) == lm) // Needed + lemmaContainsAllItsOwnKeys(ListMap(t, lm.ordd)) + lemmaInsertPairStillContainsAll(ListMap(t, lm.ordd), t, h._1, h._2) + } + case Nil() => + + } ensuring (_ => lm.toList.forall(p => lm.contains(p._1))) + + @opaque + @inlineOnce + def lemmaInsertPairStillContainsAll[K, B](lm: ListMap[K, B], l: List[(K, B)], k: K, v: B): Unit = { + require(l.forall(p => lm.contains(p._1))) + decreases(l) + l match { + case Cons(h, t) => + if (h._1 != k) { + TupleListOpsGenK.lemmaInsertStrictlySortedDoesNotModifyOtherKeyValues(lm.toList, k, v, h._1)(lm.ordd) + } + lemmaInsertPairStillContainsAll(lm, t, k, v) + case Nil() => () + } + } ensuring (_ => l.forall(p => (lm + (k, v)).contains(p._1))) + + @opaque + @inlineOnce + def addForallContainsKeyThenBeforeAdding[K, B]( + lm: ListMap[K, B], + other: ListMap[K, B], + a: K, + b: B + ): Unit = { + require((lm + (a, b)).toList.forall(p => other.contains(p._1))) + decreases(lm.toList.size) + + if (!lm.isEmpty) { + addForallContainsKeyThenBeforeAdding(lm.tail, other, a, b) + } + + }.ensuring { _ => + lm.toList.forall(p => other.contains(p._1)) + } + + @opaque + @inlineOnce + def lemmaGetValueImpliesTupleContained[K, B](lm: ListMap[K, B], a: K, b: B): Unit = { + require(lm.contains(a)) + require(lm.get(a) == Some[B](b)) + + TupleListOpsGenK.lemmaGetValueByKeyImpliesContainsTuple(lm.toList, a, b)(lm.ordd) + } ensuring (_ => lm.toList.contains((a, b))) + + @opaque + def keysOfSound[K, B](@induct lm: ListMap[K, B], value: B): Unit = { + // trivial by postcondition of getKeysOf + assert(TupleListOpsGenK.getKeysOf(lm.toList, value)(lm.ordd).forall(k => lm.get(k) == Some[B](value))) + }.ensuring(_ => lm.keysOf(value).forall((key: K) => lm.get(key) == Some[B](value))) + + @opaque + @inlineOnce + def addNotContainedContent[K, B]( + lm: ListMap[K, B], + key: K, + value: B + ): Unit = { + require(!lm.contains(key)) + TupleListOpsGenK.lemmaInsertStrictlySortedNotContainedContent( + lm.toList, + key, + value + )(lm.ordd) + } ensuring (_ => + lm.toList.content ++ Set( + (key, value) + ) == (lm + (key, value)).toList.content + ) +} diff --git a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/chassot/MutableHashMap.scala b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/chassot/MutableHashMap.scala new file mode 100644 index 00000000..c384f0ab --- /dev/null +++ b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/chassot/MutableHashMap.scala @@ -0,0 +1,1377 @@ +/** Author: Samuel Chassot + */ +package ch.epfl.chassot + +import stainless.annotation._ +import stainless.collection.{ListMap => ListMapStainless, ListMapLemmas => ListMapLemmasStainless, _} +import stainless.equations._ +import stainless.lang.{ghost => ghostExpr, _} +import stainless.proof.check +import scala.annotation.tailrec +import stainless.lang.Cell +import MutableLongMap._ +import LongMapFixedSize.validMask + +import stainless.lang.StaticChecks.* // Comment out when using the OptimisedEnsuring object below +// import OptimisedChecks.* // Import to remove `ensuring` and `require` from the code for the benchmarks + +trait Hashable[K] { + @pure + def hash(k: K): Long +} + +object MutableHashMap { + + /** Helper method to create a new empty HashMap + * + * @param defaultValue + * @return + */ + def getEmptyHashMap[K, V](defaultValue: K => V, hashF: Hashable[K], ordering: Ordering[K]): HashMap[K, V] = { + val initialSize = 16 + HashMap(Cell(MutableLongMap.getEmptyLongMap[List[(K, V)]]((l: Long) => Nil[(K, V)](), initialSize)), hashF, 0, defaultValue, ordering) + } ensuring (res => res.valid && res.size == 0) + + + @mutable + final case class HashMap[K, V]( + val underlying: Cell[LongMap[List[(K, V)]]], + val hashF: Hashable[K], + var _size: Int, + val defaultValue: K => V, + val ordering: Ordering[K] + ) { + + @pure + def imbalanced(): Boolean = underlying.v.imbalanced() + + @pure + def size: Int = _size + + @pure + def isEmpty: Boolean = { + require(valid) + underlying.v.isEmpty + } ensuring (_ => valid) + + @pure + def contains(key: K): Boolean = { + require(valid) + val hash = hashF.hash(key) + + ghostExpr({ + if (underlying.v.contains(hash)) { + if (!underlying.v.map.toList.contains((hash, underlying.v.apply(hash)))) { + TupleListOps.lemmaGetValueByKeyIsDefinedImpliesContainsKey(underlying.v.map.toList, hash) + TupleListOps.lemmaContainsKeyImpliesGetValueByKeyDefined(underlying.v.map.toList, hash) + TupleListOps.lemmaGetValueByKeyImpliesContainsTuple(underlying.v.map.toList, hash, underlying.v.apply(hash)) + check(false) + } + ListSpecs.forallContained(underlying.v.map.toList, (k, v) => noDuplicateKeys(v), (hash, underlying.v.apply(hash))) + } + }) + ghostExpr({ + if (extractMap(underlying.v.map.toList, ordering).contains(key)) { + lemmaInGenericMapThenInLongMap(underlying.v.map, key, hashF, ordering) + } else { + if (((underlying.v.map.contains(hashF.hash(key)) && getPair(underlying.v.map.apply(hashF.hash(key)), key).isDefined))) { + lemmaInLongMapThenInGenericMap(underlying.v.map, key, hashF, ordering) + check(false) + } + } + }) + underlying.v.contains(hash) && getPair(underlying.v.apply(hash), key).isDefined + } ensuring (res => valid && (res == map.contains(key))) + + @pure + def apply(key: K): V = { + require(valid) + if (!contains(key)) { + defaultValue(key) + } else { + val hash = hashF.hash(key) + ghostExpr({ + if (!underlying.v.map.toList.contains((hash, underlying.v.apply(hash)))) { + TupleListOps.lemmaContainsKeyImpliesGetValueByKeyDefined(underlying.v.map.toList, hash) + TupleListOps.lemmaGetValueByKeyImpliesContainsTuple(underlying.v.map.toList, hash, underlying.v.apply(hash)) + check(false) + } + ListSpecs.forallContained(underlying.v.map.toList, (k, v) => noDuplicateKeys(v), (hash, underlying.v.apply(hash))) + }) + + ghostExpr(lemmaGetPairGetSameValueAsMap(underlying.v.map, key, getPair(underlying.v.apply(hash), key).get._2, hashF, ordering)) + assert(getPair(underlying.v.apply(hash), key).get._2 == map.get(key).get) + getPair(underlying.v.apply(hash), key).get._2 + } + } ensuring (res => + valid + && (if (contains(key)) res == map.get(key).get + else res == defaultValue(key)) + ) + + def update(key: K, v: V): Boolean = { + require(valid) + @ghost val oldMap = map + @ghost val oldLongListMap = underlying.v.map + + val contained = contains(key) + val res = if (contained) { + val hash = hashF.hash(key) + val currentBucket = underlying.v.apply(hash) + // currentBucket contains the key and it is defined + val newBucket = Cons((key, v), removePairForKey(currentBucket, key)) + val res = underlying.v.update(hash, newBucket) + + ghostExpr({ + if (res) { + lemmaInLongMapAllKeySameHashThenForTuple(oldLongListMap.toList, hash, currentBucket, hashF) + lemmaRemovePairForKeyPreservesHash(currentBucket, key, hash, hashF) + lemmaRemovePairForKeyPreservesNoDuplicateKeys(currentBucket, key) + check(noDuplicateKeys(removePairForKey(currentBucket, key))) + check(!containsKey(removePairForKey(currentBucket, key), key)) + check(noDuplicateKeys(newBucket)) + lemmaChangeOneBucketByAValidOnePreservesForallNoDuplicatesAndHash(oldLongListMap, hash, newBucket, hashF, ordering) + lemmaChangeOneBucketToUpdateAnExistingKeyUpdateThisKeyInGenMap(oldLongListMap, hash, newBucket, key, v, hashF, ordering) + check(underlying.v.map.toList.forall((k, v) => noDuplicateKeys(v))) + check(allKeysSameHashInMap(underlying.v.map, hashF)) // TODO + check(map == oldMap + (key, v)) + } else { + check(valid) + check(map == oldMap) + } + }) + if (res && !contained) then _size += 1 + res + + } else { + val hash = hashF.hash(key) + val currentBucket = if underlying.v.contains(hash) then underlying.v.apply(hash) else Nil[(K, V)]() + // Either currentBucket is empty, or it does not contain the key + val newBucket = Cons((key, v), currentBucket) + val res = underlying.v.update(hash, newBucket) + + ghostExpr({ + if (res) { + if (!currentBucket.isEmpty) { + lemmaInLongMapAllKeySameHashThenForTuple(oldLongListMap.toList, hash, currentBucket, hashF) + lemmaRemovePairForKeyPreservesHash(currentBucket, key, hash, hashF) + } + lemmaChangeOneBucketByAValidOnePreservesForallNoDuplicatesAndHash(oldLongListMap, hash, newBucket, hashF, ordering) + lemmaChangeOneBucketToUpdateANewKeyUpdateThisKeyInGenMap(oldLongListMap, hash, newBucket, key, v, hashF, ordering) + check(underlying.v.map.toList.forall((k, v) => noDuplicateKeys(v))) + check(allKeysSameHashInMap(underlying.v.map, hashF)) // TODO + check(map == oldMap + (key, v)) + } else { + check(valid) + check(map == oldMap) + } + }) + + if (res && !contained) then _size += 1 + res + } + res + + } ensuring (res => valid && (if (res) map.contains(key) && (map == old(this).map + (key, v)) else map == old(this).map)) + + def remove(key: K): Boolean = { + require(valid) + val contained = contains(key) + if (!contained) { + ghostExpr({ + lemmaRemoveNotContainedDoesNotChange(underlying.v.map, key, hashF, ordering) + }) + true + } else { + val hash = hashF.hash(key) + val currentBucket = underlying.v.apply(hash) + + ghostExpr(ListSpecs.forallContained(underlying.v.map.toList, (k, v) => noDuplicateKeys(v), (hash, underlying.v.apply(hash)))) + + @ghost val oldMap = map + @ghost val oldLongListMap = underlying.v.map + + val newBucket = removePairForKey(currentBucket, key) + val res = underlying.v.update(hash, newBucket) + if (res && contained) then _size -= 1 + + ghostExpr({ + lemmaRemovePairForKeyPreservesNoDuplicateKeys(currentBucket, key) + if (res) { + lemmaInLongMapAllKeySameHashThenForTuple(oldLongListMap.toList, hash, currentBucket, hashF) + lemmaRemovePairForKeyPreservesHash(currentBucket, key, hash, hashF) + lemmaChangeOneBucketByAValidOnePreservesForallNoDuplicatesAndHash(oldLongListMap, hash, newBucket, hashF, ordering) + check(underlying.v.map.toList.forall((k, v) => noDuplicateKeys(v))) + check(allKeysSameHashInMap(underlying.v.map, hashF)) + check(valid) + check(oldMap.contains(key)) + lemmaChangeOneBucketToRemoveAKeyRemoveThisKeyInGenMap(oldLongListMap, hash, newBucket, key, hashF, ordering) + check(map == oldMap - key) + } else { + check(valid) + check(map == oldMap) + } + }) + res + } + + } ensuring (res => valid && (if (res) map == old(this).map - key else map == old(this).map)) + + @ghost + def valid: Boolean = underlying.v.valid && + underlying.v.map.toList.forall((k, v) => noDuplicateKeys(v)) && + allKeysSameHashInMap(underlying.v.map, hashF) + + @pure + @ghost + private def map: ListMap[K, V] = { + require(valid) + extractMap(underlying.v.map.toList, ordering) + } + + } + @ghost + def extractMap[K, V](l: List[(Long, List[(K, V)])], ordering: Ordering[K]): ListMap[K, V] = { + require(l.forall((k, v) => noDuplicateKeys(v))) + decreases(l) + l match { + case Cons((k, v), tl) => addToMapMapFromBucket(v, extractMap(tl, ordering)) + case Nil() => ListMap.empty[K, V](ordering) + } + } ensuring (res => true) + + @ghost + def addToMapMapFromBucket[K, V](l: List[(K, V)], acc: ListMap[K, V]): ListMap[K, V] = { + require(noDuplicateKeys(l)) + decreases(l) + l match { + case Nil() => { + ListMapLemmas.lemmaContainsAllItsOwnKeys(acc) + check(acc.toList.forall(p => acc.contains(p._1))) + acc + } + case Cons((k, v), tl) => { + val newAcc = acc + (k, v) + val res = addToMapMapFromBucket(tl, acc + (k, v)) + ListMapLemmas.lemmaContainsAllItsOwnKeys(acc) + check(newAcc.toList.forall(p => res.contains(p._1))) + check(tl.forall(p => res.contains(p._1))) + check(l == Cons((k, v), tl)) + ListSpecs.forallContained(newAcc.toList, (k, v) => res.contains(k), (k, v)) + check(newAcc.contains(k)) + check(res.contains(k)) + check(l.forall(p => res.contains(p._1))) + check(newAcc.toList.forall(p => res.contains(p._1))) + check(newAcc == acc + (k, v)) + ListMapLemmas.addForallContainsKeyThenBeforeAdding(acc, res, k, v) + + check(acc.toList.forall(p => res.contains(p._1))) + check(l.forall(p => res.contains(p._1)) && acc.toList.forall(p => res.contains(p._1))) + res + } + } + } ensuring (res => l.forall(p => res.contains(p._1)) && acc.toList.forall(p => res.contains(p._1))) + + @ghost + def noDuplicateKeys[K, V](l: List[(K, V)]): Boolean = { + l match { + case Cons(hd, tl) => !containsKey(tl, hd._1) && noDuplicateKeys(tl) + case Nil() => true + } + } + + @pure + @ghost + def allKeysSameHashInMap[K, V](lm: ListLongMap[List[(K, V)]], hashF: Hashable[K]): Boolean = { + lm.toList.forall((k, v) => allKeysSameHash(v, k, hashF)) + } + + @ghost + def allKeysSameHash[K, V](l: List[(K, V)], h: Long, hashF: Hashable[K]): Boolean = { + l.forall(p => hashF.hash(p._1) == h) + } + @ghost + def containsKey[K, V](l: List[(K, V)], key: K): Boolean = { + l match + case Cons(hd, tl) if hd._1 == key => true + case Cons(_, tl) => containsKey(tl, key) + case Nil() => false + } + @ghost + def containsKeyBiggerList[K, V](l: List[(Long, List[(K, V)])], key: K): Boolean = { + l match + case Cons(h, t) if containsKey(h._2, key) => true + case Cons(h, t) => containsKeyBiggerList(t, key) + case Nil() => false + } + + def getPair[K, V](l: List[(K, V)], key: K): Option[(K, V)] = { + require(noDuplicateKeys(l)) + l match + case Cons(hd, tl) if hd._1 == key => Some(hd) + case Cons(_, tl) => getPair(tl, key) + case Nil() => None() + } ensuring (res => res.isEmpty && !containsKey(l, key) || res.isDefined && res.get._1 == key && l.contains(res.get)) + + def removePairForKey[K, V](l: List[(K, V)], key: K): List[(K, V)] = { + require(noDuplicateKeys(l)) + l match + case Cons(hd, tl) if hd._1 == key => tl + case Cons(hd, tl) => Cons(hd, removePairForKey(tl, key)) + case Nil() => Nil() + } ensuring (res => !containsKey(res, key)) + + @ghost + def getValue[K, V](l: List[(Long, List[(K, V)])], k: K): V = { + require(containsKeyBiggerList(l, k)) + require(l.forall((k, v) => noDuplicateKeys(v))) + l match { + case Cons(hd, tl) if containsKey(hd._2, k) => getPair(hd._2, k).get._2 + case Cons(hd, tl) => getValue(tl, k) + } + } + + // ----------------- Lemmas ------------------------------------------------------------------------ + + @opaque + @inlineOnce + @ghost + def lemmaInGenericMapThenInLongMap[K, V](lm: ListLongMap[List[(K, V)]], key: K, hashF: Hashable[K], ordering: Ordering[K]): Unit = { + require(lm.toList.forall((k, v) => noDuplicateKeys(v))) + require(allKeysSameHashInMap(lm, hashF)) + require(extractMap(lm.toList, ordering).contains(key)) + + val map = extractMap(lm.toList, ordering) + lemmaInGenMapThenLongMapContainsHash(lm, key, hashF, ordering) + assert(lm.contains(hashF.hash(key))) + ghostExpr({ + val hash = hashF.hash(key) + if (!lm.toList.contains((hash, lm.apply(hash)))) { + TupleListOps.lemmaGetValueByKeyIsDefinedImpliesContainsKey(lm.toList, hash) + TupleListOps.lemmaContainsKeyImpliesGetValueByKeyDefined(lm.toList, hash) + TupleListOps.lemmaGetValueByKeyImpliesContainsTuple(lm.toList, hash, lm.apply(hash)) + check(false) + } + ListSpecs.forallContained(lm.toList, (k, v) => noDuplicateKeys(v), (hash, lm.apply(hash))) + + }) + ListSpecs.forallContained(lm.toList, (k, v) => noDuplicateKeys(v), (hashF.hash(key), lm.apply(hashF.hash(key)))) + + lemmaInGenMapThenGetPairDefined(lm, key, hashF, ordering) + assert(getPair(lm.apply(hashF.hash(key)), key).isDefined) + + } ensuring (_ => (lm.contains(hashF.hash(key)) && getPair(lm.apply(hashF.hash(key)), key).isDefined)) + + @opaque + @inlineOnce + @ghost + def lemmaInLongMapThenInGenericMap[K, V](lm: ListLongMap[List[(K, V)]], key: K, hashF: Hashable[K], ordering: Ordering[K]): Unit = { + require(lm.toList.forall((k, v) => noDuplicateKeys(v))) + require(allKeysSameHashInMap(lm, hashF)) + require(lm.contains(hashF.hash(key))) + require({ + ListLongMapLemmas.lemmaGetValueImpliesTupleContained(lm, hashF.hash(key), lm.apply(hashF.hash(key))) + ListSpecs.forallContained(lm.toList, (k, v) => noDuplicateKeys(v), (hashF.hash(key), lm.apply(hashF.hash(key)))) + getPair(lm.apply(hashF.hash(key)), key).isDefined + }) + + lemmaInLongMapThenContainsKeyBiggerList(lm, key, hashF, ordering) + lemmaListContainsThenExtractedMapContains(lm, key, hashF, ordering) + + } ensuring (_ => extractMap(lm.toList, ordering).contains(key)) + + @opaque + @inlineOnce + @ghost + def lemmaGetPairGetSameValueAsMap[K, V](lm: ListLongMap[List[(K, V)]], key: K, v: V, hashF: Hashable[K], ordering: Ordering[K]): Unit = { + require(lm.toList.forall((k, v) => noDuplicateKeys(v))) + require(allKeysSameHashInMap(lm, hashF)) + require(extractMap(lm.toList, ordering).contains(key)) + require({ + lemmaInGenMapThenLongMapContainsHash(lm, key, hashF, ordering) + ListLongMapLemmas.lemmaGetValueImpliesTupleContained(lm, hashF.hash(key), lm.apply(hashF.hash(key))) + ListSpecs.forallContained(lm.toList, (k, v) => noDuplicateKeys(v), (hashF.hash(key), lm.apply(hashF.hash(key)))) + lemmaInGenMapThenGetPairDefined(lm, key, hashF, ordering) + getPair(lm.apply(hashF.hash(key)), key).get._2 == v + }) + + lemmaInLongMapThenContainsKeyBiggerList(lm, key, hashF, ordering) + check(containsKeyBiggerList(lm.toList, key)) + + lemmaGetValueEquivToGetPair(lm, key, v, hashF, ordering) + lemmaExtractMapPreservesMapping(lm, key, v, hashF, ordering) + + } ensuring (_ => { + lemmaInGenMapThenLongMapContainsHash(lm, key, hashF, ordering) + ListLongMapLemmas.lemmaGetValueImpliesTupleContained(lm, hashF.hash(key), lm.apply(hashF.hash(key))) + ListSpecs.forallContained(lm.toList, (k, v) => noDuplicateKeys(v), (hashF.hash(key), lm.apply(hashF.hash(key)))) + lemmaInGenMapThenGetPairDefined(lm, key, hashF, ordering) + getPair(lm.apply(hashF.hash(key)), key).get._2 == extractMap(lm.toList, ordering).get(key).get + }) + + @opaque + @inlineOnce + @ghost + def lemmaChangeOneBucketToRemoveAKeyRemoveThisKeyInGenMap[K, V](lm: ListLongMap[List[(K, V)]], hash: Long, newBucket: List[(K, V)], key: K, hashF: Hashable[K], ordering: Ordering[K]): Unit = { + require(lm.toList.forall((k, v) => noDuplicateKeys(v))) + require(allKeysSameHashInMap(lm, hashF)) + require(extractMap(lm.toList, ordering).contains(key)) + require(hashF.hash(key) == hash) + require(allKeysSameHash(newBucket, hash, hashF)) + require({ + lemmaInGenMapThenLongMapContainsHash(lm, key, hashF, ordering) + TupleListOps.lemmaGetValueByKeyImpliesContainsTuple(lm.toList, hash, lm.apply(hash)) + ListSpecs.forallContained(lm.toList, (k, v) => noDuplicateKeys(v), (hash, lm.apply(hash))) + newBucket == removePairForKey(lm.apply(hash), key) + }) + require(noDuplicateKeys(newBucket)) + decreases(lm.toList.size) + + lm.toList match { + case Nil() => check(false) + case Cons(hd, tl) if hd._1 == hash => { + if (!containsKey(hd._2, key)) { + lemmaNotInItsHashBucketThenNotInMap(lm, key, hashF, ordering) + check(false) + } + check(containsKey(hd._2, key)) + check(containsKeyBiggerList(List((hd._1, hd._2)), key)) + lemmaListContainsThenExtractedMapContains(ListLongMap(List((hd._1, hd._2))), key, hashF, ordering) + check(extractMap(List((hd._1, hd._2)), ordering).contains(key)) + lemmaChangeOneBucketToRemoveAKeyRemoveThisKeyInGenMapOneHash(hash, hd._2, newBucket, key, hashF, ordering) + check(tl == (lm + (hash, newBucket)).toList.tail) + check(extractMap(List((hash, newBucket)), ordering) == extractMap(List((hash, hd._2)), ordering) - key) + check(lm + (hash, newBucket) == lm.tail + (hash, newBucket)) + check(extractMap((lm.tail).toList, ordering) == extractMap(tl, ordering)) + + if (extractMap((lm.tail).toList, ordering).contains(key)) { + lemmaInGenMapThenLongMapContainsHash(lm.tail, key, hashF, ordering) + check(false) + } + check(!extractMap((lm.tail).toList, ordering).contains(key)) + val oldBucket = hd._2 + lemmaChangeOneBucketToRemoveAKeyRemoveThisKeyInGenMapOneHashIsHead(lm, hash, oldBucket, newBucket, key, hashF, ordering) + + check((lm + (hash, newBucket)).toList == Cons((hash, newBucket), tl)) + check(extractMap((lm + (hash, newBucket)).toList, ordering) == addToMapMapFromBucket(newBucket, extractMap(tl, ordering))) + check(extractMap((lm + (hash, oldBucket)).toList, ordering) == addToMapMapFromBucket(oldBucket, extractMap(tl, ordering))) + check(extractMap((lm + (hash, oldBucket)).toList, ordering) == extractMap(lm.toList, ordering)) + check(extractMap((lm + (hash, newBucket)).toList, ordering) == extractMap(lm.toList, ordering) - key) + } + case Cons(hd, tl) => { + val oldBucket = lm.apply(hash) + check(lm.tail.contains(hash)) + check(lm.tail.apply(hash) == oldBucket) + check(tl.contains((hash, oldBucket))) + if (!containsKey(oldBucket, key)) { + lemmaNotInItsHashBucketThenNotInMap(lm, key, hashF, ordering) + check(false) + } + check(containsKey(oldBucket, key)) + lemmaInLongMapThenContainsKeyBiggerList(lm.tail, key, hashF, ordering) + check(containsKeyBiggerList(tl, key)) + lemmaListContainsThenExtractedMapContains(lm.tail, key, hashF, ordering) + check(extractMap(tl, ordering).contains(key)) + lemmaChangeOneBucketToRemoveAKeyRemoveThisKeyInGenMap(lm.tail, hash, newBucket, key, hashF, ordering) + check(extractMap((lm.tail + (hash, newBucket)).toList, ordering) == extractMap(lm.tail.toList, ordering) - key) + + check(extractMap((lm.tail + lm.head).toList, ordering) == extractMap(lm.toList, ordering)) + check(extractMap(lm.toList, ordering) == extractMap((lm.tail + lm.head).toList, ordering)) + check(lm.head._1 < hash) + check(lm.tail + (hash, newBucket) + lm.head == lm + (hash, newBucket)) + check((lm.tail + (hash, newBucket) + lm.head).head == lm.head) + + check((extractMap((lm.tail + (hash, newBucket) + lm.head).toList, ordering) == addToMapMapFromBucket(lm.head._2, extractMap((lm.tail + (hash, newBucket)).toList, ordering)))) + check((extractMap((lm.tail + (hash, newBucket) + lm.head).toList, ordering) == addToMapMapFromBucket(lm.head._2, (extractMap(lm.tail.toList, ordering) - key)))) + + ListSpecs.forallContained(lm.toList, (k, v) => noDuplicateKeys(v), (hd._1, hd._2)) + check(noDuplicateKeys(lm.head._2)) + + if (containsKey(lm.head._2, key)) { + check(hash != lm.head._1) + val pair = getPair(lm.head._2, key).get + val value = pair._2 + ListSpecs.forallContained(lm.toList, (k, v) => allKeysSameHash(v, k, hashF), (hd._1, hd._2)) + ListSpecs.forallContained(hd._2, p => hashF.hash(p._1) == hd._1, (key, value)) + check(false) + } + check(!containsKey(lm.head._2, key)) + lemmaAddToMapFromBucketMinusKeyIsCommutative(extractMap(lm.tail.toList, ordering), key, lm.head._2) + check(addToMapMapFromBucket(lm.head._2, (extractMap(lm.tail.toList, ordering) - key)) == addToMapMapFromBucket(lm.head._2, (extractMap(lm.tail.toList, ordering))) - key) // TODO + check(addToMapMapFromBucket(lm.head._2, (extractMap(lm.tail.toList, ordering) - key)) == extractMap(lm.toList, ordering) - key) + + check(extractMap((lm.tail + (hash, newBucket) + lm.head).toList, ordering) == extractMap(lm.toList, ordering) - key) + + check(extractMap((lm.tail + (hash, newBucket) + lm.head).toList, ordering) == extractMap((lm.tail + lm.head).toList, ordering) - key) + check(extractMap((lm + (hash, newBucket)).toList, ordering) == extractMap(lm.toList, ordering) - key) + } + } + + } ensuring (_ => { + extractMap((lm + (hash, newBucket)).toList, ordering) == extractMap(lm.toList, ordering) - key + }) + + @opaque + @inlineOnce + @ghost + def lemmaChangeOneBucketToUpdateANewKeyUpdateThisKeyInGenMap[K, V]( + lm: ListLongMap[List[(K, V)]], + hash: Long, + newBucket: List[(K, V)], + key: K, + newValue: V, + hashF: Hashable[K], + ordering: Ordering[K] + ): Unit = { + require(lm.toList.forall((k, v) => noDuplicateKeys(v))) + require(allKeysSameHashInMap(lm, hashF)) + require(hashF.hash(key) == hash) + require(allKeysSameHash(newBucket, hash, hashF)) + require(!extractMap(lm.toList, ordering).contains(key)) + require(lm.contains(hash) && newBucket == Cons((key, newValue), lm.apply(hash)) || !lm.contains(hash) && newBucket == Cons((key, newValue), Nil())) + require(noDuplicateKeys(newBucket)) + + decreases(lm.toList.size) + + check(lm.toList.forall((k, v) => noDuplicateKeys(v))) + ListLongMapLemmas.addValidProp(lm, (k, v) => noDuplicateKeys(v), hash, newBucket) + check((lm + (hash, newBucket)).toList.forall((k, v) => noDuplicateKeys(v))) + lm.toList match { + case Cons(hd, tl) if hd._1 == hash => + assert(lm.contains(hash)) + check((lm + (hash, newBucket)).toList.head == (hash, newBucket)) + check((lm + (hash, newBucket)).toList.tail == tl) + check(extractMap((lm + (hash, newBucket)).toList, ordering) == addToMapMapFromBucket(newBucket, extractMap(tl, ordering))) + + check(newBucket == Cons((key, newValue), lm.apply(hash))) + val newAcc = extractMap(tl, ordering) + (key, newValue) + check(addToMapMapFromBucket(newBucket, extractMap(tl, ordering)) == addToMapMapFromBucket(lm.apply(hash), extractMap(tl, ordering) + (key, newValue))) + lemmaAddToMapFromBucketPlusKeyValueIsCommutative(extractMap(tl, ordering), key, newValue, lm.apply(hash)) + check(addToMapMapFromBucket(newBucket, extractMap(tl, ordering)) == addToMapMapFromBucket(lm.apply(hash), extractMap(tl, ordering)) + (key, newValue)) + check(extractMap((lm + (hash, newBucket)).toList, ordering) == extractMap(lm.toList, ordering) + (key, newValue)) + case Cons(hd, tl) if hd._1 != hash => + check(!extractMap(lm.toList, ordering).contains(key)) + if (extractMap(lm.toList.tail, ordering).contains(key)) { + lemmaExtractTailMapContainsThenExtractMapDoes(lm, key, hashF, ordering) + check(false) + } + lemmaChangeOneBucketToUpdateANewKeyUpdateThisKeyInGenMap(lm.tail, hash, newBucket, key, newValue, hashF, ordering) + check(extractMap((lm.tail + (hash, newBucket)).toList, ordering) == extractMap(lm.tail.toList, ordering) + (key, newValue)) + check(lm.head != (hash, newBucket)) + check(extractMap(lm.toList, ordering) == addToMapMapFromBucket(hd._2, extractMap(lm.tail.toList, ordering))) + + lemmaNotSameHashThenCannotContainKey(lm, key, hd._1, hashF, ordering) + check(!containsKey(hd._2, key)) + lemmaAddToMapFromBucketPlusKeyValueIsCommutative(extractMap(lm.tail.toList, ordering), key, newValue, hd._2) + + case Nil() => () + } + + } ensuring (_ => { + extractMap((lm + (hash, newBucket)).toList, ordering) == extractMap(lm.toList, ordering) + (key, newValue) + }) + + @opaque + @inlineOnce + @ghost + def lemmaChangeOneBucketToUpdateAnExistingKeyUpdateThisKeyInGenMap[K, V]( + lm: ListLongMap[List[(K, V)]], + hash: Long, + newBucket: List[(K, V)], + key: K, + newValue: V, + hashF: Hashable[K], + ordering: Ordering[K] + ): Unit = { + require(lm.toList.forall((k, v) => noDuplicateKeys(v))) + require(allKeysSameHashInMap(lm, hashF)) + require(hashF.hash(key) == hash) + require(allKeysSameHash(newBucket, hash, hashF)) + require(extractMap(lm.toList, ordering).contains(key)) + require(lm.contains(hash)) + require({ + TupleListOps.lemmaGetValueByKeyImpliesContainsTuple(lm.toList, hash, lm.apply(hash)) + ListSpecs.forallContained(lm.toList, (k, v) => noDuplicateKeys(v), (hash, lm.apply(hash))) + newBucket == Cons((key, newValue), removePairForKey(lm.apply(hash), key)) + }) + require(noDuplicateKeys(newBucket)) + + decreases(lm.toList.size) + + check(lm.toList.forall((k, v) => noDuplicateKeys(v))) + ListLongMapLemmas.addValidProp(lm, (k, v) => noDuplicateKeys(v), hash, newBucket) + check((lm + (hash, newBucket)).toList.forall((k, v) => noDuplicateKeys(v))) + + lm.toList match { + case Cons(hd, tl) if hd._1 == hash => + check(lm + (hash, newBucket) == ListLongMap(Cons((hash, newBucket), tl))) + check(extractMap((lm + (hash, newBucket)).toList, ordering) == addToMapMapFromBucket(newBucket, extractMap(tl, ordering))) + check(extractMap((lm + (hash, newBucket)).toList, ordering) == addToMapMapFromBucket(Cons((key, newValue), removePairForKey(lm.apply(hash), key)), extractMap(tl, ordering))) + check(addToMapMapFromBucket(Cons((key, newValue), removePairForKey(lm.apply(hash), key)), extractMap(tl, ordering)) == addToMapMapFromBucket(removePairForKey(lm.apply(hash), key), extractMap(tl, ordering) + (key, newValue))) + lemmaAddToMapFromBucketPlusKeyValueIsCommutative(extractMap(tl, ordering), key, newValue, removePairForKey(lm.apply(hash), key)) + check(addToMapMapFromBucket(Cons((key, newValue), removePairForKey(lm.apply(hash), key)), extractMap(tl, ordering)) == addToMapMapFromBucket(removePairForKey(lm.apply(hash), key), extractMap(tl, ordering)) + (key, newValue)) + + val intermediateLm = lm + (hash, newBucket.tail) + check(newBucket.tail == removePairForKey(lm.apply(hash), key)) + check( addToMapMapFromBucket(removePairForKey(lm.apply(hash), key), extractMap(tl, ordering)) == extractMap(intermediateLm.toList, ordering)) + lemmaChangeOneBucketToRemoveAKeyRemoveThisKeyInGenMap(lm, hash, newBucket.tail, key, hashF, ordering) + check(extractMap(intermediateLm.toList, ordering) == extractMap(lm.toList, ordering) - key) + + check(intermediateLm.apply(hash) == newBucket.tail) + lemmaChangeOneBucketToUpdateANewKeyUpdateThisKeyInGenMap(intermediateLm, hash, newBucket, key, newValue, hashF, ordering) + check(extractMap((intermediateLm + (hash, newBucket)).toList, ordering) == extractMap(intermediateLm.toList, ordering) + (key, newValue)) + check(extractMap(intermediateLm.toList, ordering) == extractMap(lm.toList, ordering) - key) + + ListMapLemmas.removeThenAddForSameKeyIsSameAsAdd(extractMap(lm.toList, ordering), key, newValue) + + check(extractMap((lm + (hash, newBucket)).toList, ordering) == extractMap(lm.toList, ordering) + (key, newValue)) + case Cons(hd, tl) if hd._1 != hash => + lemmaNotSameHashThenCannotContainKey(lm, key, hd._1, hashF, ordering) + + check(!containsKey(hd._2, key)) + + lemmaAddToMapContainsAndNotInListThenInAcc(hd._2, key, newValue, extractMap(tl, ordering)) + + check(extractMap(lm.tail.toList, ordering).contains(key)) + lemmaChangeOneBucketToUpdateAnExistingKeyUpdateThisKeyInGenMap(lm.tail, hash, newBucket, key, newValue, hashF, ordering) + check(extractMap((lm.tail + (hash, newBucket)).toList, ordering) == extractMap(lm.tail.toList, ordering) + (key, newValue)) + check(lm.head != (hash, newBucket)) + check(extractMap(lm.toList, ordering) == addToMapMapFromBucket(hd._2, extractMap(lm.tail.toList, ordering))) + + lemmaAddToMapFromBucketPlusKeyValueIsCommutative(extractMap(lm.tail.toList, ordering), key, newValue, hd._2) + + check(extractMap((lm + (hash, newBucket)).toList, ordering) == extractMap(lm.toList, ordering) + (key, newValue)) + case Nil() => () + } + + } ensuring (_ => { + extractMap((lm + (hash, newBucket)).toList, ordering) == extractMap(lm.toList, ordering) + (key, newValue) + }) + + // ----------------------------------------------------- + + @opaque + @inlineOnce + @ghost + def lemmaAddToMapContainsAndNotInListThenInAcc[K, V](l: List[(K, V)], key: K, value: V, acc: ListMap[K, V]): Unit = { + require(noDuplicateKeys(l)) + require(!containsKey(l, key)) + require(addToMapMapFromBucket(l, acc).contains(key)) + + decreases(l.size) + l match { + case Cons((k, v), t) => + val newAcc = acc + (k, v) + assert(k != key) + lemmaAddToMapContainsAndNotInListThenInAcc(t, key, value, newAcc) + check(newAcc.contains(k)) + if(!acc.contains(key)){ + ListMapLemmas.addStillNotContains(acc, k, v, key) + check(false) + } + + case Nil() => () + } + + } ensuring (_ => acc.contains(key)) + + @opaque + @inlineOnce + @ghost + def lemmaExtractTailMapContainsThenExtractMapDoes[K, V](lm: ListLongMap[List[(K, V)]], key: K, hashF: Hashable[K], ordering: Ordering[K]): Unit = { + require(lm.toList.forall((k, v) => noDuplicateKeys(v))) + require(allKeysSameHashInMap(lm, hashF)) + require(!lm.toList.isEmpty) + require(extractMap(lm.tail.toList, ordering).contains(key)) + decreases(lm.toList.size) + val hash = hashF.hash(key) + + lm.toList match { + case Cons((k, v), tl) => + check(extractMap(lm.toList, ordering) == addToMapMapFromBucket(v, extractMap(lm.tail.toList, ordering))) + lemmaAddToMapMaintainsContains(lm.tail, key, v, hashF, ordering) + + case Nil() => () + } + + } ensuring (_ => extractMap(lm.toList, ordering).contains(key)) + + @opaque + @inlineOnce + @ghost + def lemmaAddToMapMaintainsContains[K, V](lm: ListLongMap[List[(K, V)]], key: K, l: List[(K, V)], hashF: Hashable[K], ordering: Ordering[K]): Unit = { + require(lm.toList.forall((k, v) => noDuplicateKeys(v))) + require(allKeysSameHashInMap(lm, hashF)) + require(noDuplicateKeys(l)) + require(extractMap(lm.toList, ordering).contains(key)) + + decreases(l.size) + val hash = hashF.hash(key) + + l match { + case Cons(hd, tl) => + lemmaAddToMapMaintainsContains(lm, key, tl, hashF, ordering) + check(addToMapMapFromBucket(tl, extractMap(lm.toList, ordering)).contains(key)) + lemmaAddToMapFromBucketPlusKeyValueIsCommutative(extractMap(lm.toList, ordering), hd._1, hd._2, tl) + + check(addToMapMapFromBucket(tl, extractMap(lm.toList, ordering) + hd) == addToMapMapFromBucket(tl, extractMap(lm.toList, ordering)) + hd) + ListMapLemmas.addStillContains(addToMapMapFromBucket(tl, extractMap(lm.toList, ordering)), hd._1, hd._2, key) + check(addToMapMapFromBucket(tl, extractMap(lm.toList, ordering) + hd).contains(key)) + + case Nil() => () + } + + } ensuring (_ => addToMapMapFromBucket(l, extractMap(lm.toList, ordering)).contains(key)) + + @opaque + @inlineOnce + @ghost + def lemmaAddToMapFromBucketMinusKeyIsCommutative[K, V](lhm: ListMap[K, V], key: K, l: List[(K, V)]): Unit = { + require(!containsKey(l, key)) + require(noDuplicateKeys(l)) + decreases(l) + + l match { + case Cons((k, v), t) => + val newAcc = lhm + (k, v) + ListMapLemmas.addRemoveCommutativeForDiffKeys(lhm, k, v, key) + lemmaAddToMapFromBucketMinusKeyIsCommutative(newAcc, key, t) + case Nil() => + check(addToMapMapFromBucket(l, (lhm - key)) == addToMapMapFromBucket(l, lhm) - key) + } + + } ensuring (_ => addToMapMapFromBucket(l, (lhm - key)) == addToMapMapFromBucket(l, lhm) - key) + + @opaque + @inlineOnce + @ghost + def lemmaAddToMapFromBucketPlusKeyValueIsCommutative[K, V](lhm: ListMap[K, V], key: K, value: V, l: List[(K, V)]): Unit = { + require(!containsKey(l, key)) + require(noDuplicateKeys(l)) + decreases(l) + + l match { + case Cons((k, v), t) => + val newAcc = lhm + (k, v) + ListMapLemmas.addCommutativeForDiffKeys(lhm, k, v, key, value) + lemmaAddToMapFromBucketPlusKeyValueIsCommutative(newAcc, key, value, t) + case Nil() => + check(addToMapMapFromBucket(l, (lhm + (key, value))) == addToMapMapFromBucket(l, lhm) + (key, value)) + } + + } ensuring (_ => addToMapMapFromBucket(l, (lhm + (key, value))) == addToMapMapFromBucket(l, lhm) + (key, value)) + + @opaque + @inlineOnce + @ghost + def lemmaChangeOneBucketToRemoveAKeyRemoveThisKeyInGenMapOneHashIsHead[K, V]( + lm: ListLongMap[List[(K, V)]], + hash: Long, + oldBucket: List[(K, V)], + newBucket: List[(K, V)], + key: K, + hashF: Hashable[K], + ordering: Ordering[K] + ): Unit = { + require(lm.toList.forall((k, v) => noDuplicateKeys(v))) + require(noDuplicateKeys(oldBucket)) + require(noDuplicateKeys(newBucket)) + require(removePairForKey(oldBucket, key) == newBucket) + require(allKeysSameHash(oldBucket, hash, hashF)) + require(extractMap(lm.toList, ordering).contains(key)) + require(hashF.hash(key) == hash) + require(allKeysSameHash(newBucket, hash, hashF)) + require(allKeysSameHashInMap(lm, hashF)) + require(noDuplicateKeys(newBucket)) + require(lm.toList.head == (hash, oldBucket)) + decreases(oldBucket.size) + + val l = lm.toList + l match + case Cons(h, t) => + check(t == lm.tail.toList) + check(extractMap(l, ordering) == addToMapMapFromBucket(h._2, extractMap(t, ordering))) + // check(extractMap(t, ordering) == ListMap.empty[K, V](ordering)) + oldBucket match { + case Cons(hd, tl) if hd._1 == key => + assert(oldBucket.tail == newBucket) + check(extractMap(Cons((hash, oldBucket.tail), t), ordering) == extractMap(Cons((hash, newBucket), t), ordering)) + check(addToMapMapFromBucket(oldBucket.tail, extractMap(t, ordering)) == addToMapMapFromBucket(newBucket, extractMap(t, ordering))) + + lemmaAddToMapFromBucketTlPlusHeadIsSameAsList(oldBucket.head, oldBucket.tail, extractMap(t, ordering)) + check(addToMapMapFromBucket(oldBucket.tail, extractMap(t, ordering)) + hd == addToMapMapFromBucket(oldBucket, extractMap(t, ordering))) + + check(!containsKey(oldBucket.tail, key)) + val m = addToMapMapFromBucket(oldBucket.tail, extractMap(t, ordering)) + check(m == extractMap(Cons((hash, oldBucket.tail), t), ordering)) + lemmaNotInItsHashBucketThenNotInMap(ListLongMap(Cons((hash, oldBucket.tail), t)), key, hashF, ordering) + check(!m.contains(key)) + ListMapLemmas.addThenRemoveForNewKeyIsSame(m, key, hd._2) + check((m + hd) - key == m) + check(extractMap(Cons((hash, oldBucket), t), ordering) - key == extractMap(Cons((hash, oldBucket.tail), t), ordering)) + check(extractMap(Cons((hash, newBucket), t), ordering) == extractMap(Cons((hash, oldBucket), t), ordering) - key) + case Cons(hd, tl) if hd._1 != key => + lemmaInGenMapThenGetPairDefined(lm, key, hashF, ordering) + lemmaGetPairDefinedThenContainsKey(oldBucket, key, hashF, ordering) + check(containsKey(oldBucket, key)) + check(containsKey(tl, key)) + check(removePairForKey(oldBucket, key) == newBucket) + check(removePairForKey(oldBucket.tail, key) == newBucket.tail) + check(removePairForKey(tl, key) == newBucket.tail) + + lemmaListContainsThenExtractedMapContains(ListLongMap(Cons((hash, tl), t)), key, hashF, ordering) + check(extractMap(Cons((hash, tl), t), ordering).contains(key)) + + lemmaAddToMapFromBucketTlPlusHeadIsSameAsList(oldBucket.head, oldBucket.tail, extractMap(t, ordering)) + lemmaAddToMapFromBucketTlPlusHeadIsSameAsList(newBucket.head, newBucket.tail, extractMap(t, ordering)) + check(extractMap(Cons((hash, oldBucket), t), ordering) == extractMap(Cons((hash, oldBucket.tail), t), ordering) + hd) + check(hd == newBucket.head) + check(extractMap(Cons((hash, oldBucket), t), ordering) == extractMap(Cons((hash, oldBucket.tail), t), ordering) + newBucket.head) + + check(extractMap(Cons((hash, tl), t), ordering).contains(key)) + lemmaChangeOneBucketToRemoveAKeyRemoveThisKeyInGenMapOneHashIsHead(ListLongMap(Cons((hash, tl), t)), hash, tl, newBucket.tail, key, hashF, ordering) + check(extractMap(Cons((hash, newBucket.tail), t), ordering) == extractMap(Cons((hash, tl), t), ordering) - key) + ListMapLemmas.addRemoveCommutativeForDiffKeys(extractMap(Cons((hash, tl), t), ordering), hd._1, hd._2, key) + check(extractMap(Cons((hash, newBucket), t), ordering) == extractMap(Cons((hash, oldBucket), t), ordering) - key) + case Nil() => + lemmaNotInItsHashBucketThenNotInMap(lm, key, hashF, ordering) + check(false) + } + + case Nil() => check(false) + + } ensuring (_ => { + extractMap(Cons((hash, newBucket), lm.toList.tail), ordering) == extractMap(Cons((hash, oldBucket), lm.toList.tail), ordering) - key + }) + + @opaque + @inlineOnce + @ghost + def lemmaChangeOneBucketToRemoveAKeyRemoveThisKeyInGenMapOneHash[K, V](hash: Long, oldBucket: List[(K, V)], newBucket: List[(K, V)], key: K, hashF: Hashable[K], ordering: Ordering[K]): Unit = { + require(noDuplicateKeys(oldBucket)) + require(noDuplicateKeys(newBucket)) + require(removePairForKey(oldBucket, key) == newBucket) + require(allKeysSameHash(oldBucket, hash, hashF)) + require(extractMap(List((hash, oldBucket)), ordering).contains(key)) + require(hashF.hash(key) == hash) + require(allKeysSameHash(newBucket, hash, hashF)) + require(noDuplicateKeys(newBucket)) + decreases(oldBucket.size) + + val l = List((hash, oldBucket)) + l match + case Cons(h, t) => + check(t.isEmpty) + check(extractMap(l, ordering) == addToMapMapFromBucket(h._2, extractMap(t, ordering))) + check(extractMap(t, ordering) == ListMap.empty[K, V](ordering)) + oldBucket match { + case Cons(hd, tl) if hd._1 == key => + assert(oldBucket.tail == newBucket) + check(extractMap(List((hash, oldBucket.tail)), ordering) == extractMap(List((hash, newBucket)), ordering)) + check(addToMapMapFromBucket(oldBucket.tail, ListMap.empty[K, V](ordering)) == addToMapMapFromBucket(newBucket, ListMap.empty[K, V](ordering))) + + lemmaAddToMapFromBucketTlPlusHeadIsSameAsList(oldBucket.head, oldBucket.tail, ListMap.empty[K, V](ordering)) + check(addToMapMapFromBucket(oldBucket.tail, ListMap.empty[K, V](ordering)) + hd == addToMapMapFromBucket(oldBucket, ListMap.empty[K, V](ordering))) + + check(!containsKey(oldBucket.tail, key)) + val m = addToMapMapFromBucket(oldBucket.tail, ListMap.empty[K, V](ordering)) + check(m == extractMap(List((hash, oldBucket.tail)), ordering)) + lemmaNotInItsHashBucketThenNotInMap(ListLongMap(List((hash, oldBucket.tail))), key, hashF, ordering) + check(!m.contains(key)) + ListMapLemmas.addThenRemoveForNewKeyIsSame(m, key, hd._2) + check((m + hd) - key == m) + check(extractMap(List((hash, oldBucket)), ordering) - key == extractMap(List((hash, newBucket)), ordering)) + + case Cons(hd, tl) if hd._1 != key => + lemmaInGenMapThenGetPairDefined(ListLongMap(List((hash, oldBucket))), key, hashF, ordering) + lemmaGetPairDefinedThenContainsKey(oldBucket, key, hashF, ordering) + check(containsKey(oldBucket, key)) + check(containsKey(tl, key)) + check(removePairForKey(oldBucket, key) == newBucket) + check(removePairForKey(oldBucket.tail, key) == newBucket.tail) + check(removePairForKey(tl, key) == newBucket.tail) + + lemmaListContainsThenExtractedMapContains(ListLongMap(List((hash, tl))), key, hashF, ordering) + check(extractMap(List((hash, tl)), ordering).contains(key)) + + lemmaAddToMapFromBucketTlPlusHeadIsSameAsList(oldBucket.head, oldBucket.tail, ListMap.empty[K, V](ordering)) + lemmaAddToMapFromBucketTlPlusHeadIsSameAsList(newBucket.head, newBucket.tail, ListMap.empty[K, V](ordering)) + check(extractMap(List((hash, oldBucket)), ordering) == extractMap(List((hash, oldBucket.tail)), ordering) + hd) + check(extractMap(List((hash, newBucket)), ordering) == extractMap(List((hash, newBucket.tail)), ordering) + newBucket.head) + + lemmaChangeOneBucketToRemoveAKeyRemoveThisKeyInGenMapOneHash(hash, tl, newBucket.tail, key, hashF, ordering) + check(extractMap(List((hash, newBucket.tail)), ordering) == extractMap(List((hash, tl)), ordering) - key) + ListMapLemmas.addRemoveCommutativeForDiffKeys(extractMap(List((hash, tl)), ordering), hd._1, hd._2, key) + check(extractMap(List((hash, newBucket)), ordering) == extractMap(List((hash, oldBucket)), ordering) - key) + case Nil() => check(false) + } + + case Nil() => check(false) + + } ensuring (_ => { + extractMap(List((hash, newBucket)), ordering) == extractMap(List((hash, oldBucket)), ordering) - key + }) + + @opaque + @inlineOnce + @ghost + def lemmaInLongMapAllKeySameHashThenForTuple[K, V](lml: List[(Long, List[(K, V)])], hash: Long, bucket: List[(K, V)], hashF: Hashable[K]): Unit = { + require(lml.forall((k, v) => allKeysSameHash(v, k, hashF))) + require(lml.contains((hash, bucket))) + decreases(lml.size) + + ListSpecs.forallContained(lml, (k, v) => allKeysSameHash(v, k, hashF), (hash, bucket)) + + } ensuring (_ => allKeysSameHash(bucket, hash, hashF)) + + @opaque + @inlineOnce + @ghost + def lemmaRemovePairForKeyPreservesHash[K, V](@induct l: List[(K, V)], key: K, hash: Long, hashF: Hashable[K]): Unit = { + require(noDuplicateKeys(l)) + require(allKeysSameHash(l, hash, hashF)) + + } ensuring (_ => allKeysSameHash(removePairForKey(l, key), hash, hashF)) + + @opaque + @inlineOnce + @ghost + def lemmaChangeOneBucketByAValidOnePreservesForallNoDuplicatesAndHash[K, V](lm: ListLongMap[List[(K, V)]], hash: Long, newBucket: List[(K, V)], hashF: Hashable[K], ordering: Ordering[K]): Unit = { + require(lm.toList.forall((k, v) => noDuplicateKeys(v))) + require(allKeysSameHashInMap(lm, hashF)) + require(noDuplicateKeys(newBucket)) + require(allKeysSameHash(newBucket, hash, hashF)) + decreases(lm.toList.size) + lm.toList match + case Cons(h, t) => { + lemmaChangeOneBucketByAValidOnePreservesForallNoDuplicatesAndHash(ListLongMap(t), hash, newBucket, hashF, ordering) + } + case Nil() => () + + } ensuring (_ => { + val newMap = old(lm) + (hash, newBucket) + newMap.toList.forall((k, v) => noDuplicateKeys(v)) && allKeysSameHashInMap(newMap, hashF) + }) + + @opaque + @inlineOnce + @ghost + def lemmaRemoveNotContainedDoesNotChange[K, V](lm: ListLongMap[List[(K, V)]], key: K, hashF: Hashable[K], ordering: Ordering[K]): Unit = { + require(lm.toList.forall((k, v) => noDuplicateKeys(v))) + require(allKeysSameHashInMap(lm, hashF)) + require(!extractMap(lm.toList, ordering).contains(key)) + + ListMapLemmas.removeNotPresentStillSame(extractMap(lm.toList, ordering), key) + + } ensuring (_ => extractMap(lm.toList, ordering) == extractMap(lm.toList, ordering) - key) + + @opaque + @inlineOnce + @ghost + def lemmaRemovePairForKeyPreservesNoDuplicateKeys[K, V](l: List[(K, V)], key: K): Unit = { + require(noDuplicateKeys(l)) + decreases(l) + + l match { + case Cons(hd, tl) if hd._1 == key => () + case Cons(hd, tl) => { + lemmaRemovePairForKeyPreservesNoDuplicateKeys(tl, key) + lemmaRemovePairForKeyPreservesNotContainsKey(tl, key, hd._1) + lemmaNotContainsKeyThenCannotContainPair(removePairForKey(tl, key), hd._1, hd._2) + } + case Nil() => () + } + + } ensuring (_ => noDuplicateKeys(removePairForKey(l, key))) + + @opaque + @inlineOnce + @ghost + def lemmaRemovePairForKeyPreservesNotContainsKey[K, V](@induct l: List[(K, V)], key: K, otherK: K): Unit = { + require(noDuplicateKeys(l)) + require(otherK != key) + require(!containsKey(l, otherK)) + + } ensuring (_ => !containsKey(removePairForKey(l, key), otherK)) + + @opaque + @inlineOnce + @ghost + def lemmaInGenMapThenGetPairDefined[K, V](lm: ListLongMap[List[(K, V)]], key: K, hashF: Hashable[K], ordering: Ordering[K]): Unit = { + require(lm.toList.forall((k, v) => noDuplicateKeys(v))) + require(allKeysSameHashInMap(lm, hashF)) + require(extractMap(lm.toList, ordering).contains(key)) + + decreases(lm.toList.size) + lemmaInGenMapThenLongMapContainsHash(lm, key, hashF, ordering) + ListLongMapLemmas.lemmaGetValueImpliesTupleContained(lm, hashF.hash(key), lm.apply(hashF.hash(key))) + ListSpecs.forallContained(lm.toList, (k, v) => noDuplicateKeys(v), (hashF.hash(key), lm.apply(hashF.hash(key)))) + + if (getPair(lm.apply(hashF.hash(key)), key).isEmpty) { + val l = lm.apply(hashF.hash(key)) + lemmaNotInItsHashBucketThenNotInMap(lm, key, hashF, ordering) + check(false) + } + check(getPair(lm.apply(hashF.hash(key)), key).isDefined) + + } ensuring (_ => { + lemmaInGenMapThenLongMapContainsHash(lm, key, hashF, ordering) + ListLongMapLemmas.lemmaGetValueImpliesTupleContained(lm, hashF.hash(key), lm.apply(hashF.hash(key))) + ListSpecs.forallContained(lm.toList, (k, v) => noDuplicateKeys(v), (hashF.hash(key), lm.apply(hashF.hash(key)))) + getPair(lm.apply(hashF.hash(key)), key).isDefined + }) + + @opaque + @inlineOnce + @ghost + def lemmaNotInItsHashBucketThenNotInMap[K, V](lm: ListLongMap[List[(K, V)]], key: K, hashF: Hashable[K], ordering: Ordering[K]): Unit = { + require(lm.toList.forall((k, v) => noDuplicateKeys(v))) + require(allKeysSameHashInMap(lm, hashF)) + require(lm.contains(hashF.hash(key))) + require(!containsKey(lm.apply(hashF.hash(key)), key)) + + decreases(lm.toList.size) + lm.toList match { + case Cons(hd, tl) if hd._1 == hashF.hash(key) => + lemmaHashNotInLongMapThenNotInGenerated(lm.tail, key, hashF, ordering) + lemmaAddToMapFromBucketContainsIIFInAccOrL(hd._2, extractMap(tl, ordering), key) + case Cons(hd, tl) => + assert(hd._1 != hashF.hash(key)) + lemmaNotSameHashThenCannotContainKey(lm, key, hd._1, hashF, ordering) + lemmaAddToMapFromBucketContainsIIFInAccOrL(hd._2, extractMap(tl, ordering), key) + lemmaNotInItsHashBucketThenNotInMap(lm.tail, key, hashF, ordering) + case Nil() => () + } + + } ensuring (_ => !extractMap(lm.toList, ordering).contains(key)) + + @opaque + @inlineOnce + @ghost + def lemmaInGenMapThenLongMapContainsHash[K, V](lm: ListLongMap[List[(K, V)]], key: K, hashF: Hashable[K], ordering: Ordering[K]): Unit = { + require(lm.toList.forall((k, v) => noDuplicateKeys(v))) + require(allKeysSameHashInMap(lm, hashF)) + require(extractMap(lm.toList, ordering).contains(key)) + decreases(lm.toList.size) + + val hash = hashF.hash(key) + + if (!lm.contains(hashF.hash(key))) { + lemmaHashNotInLongMapThenNotInGenerated(lm, key, hashF, ordering) + check(!extractMap(lm.toList, ordering).contains(key)) + check(false) + } + + } ensuring (_ => lm.contains(hashF.hash(key))) + + @opaque + @inlineOnce + @ghost + def lemmaHashNotInLongMapThenNotInGenerated[K, V](lm: ListLongMap[List[(K, V)]], key: K, hashF: Hashable[K], ordering: Ordering[K]): Unit = { + require(lm.toList.forall((k, v) => noDuplicateKeys(v))) + require(allKeysSameHashInMap(lm, hashF)) + require(!lm.contains(hashF.hash(key))) + decreases(lm.toList.size) + + lm.toList match { + case Cons(hd, tl) => + lemmaAddToMapFromBucketContainsIIFInAccOrL(hd._2, extractMap(tl, ordering), key) + lemmaNotSameHashThenCannotContainKey(lm, key, hd._1, hashF, ordering) + lemmaHashNotInLongMapThenNotInGenerated(lm.tail, key, hashF, ordering) + case Nil() => () + } + + } ensuring (_ => !extractMap(lm.toList, ordering).contains(key)) + + @opaque + @inlineOnce + @ghost + def lemmaAddToMapFromBucketContainsIIFInAccOrL[K, V](l: List[(K, V)], acc: ListMap[K, V], key: K): Unit = { + require(noDuplicateKeys(l)) + decreases(l) + l match { + case Nil() => () + case Cons((k, v), tl) => + val newAcc = acc + (k, v) + val res = addToMapMapFromBucket(tl, acc + (k, v)) + lemmaAddToMapFromBucketContainsIIFInAccOrL(tl, newAcc, key) + if (acc.contains(key)) { + ListMapLemmas.addStillContains(acc, k, v, key) + } else if (k == key) { + () + } else if (containsKey(tl, key)) { + () + } else { + lemmaAddToMapFromBucketContainsIIFInAccOrL(tl, newAcc, key) + ListMapLemmas.addStillNotContains(acc, k, v, key) + + } + } + } ensuring (_ => (addToMapMapFromBucket(l, acc).contains(key) == (containsKey(l, key) || acc.contains(key)))) + + @opaque + @inlineOnce + @ghost + def lemmaAddToMapFromBucketTlPlusHeadIsSameAsList[K, V](t: (K, V), l: List[(K, V)], acc: ListMap[K, V]): Unit = { + require(noDuplicateKeys(l)) + require(noDuplicateKeys(Cons(t, l))) + decreases(l) + l match { + case Nil() => () + case Cons((k, v), tl) => + val newAcc = acc + (k, v) + val res = addToMapMapFromBucket(tl, acc + (k, v)) + lemmaAddToMapFromBucketTlPlusHeadIsSameAsList(t, tl, newAcc) + + check(addToMapMapFromBucket(tl, newAcc) == addToMapMapFromBucket(l, acc)) + check(addToMapMapFromBucket(Cons(t, tl), newAcc) == addToMapMapFromBucket(tl, newAcc) + t) + check(addToMapMapFromBucket(Cons(t, tl), newAcc) == addToMapMapFromBucket(tl, newAcc + t)) + check(addToMapMapFromBucket(Cons(t, tl), newAcc) == addToMapMapFromBucket(tl, acc + (k, v) + t)) + check(addToMapMapFromBucket(Cons(t, l), acc) == addToMapMapFromBucket(l, acc + t)) + check(addToMapMapFromBucket(Cons(t, l), acc) == addToMapMapFromBucket(tl, acc + t + (k, v))) + ListMapLemmas.addCommutativeForDiffKeys(acc, k, v, t._1, t._2) + check(addToMapMapFromBucket(Cons(t, l), acc) == addToMapMapFromBucket(tl, acc + (k, v) + t)) + check(addToMapMapFromBucket(Cons(t, l), acc) == addToMapMapFromBucket(l, acc) + t) + + } + + } ensuring (_ => addToMapMapFromBucket(Cons(t, l), acc) == addToMapMapFromBucket(l, acc) + t) + + @opaque + @inlineOnce + @ghost + def lemmaExtractMapPreservesMapping[K, V](lm: ListLongMap[List[(K, V)]], key: K, value: V, hashF: Hashable[K], ordering: Ordering[K]): Unit = { + require(lm.toList.forall((k, v) => noDuplicateKeys(v))) + require(allKeysSameHashInMap(lm, hashF)) + require(extractMap(lm.toList, ordering).contains(key)) + require({ + lemmaInGenericMapThenInLongMap(lm, key, hashF, ordering) + lemmaInLongMapThenContainsKeyBiggerList(lm, key, hashF, ordering) + getValue(lm.toList, key) == value + }) + decreases(lm.toList.size) + + lm.toList match { + case Cons(hd, tl) => + if (containsKey(hd._2, key)) { + ListSpecs.forallContained(lm.toList, (k, v) => allKeysSameHash(v, k, hashF), (hd._1, hd._2)) + ListSpecs.forallContained(hd._2, p => hashF.hash(p._1) == hd._1, (key, value)) + lemmaHashNotInLongMapThenNotInGenerated(lm.tail, key, hashF, ordering) + lemmaAddToMapFromBucketMaintainsMapping(hd._2, extractMap(tl, ordering), key, value) + + } else { + check(!containsKey(hd._2, key)) + if (!lm.tail.contains(hashF.hash(key))) { + lemmaHashNotInLongMapThenNotInGenerated(lm.tail, key, hashF, ordering) + } + lemmaInLongMapThenContainsKeyBiggerList(lm, key, hashF, ordering) + lemmaListContainsThenExtractedMapContains(lm.tail, key, hashF, ordering) + lemmaExtractMapPreservesMapping(lm.tail, key, value, hashF, ordering) + lemmaAddToMapFromBucketMaintainsMapping(hd._2, extractMap(tl, ordering), key, value) + } + + case Nil() => () + } + + } ensuring (_ => extractMap(lm.toList, ordering).apply(key) == value) + + @opaque + @inlineOnce + @ghost + def lemmaAddToMapFromBucketMaintainsMapping[K, V](l: List[(K, V)], acc: ListMap[K, V], key: K, value: V): Unit = { + require(noDuplicateKeys(l)) + require(addToMapMapFromBucket(l, acc).contains(key)) + require(acc.contains(key) && acc.apply(key) == value && !containsKey(l, key) || containsKey(l, key) && l.contains((key, value)) && !acc.contains(key)) + decreases(l) + l match { + case Nil() => () + case Cons((k, v), tl) => + val newAcc = acc + (k, v) + val res = addToMapMapFromBucket(tl, acc + (k, v)) + + if (k == key) { + lemmaNotContainsKeyThenCannotContainPair(tl, key, value) + assert(v == value) + + lemmaAddToMapFromBucketMaintainsMapping(tl, newAcc, key, value) + } else if (acc.contains(key)) { + ListMapLemmas.addStillContains(acc, k, v, key) + ListMapLemmas.addApplyDifferent(acc, k, v, key) + lemmaNotContainsKeyThenCannotContainPair(l, key, value) + + lemmaAddToMapFromBucketMaintainsMapping(tl, newAcc, key, value) + } else { + assert(!acc.contains(key)) + assert(tl.contains((key, value))) + ListMapLemmas.addStillNotContains(acc, k, v, key) + lemmaAddToMapFromBucketMaintainsMapping(tl, newAcc, key, value) + + } + } + } ensuring (_ => addToMapMapFromBucket(l, acc).apply(key) == value) + + @opaque + @inlineOnce + @ghost + def lemmaInLongMapThenContainsKeyBiggerList[K, V](lm: ListLongMap[List[(K, V)]], key: K, hashF: Hashable[K], ordering: Ordering[K]): Unit = { + require(lm.toList.forall((k, v) => noDuplicateKeys(v))) + require(allKeysSameHashInMap(lm, hashF)) + require(lm.contains(hashF.hash(key))) + require({ + ListLongMapLemmas.lemmaGetValueImpliesTupleContained(lm, hashF.hash(key), lm.apply(hashF.hash(key))) + ListSpecs.forallContained(lm.toList, (k, v) => noDuplicateKeys(v), (hashF.hash(key), lm.apply(hashF.hash(key)))) + getPair(lm.apply(hashF.hash(key)), key).isDefined + }) + decreases(lm.toList.size) + lm.toList match + case Cons(hd, tl) if hd._1 == hashF.hash(key) => lemmaGetPairDefinedThenContainsKey(hd._2, key, hashF, ordering) + case Cons(hd, tl) => + assert(hashF.hash(key) != hd._1) + lemmaNotSameHashThenCannotContainKey(lm, key, hd._1, hashF, ordering) + assert(!containsKey(hd._2, key)) + lemmaInLongMapThenContainsKeyBiggerList(lm.tail, key, hashF, ordering) + check(containsKeyBiggerList(lm.toList, key)) + case Nil() => check(containsKeyBiggerList(lm.toList, key)) + + } ensuring (_ => containsKeyBiggerList(lm.toList, key)) + + @opaque + @inlineOnce + @ghost + def lemmaListContainsThenExtractedMapContains[K, V](lm: ListLongMap[List[(K, V)]], key: K, hashF: Hashable[K], ordering: Ordering[K]): Unit = { + require(lm.toList.forall((k, v) => noDuplicateKeys(v))) + require(allKeysSameHashInMap(lm, hashF)) + require(containsKeyBiggerList(lm.toList, key)) + decreases(lm.toList.size) + + lm.toList match + case Cons(hd, tl) if containsKey(hd._2, key) => { + val v = getValue(lm.toList, key) + lemmaInPairListHeadThenGetValueInTuple(lm, key, v, hashF, ordering) + check(hd._2.contains((key, v))) + check(extractMap(lm.toList, ordering) == addToMapMapFromBucket(hd._2, extractMap(tl, ordering))) + ListSpecs.forallContained(hd._2, p => addToMapMapFromBucket(hd._2, extractMap(tl, ordering)).contains(p._1), (key, v)) + } + case Cons(hd, tl) => { + assert(extractMap(lm.toList, ordering) == addToMapMapFromBucket(hd._2, extractMap(tl, ordering))) + lemmaInBiggerListButNotHeadThenTail(lm, key, hashF, ordering) + assert(containsKeyBiggerList(tl, key)) + lemmaListContainsThenExtractedMapContains(lm.tail, key, hashF, ordering) + assert(extractMap(tl, ordering).contains(key)) + val v = extractMap(tl, ordering).get(key).get + val m = extractMap(tl, ordering) + ListMapLemmas.lemmaGetValueImpliesTupleContained(extractMap(tl, ordering), key, v) + // lemmaGetValueInExtractMapToList(tl, key, v, hashF, ordering) + check(extractMap(tl, ordering).toList.contains((key, v))) + ListSpecs.forallContained(extractMap(tl, ordering).toList, p => extractMap(lm.toList, ordering).contains(p._1), (key, v)) + } + case Nil() => () + + } ensuring (_ => extractMap(lm.toList, ordering).contains(key)) + + @opaque + @inlineOnce + @ghost + def lemmaInPairListHeadThenGetValueInTuple[K, V](lm: ListLongMap[List[(K, V)]], key: K, v: V, hashF: Hashable[K], ordering: Ordering[K]): Unit = { + require(lm.toList.forall((k, v) => noDuplicateKeys(v))) + require(allKeysSameHashInMap(lm, hashF)) + require(containsKeyBiggerList(lm.toList, key) && containsKey(lm.toList.head._2, key)) + require(v == getValue(lm.toList, key)) + + } ensuring (_ => lm.toList.head._2.contains((key, v))) + + @opaque + @inlineOnce + @ghost + def lemmaGetValueEquivToGetPair[K, V](lm: ListLongMap[List[(K, V)]], key: K, v: V, hashF: Hashable[K], ordering: Ordering[K]): Unit = { + require(lm.toList.forall((k, v) => noDuplicateKeys(v))) + require(allKeysSameHashInMap(lm, hashF)) + require(containsKeyBiggerList(lm.toList, key)) + require({ + lemmaListContainsThenExtractedMapContains(lm, key, hashF, ordering) + lemmaInGenMapThenLongMapContainsHash(lm, key, hashF, ordering) + ListLongMapLemmas.lemmaGetValueImpliesTupleContained(lm, hashF.hash(key), lm.apply(hashF.hash(key))) + ListSpecs.forallContained(lm.toList, (k, v) => noDuplicateKeys(v), (hashF.hash(key), lm.apply(hashF.hash(key)))) + lemmaInGenMapThenGetPairDefined(lm, key, hashF, ordering) + getPair(lm.apply(hashF.hash(key)), key).get._2 == v + }) + decreases(lm.toList.size) + + lm.toList match { + case Cons(hd, tl) if hd._1 == hashF.hash(key) => + ListSpecs.forallContained(lm.toList, (k, v) => allKeysSameHash(v, k, hashF), (hd._1, hd._2)) + if (!containsKey(hd._2, key)) { + lemmaNotInItsHashBucketThenNotInMap(lm, key, hashF, ordering) + lemmaListContainsThenExtractedMapContains(lm, key, hashF, ordering) + check(false) + } + case Cons(hd, tl) => + lemmaNotSameHashThenCannotContainKey(lm, key, hd._1, hashF, ordering) + assert(!containsKey(hd._2, key)) + lemmaInBiggerListButNotHeadThenTail(lm, key, hashF, ordering) + lemmaGetValueEquivToGetPair(lm.tail, key, v, hashF, ordering) + case Nil() => () + } + + } ensuring (_ => getValue(lm.toList, key) == v) + @opaque + @inlineOnce + @ghost + def lemmaInBiggerListButNotHeadThenTail[K, V](lm: ListLongMap[List[(K, V)]], key: K, hashF: Hashable[K], ordering: Ordering[K]): Unit = { + require(lm.toList.forall((k, v) => noDuplicateKeys(v))) + require(allKeysSameHashInMap(lm, hashF)) + require(containsKeyBiggerList(lm.toList, key)) + require(!containsKey(lm.toList.head._2, key)) + decreases(lm.toList.size) + + } ensuring (_ => containsKeyBiggerList(lm.toList.tail, key)) + + @opaque + @inlineOnce + @ghost + def lemmaGetPairDefinedThenContainsKey[K, V](l: List[(K, V)], key: K, hashF: Hashable[K], ordering: Ordering[K]): Unit = { + require(noDuplicateKeys(l)) + require(getPair(l, key).isDefined) + decreases(l) + l match + case Cons(hd, tl) if hd._1 == key => () + case Cons(_, tl) => lemmaGetPairDefinedThenContainsKey(tl, key, hashF, ordering) + case Nil() => () + + } ensuring (_ => containsKey(l, key)) + + @opaque + @inlineOnce + @ghost + def lemmaNotSameHashThenCannotContainKey[K, V](lm: ListLongMap[List[(K, V)]], key: K, hash: Long, hashF: Hashable[K], ordering: Ordering[K]): Unit = { + require(lm.toList.forall((k, v) => noDuplicateKeys(v))) + require(allKeysSameHashInMap(lm, hashF)) + // require(lm.contains(hashF.hash(key))) + require(lm.contains(hash)) + require(hash != hashF.hash(key)) + + val listHash = lm.apply(hash) + + ListLongMapLemmas.lemmaGetValueImpliesTupleContained(lm, hash, listHash) + ListSpecs.forallContained(lm.toList, (k, v) => allKeysSameHash(v, k, hashF), (hash, listHash)) + assert(allKeysSameHash(listHash, hash, hashF)) + if (containsKey(listHash, key)) { + ListSpecs.forallContained(lm.toList, (k, v) => noDuplicateKeys(v), (hash, listHash)) + ListSpecs.forallContained(listHash, (k, v) => hashF.hash(k) == hash, (key, getPair(listHash, key).get._2)) + check(false) + } + } ensuring (_ => !containsKey(lm.apply(hash), key)) + + @opaque + @inlineOnce + @ghost + def lemmaNotContainsKeyThenCannotContainPair[K, V](l: List[(K, V)], key: K, v: V): Unit = { + require(!containsKey(l, key)) + decreases(l) + l match + case Cons(hd, tl) if hd._1 == key => check(false) + case Cons(_, tl) => lemmaNotContainsKeyThenCannotContainPair(tl, key, v) + case Nil() => () + + } ensuring (_ => !l.contains((key, v))) +} diff --git a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/ListUtils.scala b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/ListUtils.scala index ab2ae285..6cf1abce 100644 --- a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/ListUtils.scala +++ b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/ListUtils.scala @@ -12,6 +12,7 @@ import stainless.lang.StaticChecks._ object ListUtils { def isPrefix[B](prefix: List[B], l: List[B]): Boolean = { + decreases(prefix) (prefix, l) match { case (Nil(), _) => true case (_, Nil()) => false @@ -150,9 +151,9 @@ object ListUtils { @inlineOnce @opaque def lemmaNotHeadSoGetIndexTailIsMinusOne[B](l: List[B], e: B): Unit = { - decreases(l) require(l.contains(e)) - require(l.head != e) + require(l.head != e) + decreases(l) if (l.tail.head != e) { lemmaNotHeadSoGetIndexTailIsMinusOne(l.tail, e) @@ -173,6 +174,7 @@ object ListUtils { @inlineOnce @opaque def lemmaConcatTwoListThenFirstIsPrefix[B](l1: List[B], l2: List[B]): Unit = { + decreases(l1.size) l1 match { case Cons(hd, tl) => lemmaConcatTwoListThenFirstIsPrefix(tl, l2) case Nil() => () @@ -253,8 +255,8 @@ object ListUtils { @inlineOnce @opaque def lemmaPrefixStaysPrefixWhenAddingToSuffix[B](p: List[B], l: List[B], suffix: List[B]): Unit = { - decreases(p) require(isPrefix(p, l)) + decreases(p) p match { case Cons(hd, tl) => lemmaPrefixStaysPrefixWhenAddingToSuffix(tl, l.tail, suffix) case Nil() => () @@ -273,6 +275,7 @@ object ListUtils { require(isPrefix(p1, l)) require(isPrefix(p2, l)) require(p1.size == p2.size) + decreases(p1) p1 match { case Cons(hd, tl) => lemmaIsPrefixSameLengthThenSameList(tl, p2.tail, l.tail) @@ -419,6 +422,7 @@ object ListUtils { @inlineOnce @opaque def lemmaTailIsSubseqOfList[B](elmt: B, l: List[B]): Unit = { + l match { case Nil() => () case Cons(hd, tl) if hd == elmt => { @@ -495,6 +499,7 @@ object ListUtils { def lemmaConcatTwoListsWhichNotContainThenTotNotContain[B](l1: List[B], l2: List[B], b: B): Unit = { require(!l1.contains(b)) require(!l2.contains(b)) + decreases(l1) l1 match { case Cons(hd, tl) if hd == b => check(false) diff --git a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedRegex.scala b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedRegex.scala index 611d13ab..4af05a33 100644 --- a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedRegex.scala +++ b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedRegex.scala @@ -10,6 +10,8 @@ import stainless.annotation._ import stainless.proof._ import ch.epfl.chassot.MutableLongMap._ import ch.epfl.chassot.ListLongMap +import ch.epfl.chassot.ListMap +import ch.epfl.chassot.MutableHashMap._ import stainless.lang.StaticChecks._ trait IDGiver[C] { @@ -23,132 +25,72 @@ object Memoisation { import VerifiedRegex._ import VerifiedRegexMatcher._ - @ghost - @pure - def validIdToDerivatives[C](_idToRegexes: ListLongMap[List[Regex[C]]], _idToDerivatives: ListLongMap[List[(C, Regex[C])]], id: Long): Boolean = { - // require(_idToRegexes.contains(id)) - // require(_idToDerivatives.contains(id)) - if(!_idToRegexes.contains(id) || !_idToDerivatives.contains(id)){ - false - } else { - _idToRegexes.apply(id) match { - case Nil() => false - case Cons(hd, Nil()) => _idToDerivatives.apply(id).forall((c, reg) => validRegex(hd) && derivativeStep(hd, c) == reg) - case Cons(hd, tl) => true - } + @ghost def validCacheMap[C](m: HashMap[(Regex[C], C), Regex[C]]): Boolean = { + m.valid && m.forall { case ((r, c), res) => + validRegex(r) && res == derivativeStep(r, c) } } - final case class Cache[C](@ghost val ids: List[Long], idToRegexes: LongMap[List[Regex[C]]], idToDerivatives: LongMap[List[(C, Regex[C])]]) { - require(idToRegexes.valid) - require(idToDerivatives.valid) - - @ghost - @pure - def validContains: Boolean = - val idToRegMap = idToRegexes.map - ids.forall(i => idToRegMap.contains(i) && !idToRegMap.apply(i).isEmpty) && ids.forall(idToDerivatives.map.contains) - @ghost - @pure - def validContent: Boolean = - val idToRegMap = idToRegexes.map - val idToDerivMap = idToDerivatives.map - ids.forall(id => validIdToDerivatives(idToRegMap, idToDerivMap, id)) - - @ghost - @pure - def valid: Boolean = validContains && validContent - - private def getDerivativeFromList(l: List[(C, Regex[C])], a: C): Option[Regex[C]] = l match - case Cons(h, t) if h._1 == a => Some(h._2) - case Cons(_, t) => getDerivativeFromList(t, a) - case Nil() => None() - - /** Checks whether the regex is in the cache Meaning that it has been cached and no collision happened - * - * @param r - */ - @pure - def cacheContains(r: Regex[C])(implicit idGiver: IDGiver[C]): Boolean = { + @mutable + final case class Cache[C](private val cache: HashMap[(Regex[C], C), Regex[C]]) { + require(validCacheMap(cache)) + + @ghost def valid: Boolean = validCacheMap(cache) + + @ghost def lemmaIfInCacheThenValid(r: Regex[C], c: C): Unit = { + require(validCacheMap(cache)) require(validRegex(r)) - require(valid) - val id = hashId(r) - if (idToRegexes.contains(id)) { - idToRegexes.apply(id) match { - case Cons(hd, Nil()) => - // Good case: the regex is in the ids list, and no collision happened - hd == r - case Cons(hd, tl) => - // Collision happened, i.e., 2 different regexes got the same id, abort - false - case Nil() => - // The regex was never cached - false - } - } else { - // The regex was never cached - ghostExpr(if (ids.contains(id)) { - assert(validContent) - val idToRegMap = idToRegexes.map - ListSpecs.forallContained(ids, i => idToRegMap.contains(i) && !idToRegMap.apply(i).isEmpty, id) - assert(idToRegexes.map.contains(id)) - - }) - assert(!ids.contains(id)) - false + if (cache.contains((r, c))) { + lemmaForallThenForAPair( + cache, + { case ((r, c), res) => + validRegex(r) && res == derivativeStep(r, c) + }, + (r, c), + cache((r, c)) + ) } - } ensuring(res => !res || ids.contains(hashId(r))) + } ensuring (_ => cache.contains((r, c)) ==> (derivativeStep(r, c) == cache((r, c)))) + + def contains(r: Regex[C], c: C): Boolean = { + require(validCacheMap(cache)) + cache.contains((r, c)) + } - @pure - def getDerivative(r: Regex[C], a: C)(implicit idGiver: IDGiver[C]): Option[Regex[C]] = { + def get(r: Regex[C], c: C): Option[Regex[C]] = { require(validRegex(r)) - require(valid) - require(cacheContains(r)) - val id = hashId(r) - val res = getDerivativeFromList(idToDerivatives.apply(id), a) - assert(valid) - if(res.isEmpty){ - ghostExpr({check(valid && (res.isEmpty || res.get == derivativeStep(r, a)))}) - } else{ - assert(valid) - ghostExpr({ - assert(cacheContains(r)) - assert(validContent) - val idToRegMap = idToRegexes.map - val idToDerivMap = idToDerivatives.map - ListSpecs.forallContained(ids, id => idToDerivMap.contains(id), id) - ListSpecs.forallContained(ids, i => idToRegMap.contains(i) && !idToRegMap.apply(i).isEmpty, id) - - assert(idToRegMap.contains(id)) - assert(idToDerivMap.contains(id)) - assert(idToRegexes.contains(id)) - assert(ids.forall(id => validIdToDerivatives(idToRegMap, idToDerivMap, id))) - ListSpecs.forallContained(ids, id => validIdToDerivatives(idToRegMap, idToDerivMap, id), id) - assert(ids.contains(id)) - assert(valid) - check(validIdToDerivatives(idToRegexes.map, idToDerivatives.map, id)) - idToRegexes.apply(id) match { - case Nil() => check(false) - case Cons(hd, Nil()) => - check(idToDerivMap.apply(id).forall((c, reg) => validRegex(hd) && derivativeStep(hd, c) == reg)) - check(r == hd) - assert(r == hd) - check(idToDerivMap.apply(id).forall((c, reg) => validRegex(r) && derivativeStep(r, c) == reg)) - case Cons(hd, tl) => - // cacheContains(r) is false in this case - check(false) - } - check(idToDerivMap.apply(id).forall((c, reg) => validRegex(r) && derivativeStep(r, c) == reg)) - ListSpecs.forallContained(idToDerivMap.apply(id), (c, reg) => validRegex(r) && derivativeStep(r, c) == reg, (a, res.get)) - }) - assert(res.get == derivativeStep(r, a)) - ghostExpr({check(valid && (res.isEmpty || res.get == derivativeStep(r, a)))}) + require(validCacheMap(cache)) + + if (cache.contains((r, c))) { + // ghostExpr(lemmaIfInCacheThenValid(r, c)) + Some(cache((r, c))) + } else { + None() } - res + } ensuring (res => res.isEmpty || res.get == derivativeStep(r, c)) + + def update(r: Regex[C], c: C, res: Regex[C]): Unit = { + require(validCacheMap(cache)) + require(validRegex(r)) + require(res == derivativeStep(r, c)) + + // ghostExpr( + // lemmaUpdateValidPairMaintainsForall( + // cache, + // { case ((r, c), res) => + // validRegex(r) && res == derivativeStep(r, c) + // }, + // (r, c), + // res + // ) + // ) + val _ = cache.update((r, c), res) + + } ensuring (_ => validCacheMap(this.cache)) - } ensuring (res => valid && (res.isEmpty || res.get == derivativeStep(r, a))) } } + object VerifiedRegex { abstract sealed class Regex[C] {} @@ -326,14 +268,31 @@ object VerifiedRegexMatcher { res } ensuring (res => validRegex(res)) - // def derivativeStepMem[C](r: Regex[C], a: C)(cache: Cache[C]): Regex[C] = { - // require(validRegex(r)) - // require(cache.valid) - // decreases(r) - // val id = hashId(r) + def derivativeStepMem[C](r: Regex[C], a: C)(cache: Cache[C]): Regex[C] = { + require(validRegex(r)) + require(cache.valid) + decreases(r) + + cache.get(r, a) match { + case Some(res) => res + case None() => { + val res: Regex[C] = r match { + case EmptyExpr() => EmptyLang() + case EmptyLang() => EmptyLang() + case ElementMatch(c) => if (a == c) EmptyExpr() else EmptyLang() + case Union(rOne, rTwo) => Union(derivativeStepMem(rOne, a)(cache), derivativeStepMem(rTwo, a)(cache)) + case Star(rInner) => Concat(derivativeStepMem(rInner, a)(cache), Star(rInner)) + case Concat(rOne, rTwo) => { + if (nullable(rOne)) Union(Concat(derivativeStepMem(rOne, a)(cache), rTwo), derivativeStepMem(rTwo, a)(cache)) + else Union(Concat(derivativeStepMem(rOne, a)(cache), rTwo), EmptyLang()) + } + } + cache.update(r, a, res) + res + } + } - // res - // } ensuring (res => res == derivativeStep(r, a)) + } ensuring (res => res == derivativeStep(r, a)) def derivative[C](r: Regex[C], input: List[C]): Regex[C] = { require(validRegex(r)) From f0efe2ba5b3d9dcef0176116552b0dd69d4f2d0b Mon Sep 17 00:00:00 2001 From: Samuel Chassot Date: Thu, 7 Mar 2024 13:59:22 +0100 Subject: [PATCH 10/78] working --- .../ch/epfl/chassot/MutableHashMap.scala | 86 ++++++++++++++----- .../scala/ch/epfl/lexer/VerifiedRegex.scala | 22 ++--- 2 files changed, 75 insertions(+), 33 deletions(-) diff --git a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/chassot/MutableHashMap.scala b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/chassot/MutableHashMap.scala index c384f0ab..639de4b3 100644 --- a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/chassot/MutableHashMap.scala +++ b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/chassot/MutableHashMap.scala @@ -32,7 +32,6 @@ object MutableHashMap { HashMap(Cell(MutableLongMap.getEmptyLongMap[List[(K, V)]]((l: Long) => Nil[(K, V)](), initialSize)), hashF, 0, defaultValue, ordering) } ensuring (res => res.valid && res.size == 0) - @mutable final case class HashMap[K, V]( val underlying: Cell[LongMap[List[(K, V)]]], @@ -224,11 +223,17 @@ object MutableHashMap { @pure @ghost - private def map: ListMap[K, V] = { + def map: ListMap[K, V] = { require(valid) extractMap(underlying.v.map.toList, ordering) } + @ghost + def forall(p: ((K, V)) => Boolean): Boolean = { + require(valid) + map.forall(p) + } + } @ghost def extractMap[K, V](l: List[(Long, List[(K, V)])], ordering: Ordering[K]): ListMap[K, V] = { @@ -538,10 +543,10 @@ object MutableHashMap { check((lm + (hash, newBucket)).toList.forall((k, v) => noDuplicateKeys(v))) lm.toList match { case Cons(hd, tl) if hd._1 == hash => - assert(lm.contains(hash)) - check((lm + (hash, newBucket)).toList.head == (hash, newBucket)) - check((lm + (hash, newBucket)).toList.tail == tl) - check(extractMap((lm + (hash, newBucket)).toList, ordering) == addToMapMapFromBucket(newBucket, extractMap(tl, ordering))) + assert(lm.contains(hash)) + check((lm + (hash, newBucket)).toList.head == (hash, newBucket)) + check((lm + (hash, newBucket)).toList.tail == tl) + check(extractMap((lm + (hash, newBucket)).toList, ordering) == addToMapMapFromBucket(newBucket, extractMap(tl, ordering))) check(newBucket == Cons((key, newValue), lm.apply(hash))) val newAcc = extractMap(tl, ordering) + (key, newValue) @@ -564,7 +569,7 @@ object MutableHashMap { check(!containsKey(hd._2, key)) lemmaAddToMapFromBucketPlusKeyValueIsCommutative(extractMap(lm.tail.toList, ordering), key, newValue, hd._2) - case Nil() => () + case Nil() => () } } ensuring (_ => { @@ -603,17 +608,27 @@ object MutableHashMap { check((lm + (hash, newBucket)).toList.forall((k, v) => noDuplicateKeys(v))) lm.toList match { - case Cons(hd, tl) if hd._1 == hash => + case Cons(hd, tl) if hd._1 == hash => check(lm + (hash, newBucket) == ListLongMap(Cons((hash, newBucket), tl))) check(extractMap((lm + (hash, newBucket)).toList, ordering) == addToMapMapFromBucket(newBucket, extractMap(tl, ordering))) check(extractMap((lm + (hash, newBucket)).toList, ordering) == addToMapMapFromBucket(Cons((key, newValue), removePairForKey(lm.apply(hash), key)), extractMap(tl, ordering))) - check(addToMapMapFromBucket(Cons((key, newValue), removePairForKey(lm.apply(hash), key)), extractMap(tl, ordering)) == addToMapMapFromBucket(removePairForKey(lm.apply(hash), key), extractMap(tl, ordering) + (key, newValue))) + check( + addToMapMapFromBucket(Cons((key, newValue), removePairForKey(lm.apply(hash), key)), extractMap(tl, ordering)) == addToMapMapFromBucket( + removePairForKey(lm.apply(hash), key), + extractMap(tl, ordering) + (key, newValue) + ) + ) lemmaAddToMapFromBucketPlusKeyValueIsCommutative(extractMap(tl, ordering), key, newValue, removePairForKey(lm.apply(hash), key)) - check(addToMapMapFromBucket(Cons((key, newValue), removePairForKey(lm.apply(hash), key)), extractMap(tl, ordering)) == addToMapMapFromBucket(removePairForKey(lm.apply(hash), key), extractMap(tl, ordering)) + (key, newValue)) + check( + addToMapMapFromBucket(Cons((key, newValue), removePairForKey(lm.apply(hash), key)), extractMap(tl, ordering)) == addToMapMapFromBucket( + removePairForKey(lm.apply(hash), key), + extractMap(tl, ordering) + ) + (key, newValue) + ) val intermediateLm = lm + (hash, newBucket.tail) check(newBucket.tail == removePairForKey(lm.apply(hash), key)) - check( addToMapMapFromBucket(removePairForKey(lm.apply(hash), key), extractMap(tl, ordering)) == extractMap(intermediateLm.toList, ordering)) + check(addToMapMapFromBucket(removePairForKey(lm.apply(hash), key), extractMap(tl, ordering)) == extractMap(intermediateLm.toList, ordering)) lemmaChangeOneBucketToRemoveAKeyRemoveThisKeyInGenMap(lm, hash, newBucket.tail, key, hashF, ordering) check(extractMap(intermediateLm.toList, ordering) == extractMap(lm.toList, ordering) - key) @@ -625,7 +640,7 @@ object MutableHashMap { ListMapLemmas.removeThenAddForSameKeyIsSameAsAdd(extractMap(lm.toList, ordering), key, newValue) check(extractMap((lm + (hash, newBucket)).toList, ordering) == extractMap(lm.toList, ordering) + (key, newValue)) - case Cons(hd, tl) if hd._1 != hash => + case Cons(hd, tl) if hd._1 != hash => lemmaNotSameHashThenCannotContainKey(lm, key, hd._1, hashF, ordering) check(!containsKey(hd._2, key)) @@ -648,11 +663,38 @@ object MutableHashMap { extractMap((lm + (hash, newBucket)).toList, ordering) == extractMap(lm.toList, ordering) + (key, newValue) }) + @opaque + @inlineOnce + @ghost + def lemmaForallThenForAPair[K, V](hashMap: HashMap[K, V], p: ((K, V)) => Boolean, k: K, v: V): Unit = { + require(hashMap.valid) + require(hashMap.forall(p)) + require(hashMap.contains(k)) + require(hashMap.apply(k) == v) + + ListMapLemmas.lemmaGetValueImpliesTupleContained(hashMap.map, k, v) + + check(hashMap.map.toList.contains((k, v))) + ListSpecs.forallContained(hashMap.map.toList, p, (k, v)) + check(p((k, v))) + + } ensuring (_ => p((k, v))) + + @opaque + @inlineOnce + @ghost + def lemmaUpdateValidPairMaintainsForall[K, V](hashMap: HashMap[K, V], p: ((K, V)) => Boolean, k: K, v: V): Unit = { + require(hashMap.valid) + require(hashMap.forall(p)) + require(p((k, v))) + + } ensuring (_ => (hashMap.map + (k, v)).forall(p)) + // ----------------------------------------------------- @opaque @inlineOnce - @ghost + @ghost def lemmaAddToMapContainsAndNotInListThenInAcc[K, V](l: List[(K, V)], key: K, value: V, acc: ListMap[K, V]): Unit = { require(noDuplicateKeys(l)) require(!containsKey(l, key)) @@ -660,15 +702,15 @@ object MutableHashMap { decreases(l.size) l match { - case Cons((k, v), t) => - val newAcc = acc + (k, v) - assert(k != key) - lemmaAddToMapContainsAndNotInListThenInAcc(t, key, value, newAcc) - check(newAcc.contains(k)) - if(!acc.contains(key)){ - ListMapLemmas.addStillNotContains(acc, k, v, key) - check(false) - } + case Cons((k, v), t) => + val newAcc = acc + (k, v) + assert(k != key) + lemmaAddToMapContainsAndNotInListThenInAcc(t, key, value, newAcc) + check(newAcc.contains(k)) + if (!acc.contains(key)) { + ListMapLemmas.addStillNotContains(acc, k, v, key) + check(false) + } case Nil() => () } diff --git a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedRegex.scala b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedRegex.scala index 4af05a33..a8658f23 100644 --- a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedRegex.scala +++ b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedRegex.scala @@ -62,7 +62,7 @@ object Memoisation { require(validCacheMap(cache)) if (cache.contains((r, c))) { - // ghostExpr(lemmaIfInCacheThenValid(r, c)) + ghostExpr(lemmaIfInCacheThenValid(r, c)) Some(cache((r, c))) } else { None() @@ -74,16 +74,16 @@ object Memoisation { require(validRegex(r)) require(res == derivativeStep(r, c)) - // ghostExpr( - // lemmaUpdateValidPairMaintainsForall( - // cache, - // { case ((r, c), res) => - // validRegex(r) && res == derivativeStep(r, c) - // }, - // (r, c), - // res - // ) - // ) + ghostExpr( + lemmaUpdateValidPairMaintainsForall( + cache, + { case ((r, c), res) => + validRegex(r) && res == derivativeStep(r, c) + }, + (r, c), + res + ) + ) val _ = cache.update((r, c), res) } ensuring (_ => validCacheMap(this.cache)) From 6714b0bcf9a5245257345d6a99bba808902fd4ef Mon Sep 17 00:00:00 2001 From: Samuel Chassot Date: Thu, 7 Mar 2024 17:47:14 +0100 Subject: [PATCH 11/78] cached version proven --- .../main/scala/ch/epfl/benchmark/Utils.scala | 16 +++ .../main/scala/ch/epfl/chassot/Hashable.scala | 10 ++ .../main/scala/ch/epfl/chassot/ListMap.scala | 21 +--- .../ch/epfl/chassot/MutableHashMap.scala | 5 +- .../main/scala/ch/epfl/chassot/Ordering.scala | 27 +++++ .../scala/ch/epfl/lexer/VerifiedRegex.scala | 103 ++++++++++++++---- lexers/regex/verifiedlexer/stainless.conf | 2 +- lexers/regex/verifiedlexer/verify.sh | 2 +- 8 files changed, 136 insertions(+), 50 deletions(-) create mode 100644 lexers/regex/verifiedlexer/src/main/scala/ch/epfl/benchmark/Utils.scala create mode 100644 lexers/regex/verifiedlexer/src/main/scala/ch/epfl/chassot/Hashable.scala create mode 100644 lexers/regex/verifiedlexer/src/main/scala/ch/epfl/chassot/Ordering.scala diff --git a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/benchmark/Utils.scala b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/benchmark/Utils.scala new file mode 100644 index 00000000..00d7b7f9 --- /dev/null +++ b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/benchmark/Utils.scala @@ -0,0 +1,16 @@ +/** Author: Samuel Chassot + */ + +package ch.epfl.benchmark + +import stainless.annotation._ +import stainless.lang._ +import stainless.proof.check +import stainless.lang.StaticChecks.* +import stainless.collection._ + +import ch.epfl.chassot.Hashable +import ch.epfl.chassot.Ordering +import ch.epfl.lexer.VerifiedRegex._ + + diff --git a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/chassot/Hashable.scala b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/chassot/Hashable.scala new file mode 100644 index 00000000..f0284dbd --- /dev/null +++ b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/chassot/Hashable.scala @@ -0,0 +1,10 @@ +/** Author: Samuel Chassot +*/ +package ch.epfl.chassot + +import stainless.annotation._ + +trait Hashable[K] { + @pure + def hash(k: K): Long +} \ No newline at end of file diff --git a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/chassot/ListMap.scala b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/chassot/ListMap.scala index a889ee70..a9b4a31f 100644 --- a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/chassot/ListMap.scala +++ b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/chassot/ListMap.scala @@ -10,30 +10,11 @@ import stainless.lang._ import stainless.proof.check import scala.annotation.tailrec import scala.collection.immutable +import ch.epfl.chassot.Ordering // Uncomment the following import to run benchmarks // import OptimisedChecks.* -trait Ordering[T]: - def compare(x: T, y: T): Int - - @law def inverse(x: T, y: T): Boolean = - sign(compare(x, y)) == -sign(compare(y, x)) - - @law def transitive(x: T, y: T, z: T): Boolean = - if (compare(x, y) > 0 && compare(y, z) > 0) then compare(x, z) > 0 else if (compare(x, y) < 0 && compare(y, z) < 0) then compare(x, z) < 0 else true - - @law def consistent(x: T, y: T, z: T): Boolean = - if compare(x, y) == 0 then sign(compare(x, z)) == sign(compare(y, z)) else true - - @law def equalsMeansEquals(x: T, y: T): Boolean = - (compare(x, y) == 0) == (x == y) - - final def sign(x: Int): BigInt = - if x < 0 then -1 else if x > 0 then 1 else 0 - -end Ordering - case class ListMap[K, B](toList: List[(K, B)], ordd: Ordering[K]) { require(TupleListOpsGenK.isStrictlySorted(toList)(ordd)) diff --git a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/chassot/MutableHashMap.scala b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/chassot/MutableHashMap.scala index 639de4b3..5e3acd98 100644 --- a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/chassot/MutableHashMap.scala +++ b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/chassot/MutableHashMap.scala @@ -11,14 +11,11 @@ import scala.annotation.tailrec import stainless.lang.Cell import MutableLongMap._ import LongMapFixedSize.validMask +import ch.epfl.chassot.Hashable import stainless.lang.StaticChecks.* // Comment out when using the OptimisedEnsuring object below // import OptimisedChecks.* // Import to remove `ensuring` and `require` from the code for the benchmarks -trait Hashable[K] { - @pure - def hash(k: K): Long -} object MutableHashMap { diff --git a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/chassot/Ordering.scala b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/chassot/Ordering.scala new file mode 100644 index 00000000..adbd7149 --- /dev/null +++ b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/chassot/Ordering.scala @@ -0,0 +1,27 @@ +/** Author: Samuel Chassot +*/ + +package ch.epfl.chassot + +import stainless.annotation._ + + +trait Ordering[T]: + def compare(x: T, y: T): Int + + @law def inverse(x: T, y: T): Boolean = + sign(compare(x, y)) == -sign(compare(y, x)) + + @law def transitive(x: T, y: T, z: T): Boolean = + if (compare(x, y) > 0 && compare(y, z) > 0) then compare(x, z) > 0 else if (compare(x, y) < 0 && compare(y, z) < 0) then compare(x, z) < 0 else true + + @law def consistent(x: T, y: T, z: T): Boolean = + if compare(x, y) == 0 then sign(compare(x, z)) == sign(compare(y, z)) else true + + @law def equalsMeansEquals(x: T, y: T): Boolean = + (compare(x, y) == 0) == (x == y) + + final def sign(x: Int): BigInt = + if x < 0 then -1 else if x > 0 then 1 else 0 + +end Ordering \ No newline at end of file diff --git a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedRegex.scala b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedRegex.scala index a8658f23..95c530c1 100644 --- a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedRegex.scala +++ b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedRegex.scala @@ -12,14 +12,53 @@ import ch.epfl.chassot.MutableLongMap._ import ch.epfl.chassot.ListLongMap import ch.epfl.chassot.ListMap import ch.epfl.chassot.MutableHashMap._ +import ch.epfl.chassot.Ordering +import ch.epfl.chassot.Hashable import stainless.lang.StaticChecks._ -trait IDGiver[C] { - def id(c: C): Long - val MAX_ID = Int.MaxValue - @law def smallEnough(c: C): Boolean = id(c) >= 0 && id(c) <= MAX_ID - @law def uniqueness(c1: C, c2: C): Boolean = if (id(c1) == id(c2)) then c1 == c2 else true -} +// object CharRegexUtils { +// import VerifiedRegex._ +// case class CharOrdering() extends Ordering[Char] { +// override def compare(x: Char, y: Char): Int = { +// if (x < y) -1 +// else if (x > y) 1 +// else 0 +// } +// override def inverse(x: Char, y: Char): Boolean = { +// sign(compare(x, y)) == -sign(compare(y, x)) +// } +// override def transitive(x: Char, y: Char, z: Char): Boolean = { +// if (compare(x, y) > 0 && compare(y, z) > 0) compare(x, z) > 0 +// else if (compare(x, y) < 0 && compare(y, z) < 0) compare(x, z) < 0 +// else true +// } +// override def consistent(x: Char, y: Char, z: Char): Boolean = { +// if (compare(x, y) == 0) sign(compare(x, z)) == sign(compare(y, z)) +// else true +// } +// override def equalsMeansEquals(x: Char, y: Char): Boolean = { +// (compare(x, y) == 0) == (x == y) +// } +// } + +// case class CharRegexOrdering() extends Ordering[Regex[Char]] { +// override def compare(x: Regex[Char], y: Regex[Char]): Int = { +// 0 +// } +// } + +// case class CharRegexTuplOrdering() extends Ordering[(Regex[Char], Char)] { +// override def compare(x: (Regex[Char], Char), y: (Regex[Char], Char)): Int = { +// val r1 = x._1 +// val r2 = y._1 +// val char1 = x._2 +// val char2 = y._2 +// 0 + +// } +// } + +// } object Memoisation { import VerifiedRegex._ @@ -122,24 +161,6 @@ object VerifiedRegex { }) ) - def hashId[C](r: Regex[C])(implicit idC: IDGiver[C]): Long = { - decreases(r) - r match { - case ElementMatch(c) => - 2L * idC.id(c) - case Star(r) => - 3L * hashId(r) - case Union(rOne, rTwo) => - 5L * hashId(rOne) + 5L * hashId(rTwo) - case Concat(rOne, rTwo) => - 7L * hashId(rOne) + 7L * hashId(rTwo) - case EmptyExpr() => - 11L - case EmptyLang() => - 13L - } - } - case class ElementMatch[C](c: C) extends Regex[C] case class Star[C](reg: Regex[C]) extends Regex[C] case class Union[C](regOne: Regex[C], regTwo: Regex[C]) extends Regex[C] @@ -187,6 +208,24 @@ object VerifiedRegex { } } + def hash[C](r: Regex[C])(implicit hashC: Hashable[C]): Long = { + // decreases(r) + r match { + case ElementMatch(c) => + 2L * hashC.hash(c) + case Star(r) => + 3L * hash(r) + case Union(rOne, rTwo) => + 5L * hash(rOne) + 5L * hash(rTwo) + case Concat(rOne, rTwo) => + 7L * hash(rOne) + 7L * hash(rTwo) + case EmptyExpr() => + 11L + case EmptyLang() => + 13L + } + } + @inline def isEmptyExpr[C](r: Regex[C]): Boolean = { r match { @@ -302,6 +341,15 @@ object VerifiedRegexMatcher { } } ensuring (res => validRegex(res)) + def derivativeMem[C](r: Regex[C], input: List[C])(cache: Cache[C]): Regex[C] = { + require(validRegex(r)) + require(cache.valid) + input match { + case Cons(hd, tl) => derivative(derivativeStepMem(r, hd)(cache: Cache[C]), tl) + case Nil() => r + } + } ensuring (res => validRegex(res) && res == derivative(r, input)) + def matchR[C](r: Regex[C], input: List[C]): Boolean = { require(validRegex(r)) decreases(input.size) @@ -316,6 +364,13 @@ object VerifiedRegexMatcher { } ) + def matchRCache[C](r: Regex[C], input: List[C])(cache: Cache[C]): Boolean = { + require(validRegex(r)) + require(cache.valid) + decreases(input.size) + if (input.isEmpty) nullable(r) else matchR(derivativeStepMem(r, input.head)(cache: Cache[C]), input.tail) + } ensuring (res => res == matchR(r, input)) + def matchRSpec[C](r: Regex[C], s: List[C]): Boolean = { require(validRegex(r)) decreases(s.size + regexDepth(r)) diff --git a/lexers/regex/verifiedlexer/stainless.conf b/lexers/regex/verifiedlexer/stainless.conf index 286d55ce..5a3ff625 100644 --- a/lexers/regex/verifiedlexer/stainless.conf +++ b/lexers/regex/verifiedlexer/stainless.conf @@ -3,7 +3,7 @@ vc-cache = true # debug = ["verification", "smt"] -timeout = 10 +timeout = 30 check-models = false print-ids = false print-types = false diff --git a/lexers/regex/verifiedlexer/verify.sh b/lexers/regex/verifiedlexer/verify.sh index 9bfe91bd..be625159 100755 --- a/lexers/regex/verifiedlexer/verify.sh +++ b/lexers/regex/verifiedlexer/verify.sh @@ -1 +1 @@ -stainless-dotty --config-file=stainless.conf --watch src/main/scala/ch/epfl/lexer/VerifiedRegex.scala src/main/scala/ch/epfl/lexer/ListUtils.scala src/main/scala/ch/epfl/chassot/* $1 \ No newline at end of file +stainless-dotty --config-file=stainless.conf --watch -D-parallel=12 src/main/scala/ch/epfl/lexer/VerifiedRegex.scala src/main/scala/ch/epfl/lexer/ListUtils.scala src/main/scala/ch/epfl/chassot/* $1 \ No newline at end of file From d4b34df603dca7c5611bc5184c2b4780aa94807b Mon Sep 17 00:00:00 2001 From: Samuel Chassot Date: Fri, 8 Mar 2024 17:49:20 +0100 Subject: [PATCH 12/78] working examples --- .../verifiedlexer/src/main/scala/Main.scala | 5 - .../ch/epfl/benchmark/LexerBenchmark.scala | 3 + .../main/scala/ch/epfl/benchmark/Utils.scala | 11 ++ .../src/main/scala/ch/epfl/lexer/Main.scala | 98 +++++++++++ .../scala/ch/epfl/lexer/VerifiedLexer.scala | 2 +- .../scala/ch/epfl/lexer/VerifiedRegex.scala | 163 ++++++++++++------ 6 files changed, 219 insertions(+), 63 deletions(-) delete mode 100644 lexers/regex/verifiedlexer/src/main/scala/Main.scala create mode 100644 lexers/regex/verifiedlexer/src/main/scala/ch/epfl/benchmark/LexerBenchmark.scala create mode 100644 lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/Main.scala diff --git a/lexers/regex/verifiedlexer/src/main/scala/Main.scala b/lexers/regex/verifiedlexer/src/main/scala/Main.scala deleted file mode 100644 index bd01e94b..00000000 --- a/lexers/regex/verifiedlexer/src/main/scala/Main.scala +++ /dev/null @@ -1,5 +0,0 @@ -@main def hello(): Unit = - println("Hello world!") - println(msg) - -def msg = "I was compiled by Scala 3. :)" diff --git a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/benchmark/LexerBenchmark.scala b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/benchmark/LexerBenchmark.scala new file mode 100644 index 00000000..67167205 --- /dev/null +++ b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/benchmark/LexerBenchmark.scala @@ -0,0 +1,3 @@ +object LexerBenchmark { + +} diff --git a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/benchmark/Utils.scala b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/benchmark/Utils.scala index 00d7b7f9..5a4ec992 100644 --- a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/benchmark/Utils.scala +++ b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/benchmark/Utils.scala @@ -14,3 +14,14 @@ import ch.epfl.chassot.Ordering import ch.epfl.lexer.VerifiedRegex._ +object RegexUtils { + extension (s: String) def r: Regex[Char] = s.toCharArray().toList.foldRight[Regex[Char]](EmptyExpr())((c, acc) => Concat(ElementMatch(c), acc)) + extension (r: Regex[Char]) infix def + (r2: Regex[Char]): Regex[Char] = Union(r, r2) + extension (r: Regex[Char]) def * : Regex[Char] = Star(r) + extension (r: Regex[Char]) def + : Regex[Char] = r ~ Star(r) + extension (r: Regex[Char]) infix def ~ (r2: Regex[Char]): Regex[Char] = Concat(r, r2) + extension (s: String) infix def + (s2: String): Regex[Char] = r(s) + r(s2) + extension (s: String) infix def ~ (s2: String): Regex[Char] = r(s) ~ r(s2) + extension (s: String) def * : Regex[Char] = r(s).* + extension (s: String) def anyOf: Regex[Char] = s.toCharArray().toList.foldRight[Regex[Char]](EmptyLang())((c, acc) => Union(ElementMatch(c), acc)) +} \ No newline at end of file diff --git a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/Main.scala b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/Main.scala new file mode 100644 index 00000000..1feaa88a --- /dev/null +++ b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/Main.scala @@ -0,0 +1,98 @@ +package ch.epfl.lexer + +import ch.epfl.chassot.Ordering +import ch.epfl.chassot.Hashable +import ch.epfl.chassot.MutableHashMap +import ch.epfl.lexer.VerifiedRegex._ +import ch.epfl.lexer.VerifiedRegexMatcher._ +import ch.epfl.lexer.Memoisation._ +import ch.epfl.benchmark.RegexUtils._ +import stainless.annotation._ +import stainless.lang._ +import stainless.collection._ + +object Main { + def main(args: Array[String]): Unit = { + testRegex() + } +} + +def testRegex(): Unit = { + val cache: Cache[Char] = Cache(MutableHashMap.getEmptyHashMap(_ => EmptyLang(), KeyHashable, KeyOrdering)) + val r1 = ("a".r + "b".r).* + println(f"r1 = ${r1}") + println(f"list = ${toStainlessList("ab".toCharArray().toList)}") + println(f"matching a with r1 without cache: ${matchR(r1, Cons('a', Nil()))}") + println(f"matching a with r1: ${matchRMem(r1, toStainlessList("a".toCharArray().toList))(cache)}") + println(f"matching abababababababababbbababbababbbabab with r1: ${matchRMem(r1, toStainlessList("abababababababababbbababbababbbabab".toCharArray().toList))(cache)}") + println(f"matching abchihihi with r1: ${matchRMem(r1, toStainlessList("abchihihi".toCharArray().toList))(cache)}") + + val r2 = "abcdedfghijklmnopqrstuvwxyz.".anyOf.+ ~ "@".r ~ "abcdedfghijklmnopqrstuvwxyz".anyOf.+ ~ ".".r ~ "abcdedfghijklmnopqrstuvwxyz".anyOf.+ + println(f"r2 = ${r2}") + val s21 = "samuel.chassot@gmail.com" + println(f"matching $s21 with r2: ${matchRMem(r2, toStainlessList(s21.toCharArray().toList))(cache)}") +} + +def toStainlessList(l: scala.collection.immutable.List[Char]): stainless.collection.List[Char] = l match { + case l: scala.collection.immutable.List[Char] if l.isEmpty => stainless.collection.Nil[Char]() + case l: scala.collection.immutable.List[Char] => stainless.collection.Cons(l.head, toStainlessList(l.tail)) +} + +object KeyHashable extends Hashable[(Regex[Char], Char)] { + override def hash(x: (Regex[Char], Char)): Long = CharHashable.hash(x._2) + RegexHashable(CharHashable).hash(x._1) +} + +object KeyOrdering extends Ordering[(Regex[Char], Char)] { + override def compare(x: (Regex[Char], Char), y: (Regex[Char], Char)): Int = { + val c = CharOrdering.compare(x._2, y._2) + if (c == 0) { + RegexOrdering(CharOrdering).compare(x._1, y._1) + } else { + c + } + } +} + +object CharHashable extends Hashable[Char] { + override def hash(x: Char): Long = x.toLong +} + +object CharOrdering extends Ordering[Char] { + override def compare(x: Char, y: Char): Int = x - y +} + +case class RegexHashable[C](hc: Hashable[C]) extends Hashable[Regex[C]] { + override def hash(x: Regex[C]): Long = x match { + case ElementMatch(c) => 2L * hc.hash(c) + case Concat(l, r) => 3L * hash(l) + 5L * hash(r) + case Union(l, r) => 7L * hash(l) + 11L * hash(r) + case Star(r) => 13L * hash(r) + case _ => 17L + } +} + +case class RegexOrdering[C](oc: Ordering[C]) extends Ordering[Regex[C]] { + override def compare(x: Regex[C], y: Regex[C]): Int = (x, y) match { + case (_, _) if x == y => 0 + case (ElementMatch(xc), ElementMatch(yc)) => oc.compare(xc, yc) + case (ElementMatch(_), _) => -1 + case (_, ElementMatch(_)) => 1 + case (Concat(lx, rx), Concat(ly, ry)) => + compare(lx, ly) match { + case 0 => compare(rx, ry) + case c => c + } + case (Concat(_, _), _) => -1 + case (_, Concat(_, _)) => 1 + case (Union(lx, rx), Union(ly, ry)) => + compare(lx, ly) match { + case 0 => compare(rx, ry) + case c => c + } + case (Union(_, _), _) => -1 + case (_, Union(_, _)) => 1 + case (Star(rx), Star(ry)) => compare(rx, ry) + case (Star(_), _) => -1 + case (_, Star(_)) => 1 + } +} diff --git a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedLexer.scala b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedLexer.scala index d1e4f2c2..5e1691bd 100644 --- a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedLexer.scala +++ b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedLexer.scala @@ -11,7 +11,7 @@ import stainless.proof.check import scala.annotation.tailrec import stainless.lang.StaticChecks._ -object Main { +object MainLexer { import VerifiedLexer._ import VerifiedRegex._ import VerifiedRegexMatcher._ diff --git a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedRegex.scala b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedRegex.scala index 95c530c1..55e6c6a9 100644 --- a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedRegex.scala +++ b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedRegex.scala @@ -16,50 +16,6 @@ import ch.epfl.chassot.Ordering import ch.epfl.chassot.Hashable import stainless.lang.StaticChecks._ -// object CharRegexUtils { -// import VerifiedRegex._ -// case class CharOrdering() extends Ordering[Char] { -// override def compare(x: Char, y: Char): Int = { -// if (x < y) -1 -// else if (x > y) 1 -// else 0 -// } -// override def inverse(x: Char, y: Char): Boolean = { -// sign(compare(x, y)) == -sign(compare(y, x)) -// } -// override def transitive(x: Char, y: Char, z: Char): Boolean = { -// if (compare(x, y) > 0 && compare(y, z) > 0) compare(x, z) > 0 -// else if (compare(x, y) < 0 && compare(y, z) < 0) compare(x, z) < 0 -// else true -// } -// override def consistent(x: Char, y: Char, z: Char): Boolean = { -// if (compare(x, y) == 0) sign(compare(x, z)) == sign(compare(y, z)) -// else true -// } -// override def equalsMeansEquals(x: Char, y: Char): Boolean = { -// (compare(x, y) == 0) == (x == y) -// } -// } - -// case class CharRegexOrdering() extends Ordering[Regex[Char]] { -// override def compare(x: Regex[Char], y: Regex[Char]): Int = { -// 0 -// } -// } - -// case class CharRegexTuplOrdering() extends Ordering[(Regex[Char], Char)] { -// override def compare(x: (Regex[Char], Char), y: (Regex[Char], Char)): Int = { -// val r1 = x._1 -// val r2 = y._1 -// val char1 = x._2 -// val char2 = y._2 -// 0 - -// } -// } - -// } - object Memoisation { import VerifiedRegex._ import VerifiedRegexMatcher._ @@ -76,7 +32,8 @@ object Memoisation { @ghost def valid: Boolean = validCacheMap(cache) - @ghost def lemmaIfInCacheThenValid(r: Regex[C], c: C): Unit = { + @ghost + def lemmaIfInCacheThenValid(r: Regex[C], c: C): Unit = { require(validCacheMap(cache)) require(validRegex(r)) if (cache.contains((r, c))) { @@ -129,10 +86,10 @@ object Memoisation { } } - object VerifiedRegex { abstract sealed class Regex[C] {} + @ghost def validRegex[C](r: Regex[C]): Boolean = r match { case ElementMatch(c) => true case Star(r) => !nullable(r) && !isEmptyLang(r) && validRegex(r) @@ -142,6 +99,7 @@ object VerifiedRegex { case EmptyLang() => true } + @ghost def regexDepth[C](r: Regex[C]): BigInt = { decreases(r) r match { @@ -226,49 +184,62 @@ object VerifiedRegex { } } - @inline + // case class RegexHashable[C](cHashable: Hashable[C]) extends Hashable[Regex[C]] { + // override def hash(r: Regex[C]): Long = { + // r match { + // case ElementMatch(c) => 2L * cHashable.hash(c) + // case Star(rInner) => 3L * hash(rInner) + // case Union(r1, r2) => 5L * hash(r1) + 5L * hash(r2) + // case Concat(r1, r2) => 7L * hash(r1) + 7L * hash(r2) + // case EmptyExpr() => 11L + // case EmptyLang() => 13L + // } + // } + // } + + @ghost def isEmptyExpr[C](r: Regex[C]): Boolean = { r match { case EmptyExpr() => true case _ => false } } - @inline + @ghost def isEmptyLang[C](r: Regex[C]): Boolean = { r match { case EmptyLang() => true case _ => false } } - @inline + @ghost def isElementMatch[C](r: Regex[C]): Boolean = { r match { case ElementMatch(_) => true case _ => false } } - @inline + @ghost def elementMatchIsChar[C](r: Regex[C], c: C): Boolean = { require(isElementMatch(r)) r match { case ElementMatch(cc) => c == cc } } - @inline + @ghost def isStar[C](r: Regex[C]): Boolean = { r match { case Star(_) => true case _ => false } } - @inline + @ghost def isUnion[C](r: Regex[C]): Boolean = { r match { case Union(_, _) => true case _ => false } } - @inline + @ghost def unionInnersEquals[C](r: Regex[C], r1: Regex[C], r2: Regex[C]): Boolean = { require(isUnion(r)) r match { @@ -276,7 +247,7 @@ object VerifiedRegex { } } - @inline + @ghost def isConcat[C](r: Regex[C]): Boolean = { r match { case Concat(_, _) => true @@ -307,7 +278,7 @@ object VerifiedRegexMatcher { res } ensuring (res => validRegex(res)) - def derivativeStepMem[C](r: Regex[C], a: C)(cache: Cache[C]): Regex[C] = { + def derivativeStepMem[C](r: Regex[C], a: C)(implicit cache: Cache[C]): Regex[C] = { require(validRegex(r)) require(cache.valid) decreases(r) @@ -341,7 +312,7 @@ object VerifiedRegexMatcher { } } ensuring (res => validRegex(res)) - def derivativeMem[C](r: Regex[C], input: List[C])(cache: Cache[C]): Regex[C] = { + def derivativeMem[C](r: Regex[C], input: List[C])(implicit cache: Cache[C]): Regex[C] = { require(validRegex(r)) require(cache.valid) input match { @@ -364,13 +335,14 @@ object VerifiedRegexMatcher { } ) - def matchRCache[C](r: Regex[C], input: List[C])(cache: Cache[C]): Boolean = { + def matchRMem[C](r: Regex[C], input: List[C])(implicit cache: Cache[C]): Boolean = { require(validRegex(r)) require(cache.valid) decreases(input.size) if (input.isEmpty) nullable(r) else matchR(derivativeStepMem(r, input.head)(cache: Cache[C]), input.tail) } ensuring (res => res == matchR(r, input)) + @ghost def matchRSpec[C](r: Regex[C], s: List[C]): Boolean = { require(validRegex(r)) decreases(s.size + regexDepth(r)) @@ -384,6 +356,7 @@ object VerifiedRegexMatcher { } } + @ghost def mainMatchTheorem[C](r: Regex[C], s: List[C]): Unit = { require(validRegex(r)) decreases(s.size + regexDepth(r)) @@ -456,6 +429,7 @@ object VerifiedRegexMatcher { * @param s2 * @param s */ + @ghost def findConcatSeparation[C](r1: Regex[C], r2: Regex[C], s1: List[C], s2: List[C], s: List[C]): Option[(List[C], List[C])] = { require(validRegex(r1)) require(validRegex(r2)) @@ -515,13 +489,58 @@ object VerifiedRegexMatcher { } } ensuring (res => res._1 ++ res._2 == totalInput && (res._1.isEmpty || res._1.size >= testedP.size)) + def findLongestMatchMem[C](r: Regex[C], input: List[C])(implicit cache: Cache[C]): (List[C], List[C]) = { + require(validRegex(r)) + require(cache.valid) + findLongestMatchInnerMem(r, Nil(), input)(cache) + } ensuring (res => res == findLongestMatch(r, input) && cache.valid) + + def findLongestMatchInnerMem[C](r: Regex[C], testedP: List[C], totalInput: List[C])(implicit cache: Cache[C]): (List[C], List[C]) = { + require(validRegex(r)) + require(cache.valid) + require(ListUtils.isPrefix(testedP, totalInput)) + decreases(totalInput.size - testedP.size) + + if (testedP == totalInput) { + if (nullable(r)) { + (testedP, Nil[C]()) + } else { + (Nil[C](), totalInput) + } + } else { + ListUtils.lemmaIsPrefixThenSmallerEqSize(testedP, totalInput) + if (testedP.size == totalInput.size) { + ListUtils.lemmaIsPrefixRefl(totalInput, totalInput) + ListUtils.lemmaIsPrefixSameLengthThenSameList(totalInput, testedP, totalInput) + check(false) + } + assert(testedP.size < totalInput.size) + val suffix = ListUtils.getSuffix(totalInput, testedP) + val newP = testedP ++ List(suffix.head) + lemmaAddHeadSuffixToPrefixStillPrefix(testedP, totalInput) + check(newP.size > testedP.size) + if (nullable(r)) { + val recursive = findLongestMatchInnerMem(derivativeStepMem(r, suffix.head), newP, totalInput) + if (recursive._1.isEmpty) { + (testedP, ListUtils.getSuffix(totalInput, testedP)) + } else { + recursive + } + } else { + findLongestMatchInnerMem(derivativeStepMem(r, suffix.head), newP, totalInput) + } + } + } ensuring (res => res == findLongestMatchInner(r, testedP, totalInput) && cache.valid) + // Longest match theorems + @ghost def longestMatchIsAcceptedByMatchOrIsEmpty[C](r: Regex[C], input: List[C]): Unit = { require(validRegex(r)) longestMatchIsAcceptedByMatchOrIsEmptyRec(r, r, Nil(), input) } ensuring (_ => findLongestMatchInner(r, Nil(), input)._1.isEmpty || matchR(r, findLongestMatchInner(r, Nil(), input)._1)) + @ghost def longestMatchNoBiggerStringMatch[C](baseR: Regex[C], input: List[C], returnP: List[C], bigger: List[C]): Unit = { require(validRegex(baseR)) require(ListUtils.isPrefix(returnP, input)) @@ -540,6 +559,7 @@ object VerifiedRegexMatcher { } ensuring (_ => bigger == returnP || !matchR(baseR, bigger)) + @ghost def lemmaIfMatchRThenLongestMatchFromThereReturnsAtLeastThis[C](baseR: Regex[C], r: Regex[C], input: List[C], testedP: List[C]): Unit = { require(validRegex(baseR)) require(validRegex(r)) @@ -553,6 +573,7 @@ object VerifiedRegexMatcher { } ensuring (_ => findLongestMatchInner(r, testedP, input)._1.size >= testedP.size) + @ghost def lemmaKnownAcceptedStringThenFromSmallPAtLeastThat[C](baseR: Regex[C], r: Regex[C], input: List[C], testedP: List[C], knownP: List[C]): Unit = { require(validRegex(baseR)) require(validRegex(r)) @@ -581,6 +602,7 @@ object VerifiedRegexMatcher { } ensuring (_ => findLongestMatchInner(r, testedP, input)._1.size >= knownP.size) + @ghost def longestMatchIsAcceptedByMatchOrIsEmptyRec[C](baseR: Regex[C], r: Regex[C], testedP: List[C], input: List[C]): Unit = { require(validRegex(baseR)) require(ListUtils.isPrefix(testedP, input)) @@ -624,6 +646,7 @@ object VerifiedRegexMatcher { } ensuring (_ => findLongestMatchInner(r, testedP, input)._1.isEmpty || matchR(baseR, findLongestMatchInner(r, testedP, input)._1)) + @ghost def lemmaMatchRIsSameAsWholeDerivativeAndNil[C](r: Regex[C], input: List[C]): Unit = { require(validRegex(r)) input match { @@ -632,6 +655,7 @@ object VerifiedRegexMatcher { } } ensuring (_ => matchR(r, input) == matchR(derivative(r, input), Nil())) + @ghost def lemmaDerivativeOnLWithANewCharIsANewDerivativeStep[C](baseR: Regex[C], r: Regex[C], input: List[C], c: C): Unit = { require(validRegex(baseR)) require(derivative(baseR, input) == r) @@ -644,12 +668,14 @@ object VerifiedRegexMatcher { } ensuring (_ => derivative(baseR, input ++ List(c)) == derivativeStep(r, c)) // Basic lemmas + @ghost def lemmaIfAcceptEmptyStringThenNullable[C](r: Regex[C], s: List[C]): Unit = { require(validRegex(r)) require(s.isEmpty) require(matchR(r, s)) } ensuring (_ => nullable(r)) + @ghost def lemmaRegexAcceptsStringThenDerivativeAcceptsTail[C](r: Regex[C], s: List[C]): Unit = { require(validRegex(r)) require(matchR(r, s)) @@ -657,11 +683,13 @@ object VerifiedRegexMatcher { } ensuring (_ => if (s.isEmpty) nullable(r) else matchR(derivativeStep(r, s.head), s.tail)) // EmptyString Lemma + @ghost def lemmaRegexEmptyStringAcceptsTheEmptyString[C](r: EmptyExpr[C]): Unit = { require(validRegex(r)) } ensuring (_ => matchR(r, List())) // Single Character Lemma + @ghost def lemmaElementRegexAcceptsItsCharacterAndOnlyIt[C]( r: ElementMatch[C], c: C, @@ -671,6 +699,7 @@ object VerifiedRegexMatcher { require(c != d) } ensuring (_ => matchR(r, List(c)) && !matchR(r, List(d))) + @ghost def lemmaElementRegexDoesNotAcceptMultipleCharactersString[C]( r: ElementMatch[C], c: C, @@ -681,6 +710,7 @@ object VerifiedRegexMatcher { } ensuring (_ => !matchR(r, Cons(c, s))) // Union lemmas + @ghost def lemmaRegexAcceptsStringThenUnionWithAnotherAcceptsToo[C]( r1: Regex[C], r2: Regex[C], @@ -698,6 +728,7 @@ object VerifiedRegexMatcher { } } ensuring (_ => matchR(Union(r1, r2), s)) + @ghost def lemmaRegexUnionAcceptsThenOneOfTheTwoAccepts[C](r1: Regex[C], r2: Regex[C], s: List[C]): Unit = { require(validRegex(r1) && validRegex(r2)) require(matchR(Union(r1, r2), s)) @@ -710,6 +741,7 @@ object VerifiedRegexMatcher { } } ensuring (_ => matchR(r1, s) || matchR(r2, s)) + @ghost def lemmaReversedUnionAcceptsSameString[C]( r1: Regex[C], r2: Regex[C], @@ -729,6 +761,7 @@ object VerifiedRegexMatcher { // Concat lemmas + @ghost def lemmaRegexConcatWithNullableAcceptsSameStr[C]( r1: Regex[C], r2: Regex[C], @@ -759,6 +792,7 @@ object VerifiedRegexMatcher { } } ensuring (_ => matchR(Concat(r2, r1), s)) + @ghost def lemmaTwoRegexMatchThenConcatMatchesConcatString[C]( r1: Regex[C], r2: Regex[C], @@ -799,6 +833,7 @@ object VerifiedRegexMatcher { } } ensuring (_ => matchR(Concat(r1, r2), s1 ++ s2)) + @ghost def lemmaFindSeparationIsDefinedThenConcatMatches[C](r1: Regex[C], r2: Regex[C], s1: List[C], s2: List[C], s: List[C]): Unit = { require(validRegex(r1)) require(validRegex(r2)) @@ -810,6 +845,7 @@ object VerifiedRegexMatcher { } ensuring (_ => matchR(Concat(r1, r2), s1 ++ s2)) + @ghost def lemmaR1MatchesS1AndR2MatchesS2ThenFindSeparationFindsAtLeastThem[C]( r1: Regex[C], r2: Regex[C], @@ -859,6 +895,7 @@ object VerifiedRegexMatcher { } ensuring (_ => findConcatSeparation(r1, r2, s1Rec, s2Rec, s).isDefined) + @ghost def lemmaConcatAcceptsStringThenFindSeparationIsDefined[C](r1: Regex[C], r2: Regex[C], s: List[C]): Unit = { require(validRegex(r1)) require(validRegex(r2)) @@ -901,10 +938,12 @@ object VerifiedRegexMatcher { } ensuring (_ => findConcatSeparation(r1, r2, Nil(), s, s).isDefined) // Star lemmas + @ghost def lemmaStarAcceptsEmptyString[C](r: Star[C]): Unit = { require(validRegex(r)) } ensuring (_ => matchR(r, List())) + @ghost def lemmaStarApp[C](r: Regex[C], s1: List[C], s2: List[C]): Unit = { require(validRegex(Star(r))) require(matchR(r, s1)) @@ -919,6 +958,7 @@ object VerifiedRegexMatcher { } } ensuring (_ => matchR(Star(r), s1 ++ s2)) + @ghost def lemmaStarAppConcat[C](r: Regex[C], s: List[C]): Unit = { require(validRegex(Star(r))) require(matchR(Star(r), s)) @@ -938,6 +978,7 @@ object VerifiedRegexMatcher { // usedCharacters lemmas --------------------------------------------------------------------------------------------------- + @ghost def lemmaRegexCannotMatchAStringContainingACharItDoesNotContain[C](r: Regex[C], s: List[C], c: C): Unit = { require(validRegex(r)) require(s.contains(c)) @@ -955,6 +996,7 @@ object VerifiedRegexMatcher { } ensuring (_ => !matchR(r, s)) + @ghost def lemmaRegexCannotMatchAStringStartingWithACharItDoesNotContain[C](r: Regex[C], s: List[C], c: C): Unit = { require(validRegex(r)) require(s.contains(c)) @@ -969,6 +1011,7 @@ object VerifiedRegexMatcher { } ensuring (_ => !matchR(r, s)) + @ghost def lemmaRegexCannotMatchAStringStartingWithACharWhichIsNotInFirstChars[C](r: Regex[C], s: List[C], c: C): Unit = { require(validRegex(r)) require(s.contains(c)) @@ -984,6 +1027,7 @@ object VerifiedRegexMatcher { } ensuring (_ => !matchR(r, s)) // not used + @ghost def lemmaRIsNotNullableDerivativeStepIsThenUsedCharContainsC[C](r: Regex[C], c: C): Unit = { require(validRegex(r)) require(!nullable(r)) @@ -1027,6 +1071,7 @@ object VerifiedRegexMatcher { } ensuring (_ => usedCharacters(r).contains(c)) // DONE + @ghost def lemmaDerivativeAfterDerivativeStepIsNullableThenUsedCharsContainsHead[C](r: Regex[C], c: C, tl: List[C]): Unit = { require(validRegex(r)) require(nullable(derivative(derivativeStep(r, c), tl))) @@ -1131,6 +1176,7 @@ object VerifiedRegexMatcher { } ensuring (_ => usedCharacters(r).contains(c)) + @ghost def lemmaDerivativeStepDoesNotAddCharToUsedCharacters[C](r: Regex[C], c: C, cNot: C): Unit = { decreases(r) require(validRegex(r)) @@ -1161,6 +1207,7 @@ object VerifiedRegexMatcher { } ensuring (_ => !usedCharacters(derivativeStep(r, c)).contains(cNot)) + @ghost def lemmaEmptyLangDerivativeIsAFixPoint[C](r: Regex[C], s: List[C]): Unit = { require(r == EmptyLang[C]()) s match { @@ -1170,6 +1217,7 @@ object VerifiedRegexMatcher { } ensuring (_ => derivative(r, s) == r) + @ghost def lemmaUsedCharsContainsAllFirstChars[C](r: Regex[C], c: C): Unit = { require(validRegex(r)) require(firstChars(r).contains(c)) @@ -1198,6 +1246,7 @@ object VerifiedRegexMatcher { } ensuring (_ => usedCharacters(r).contains(c)) + @ghost def lemmaDerivAfterDerivStepIsNullableThenFirstCharsContainsHead[C](r: Regex[C], c: C, tl: List[C]): Unit = { require(validRegex(r)) require(nullable(derivative(derivativeStep(r, c), tl))) From c07af036ddece346a9d369f5925e65ad81851500 Mon Sep 17 00:00:00 2001 From: Samuel Chassot Date: Fri, 26 Jul 2024 09:54:15 +0200 Subject: [PATCH 13/78] new hashmap without ordering --- lexers/regex/verifiedlexer/build.sbt | 4 +- .../main/scala/ch/epfl/benchmark/Utils.scala | 3 +- .../main/scala/ch/epfl/chassot/Hashable.scala | 10 - .../main/scala/ch/epfl/chassot/ListMap.scala | 821 +++++++++++------ .../ch/epfl/chassot/MutableHashMap.scala | 848 ++++++++++-------- .../ch/epfl/chassot/MutableLongMap.scala | 42 +- .../main/scala/ch/epfl/chassot/Ordering.scala | 27 - .../src/main/scala/ch/epfl/lexer/Main.scala | 16 +- .../scala/ch/epfl/lexer/VerifiedLexer.scala | 1 + .../scala/ch/epfl/lexer/VerifiedRegex.scala | 52 +- 10 files changed, 1072 insertions(+), 752 deletions(-) delete mode 100644 lexers/regex/verifiedlexer/src/main/scala/ch/epfl/chassot/Hashable.scala delete mode 100644 lexers/regex/verifiedlexer/src/main/scala/ch/epfl/chassot/Ordering.scala diff --git a/lexers/regex/verifiedlexer/build.sbt b/lexers/regex/verifiedlexer/build.sbt index 8c78250f..c64b8f37 100644 --- a/lexers/regex/verifiedlexer/build.sbt +++ b/lexers/regex/verifiedlexer/build.sbt @@ -1,9 +1,9 @@ name := "VerifiedLexer" version := "0.1.0-SNAPSHOT" -scalaVersion :="3.3.0" +scalaVersion :="3.3.3" run / fork := true stainlessEnabled := false -enablePlugins(StainlessPlugin, JmhPlugin) \ No newline at end of file +enablePlugins(StainlessPlugin, JmhPlugin) diff --git a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/benchmark/Utils.scala b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/benchmark/Utils.scala index 5a4ec992..e5774b87 100644 --- a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/benchmark/Utils.scala +++ b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/benchmark/Utils.scala @@ -10,7 +10,6 @@ import stainless.lang.StaticChecks.* import stainless.collection._ import ch.epfl.chassot.Hashable -import ch.epfl.chassot.Ordering import ch.epfl.lexer.VerifiedRegex._ @@ -24,4 +23,4 @@ object RegexUtils { extension (s: String) infix def ~ (s2: String): Regex[Char] = r(s) ~ r(s2) extension (s: String) def * : Regex[Char] = r(s).* extension (s: String) def anyOf: Regex[Char] = s.toCharArray().toList.foldRight[Regex[Char]](EmptyLang())((c, acc) => Union(ElementMatch(c), acc)) -} \ No newline at end of file +} diff --git a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/chassot/Hashable.scala b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/chassot/Hashable.scala deleted file mode 100644 index f0284dbd..00000000 --- a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/chassot/Hashable.scala +++ /dev/null @@ -1,10 +0,0 @@ -/** Author: Samuel Chassot -*/ -package ch.epfl.chassot - -import stainless.annotation._ - -trait Hashable[K] { - @pure - def hash(k: K): Long -} \ No newline at end of file diff --git a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/chassot/ListMap.scala b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/chassot/ListMap.scala index a9b4a31f..16fe67da 100644 --- a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/chassot/ListMap.scala +++ b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/chassot/ListMap.scala @@ -10,16 +10,19 @@ import stainless.lang._ import stainless.proof.check import scala.annotation.tailrec import scala.collection.immutable -import ch.epfl.chassot.Ordering +import stainless.collection.ListOps.noDuplicate +import scala.collection.mutable // Uncomment the following import to run benchmarks // import OptimisedChecks.* -case class ListMap[K, B](toList: List[(K, B)], ordd: Ordering[K]) { - require(TupleListOpsGenK.isStrictlySorted(toList)(ordd)) +case class ListMap[K, B](toList: List[(K, B)]) { + require(TupleListOpsGenK.noDuplicatedKeys(toList)) def isEmpty: Boolean = toList.isEmpty + def eq(other: ListMap[K, B]): Boolean = toList.content == other.toList.content + def head: (K, B) = { require(!isEmpty) toList.head @@ -27,24 +30,24 @@ case class ListMap[K, B](toList: List[(K, B)], ordd: Ordering[K]) { def size: Int = { require(toList.size < Integer.MAX_VALUE) - TupleListOpsGenK.intSize(toList)(ordd) + TupleListOpsGenK.intSize(toList) } @pure def nKeys: Int = { require(toList.size < Integer.MAX_VALUE) - TupleListOpsGenK.intSizeKeys(TupleListOpsGenK.getKeysList(toList)(ordd))(ordd) + TupleListOpsGenK.intSizeKeys(TupleListOpsGenK.getKeysList(toList)) } def tail: ListMap[K, B] = { require(!isEmpty) - ListMap(toList.tail, ordd) + ListMap(toList.tail) } def contains(key: K): Boolean = { - val res = TupleListOpsGenK.containsKey(toList, key)(ordd) + val res = TupleListOpsGenK.containsKey(toList, key) if (res) { - TupleListOpsGenK.lemmaContainsKeyImpliesGetValueByKeyDefined(toList, key)(ordd) + TupleListOpsGenK.lemmaContainsKeyImpliesGetValueByKeyDefined(toList, key) } res @@ -52,16 +55,16 @@ case class ListMap[K, B](toList: List[(K, B)], ordd: Ordering[K]) { @inline def get(key: K): Option[B] = { - TupleListOpsGenK.getValueByKey(toList, key)(ordd) + TupleListOpsGenK.getValueByKey(toList, key) } @inline def keysOf(value: B): List[K] = { - TupleListOpsGenK.getKeysOf(toList, value)(ordd) + TupleListOpsGenK.getKeysOf(toList, value) } def keys(): List[K] = { - TupleListOpsGenK.getKeysList(toList)(ordd) + TupleListOpsGenK.getKeysList(toList) } def apply(key: K): B = { @@ -71,14 +74,14 @@ case class ListMap[K, B](toList: List[(K, B)], ordd: Ordering[K]) { def +(keyValue: (K, B)): ListMap[K, B] = { val newList = - TupleListOpsGenK.insertStrictlySorted(toList, keyValue._1, keyValue._2)(ordd) + TupleListOpsGenK.insertNoDuplicatedKeys(toList, keyValue._1, keyValue._2) TupleListOpsGenK.lemmaContainsTupThenGetReturnValue( newList, keyValue._1, keyValue._2 - )(ordd) - ListMap(newList, ordd) + ) + ListMap(newList) }.ensuring(res => res.contains(keyValue._1) && res.get(keyValue._1) == Some[B]( @@ -94,7 +97,7 @@ case class ListMap[K, B](toList: List[(K, B)], ordd: Ordering[K]) { } } def -(key: K): ListMap[K, B] = { - ListMap(TupleListOpsGenK.removeStrictlySorted(toList, key)(ordd), ordd) + ListMap(TupleListOpsGenK.removePresrvNoDuplicatedKeys(toList, key)) }.ensuring(res => !res.contains(key)) def --(keys: List[K]): ListMap[K, B] = { @@ -112,27 +115,36 @@ case class ListMap[K, B](toList: List[(K, B)], ordd: Ordering[K]) { object TupleListOpsGenK { - extension [K](k: K) def >(other: K)(implicit ord: Ordering[K]): Boolean = ord.compare(k, other) > 0 - extension [K](k: K) def <(other: K)(implicit ord: Ordering[K]): Boolean = ord.compare(k, other) < 0 - extension [K](k: K) def >=(other: K)(implicit ord: Ordering[K]): Boolean = ord.compare(k, other) >= 0 - extension [K](k: K) def <=(other: K)(implicit ord: Ordering[K]): Boolean = ord.compare(k, other) <= 0 - // @inline - def invariantList[K, B](l: List[(K, B)])(implicit ord: Ordering[K]): Boolean = { - isStrictlySorted(l) + def invariantList[K, B](l: List[(K, B)]): Boolean = { + noDuplicatedKeys(l) } - def getKeysList[K, B](l: List[(K, B)])(implicit ord: Ordering[K]): List[K] = { + def getKeysList[K, B](l: List[(K, B)]): List[K] = { require(invariantList(l)) decreases(l) l match { - case Cons(head, tl) => Cons(head._1, getKeysList(tl)) - case Nil() => Nil[K]() + case Cons(head, tl) => + if (containsKey(tl, head._1)) { + check(false) + } + if (getKeysList(tl).contains(head._1)) { + ListSpecs.forallContained(getKeysList(tl), k => containsKey(tl, k), head._1) + check(false) + } + assert(noDuplicate(getKeysList(tl)) && getKeysList(tl).forall(k => containsKey(tl, k))) + assert(noDuplicate(Cons(head._1, getKeysList(tl)))) + assert(getKeysList(tl).forall(k => containsKey(tl, k))) + lemmaForallContainsAddHeadPreserves(tl, getKeysList(tl), head) + assert(getKeysList(tl).forall(k => containsKey(Cons(head, tl), k))) + assert(Cons(head._1, getKeysList(tl)).forall(k => containsKey(Cons(head, tl), k))) + Cons(head._1, getKeysList(tl)) + case Nil() => Nil[K]() } - }.ensuring(res => isStrictlySortedK(res) && res.length == l.length) + }.ensuring(res => noDuplicate(res) && res.length == l.length && res.forall(k => containsKey(l, k))) @pure - def intSizeKeys[K](l: List[K])(implicit ord: Ordering[K]): Int = { + def intSizeKeys[K](l: List[K]): Int = { require(l.length < Integer.MAX_VALUE) decreases(l) @@ -142,7 +154,7 @@ object TupleListOpsGenK { } } - def intSize[K, B](l: List[(K, B)])(implicit ord: Ordering[K]): Int = { + def intSize[K, B](l: List[(K, B)]): Int = { decreases(l) l match { case Cons(head, tl) => { @@ -158,14 +170,14 @@ object TupleListOpsGenK { } }.ensuring(res => res >= 0) - def getKeysOf[K, B](l: List[(K, B)], value: B)(implicit ord: Ordering[K]): List[K] = { + def getKeysOf[K, B](l: List[(K, B)], value: B): List[K] = { require(invariantList(l)) decreases(l) l match { case Cons(head, tl) if (head._2 == value) => { if (!getKeysOf(tl, value).isEmpty) { - lemmaForallGetValueByKeySameWithASmallerHead( + lemmaForallGetValueByKeySameWithADiffHead( tl, getKeysOf(tl, value), value, @@ -178,7 +190,7 @@ object TupleListOpsGenK { case Cons(head, tl) if (head._2 != value) => { val r = getKeysOf(tl, value) if (!getKeysOf(tl, value).isEmpty) { - lemmaForallGetValueByKeySameWithASmallerHead( + lemmaForallGetValueByKeySameWithADiffHead( tl, getKeysOf(tl, value), value, @@ -192,37 +204,34 @@ object TupleListOpsGenK { }.ensuring(res => res.forall(getValueByKey(l, _) == Some[B](value))) - def filterByValue[K, B](l: List[(K, B)], value: B)(implicit ord: Ordering[K]): List[(K, B)] = { + def filterByValue[K, B](l: List[(K, B)], value: B): List[(K, B)] = { require(invariantList(l)) decreases(l) l match { case Cons(head, tl) if (head._2 == value) => - check(ord.equalsMeansEquals(head._1, head._1)) val res = head :: filterByValue(tl, value) - filterByValue(tl, value) match { - case Cons(a, _) => - lemmaInTailThenBigger(head, tl, a) - case _ => () + if (containsKey(filterByValue(tl, value), head._1)) { + assert(ListSpecs.subseq(filterByValue(tl, value), tl)) + lemmaContainsKeyImpliesGetValueByKeyDefined(filterByValue(tl, value), head._1) + val va = getValueByKey(filterByValue(tl, value), head._1).get + lemmaGetValueByKeyImpliesContainsTuple(filterByValue(tl, value), head._1, va) + ListSpecs.subseqContains(filterByValue(tl, value), tl, (head._1, va)) + assert(tl.contains((head._1, va))) + lemmaContainsTupleThenContainsKey(tl, head._1, va) + assert(containsKey(tl, head._1)) + check(false) } + assert(!containsKey(filterByValue(tl, value), head._1)) res case Cons(head, tl) if (head._2 != value) => val res = filterByValue(tl, value) - filterByValue(tl, value) match { - case Cons(a, _) => - lemmaInTailThenBigger(head, tl, a) - check(ord.inverse(head._1, a._1)) - case _ => () - } res case Nil() => Nil[(K, B)]() } - }.ensuring(res => - invariantList(res) && res.forall(_._2 == value) && - (if (l.isEmpty) res.isEmpty else res.isEmpty || res.head._1 >= l.head._1 && l.contains(res.head)) - ) + }.ensuring(res => res.forall(_._2 == value) && ListSpecs.subseq(res, l) && invariantList(res)) - def getValueByKey[K, B](l: List[(K, B)], key: K)(implicit ord: Ordering[K]): Option[B] = { + def getValueByKey[K, B](l: List[(K, B)], key: K): Option[B] = { require(invariantList(l)) decreases(l) @@ -234,140 +243,208 @@ object TupleListOpsGenK { } - def insertStrictlySorted[K, B]( + def insertNoDuplicatedKeys[K, B]( l: List[(K, B)], newKey: K, newValue: B - )(implicit ord: Ordering[K]): List[(K, B)] = { + ): List[(K, B)] = { require(invariantList(l)) decreases(l) l match { - case Cons(a, _) => { - check(ord.equalsMeansEquals(a._1, newKey)) - check(ord.inverse(a._1, newKey)) - } - case _ => () - } - - l match { - case Cons(head, tl) if (head._1 < newKey) => - head :: insertStrictlySorted(tl, newKey, newValue) - case Cons(head, tl) if (head._1 == newKey) => (newKey, newValue) :: tl - case Cons(head, tl) if (head._1 > newKey) => - (newKey, newValue) :: Cons(head, tl) + case Cons(head, tl) if (head._1 == newKey) => + lemmaSubseqRefl(getKeysList(l)) + (newKey, newValue) :: tl + case Cons(head, tl) if (!containsKey(l, newKey)) => (newKey, newValue) :: l + case Cons(head, tl) => + assert(containsKey(tl, newKey)) + val res = head :: insertNoDuplicatedKeys(tl, newKey, newValue) + if (containsKey(insertNoDuplicatedKeys(tl, newKey, newValue), head._1)) { + assert(head._1 != newKey) + assert(getKeysList(tl).content ++ Set(newKey) == getKeysList(insertNoDuplicatedKeys(tl, newKey, newValue)).content) + lemmaInListThenGetKeysListContains(insertNoDuplicatedKeys(tl, newKey, newValue), head._1) + assert(getKeysList(insertNoDuplicatedKeys(tl, newKey, newValue)).contains(head._1)) + assert(getKeysList(tl).contains(head._1)) + lemmaInGetKeysListThenContainsKey(tl, head._1) + assert(containsKey(tl, head._1)) + check(false) + } + assert(invariantList(res)) + assert(containsKey(res, newKey)) + assert(res.contains((newKey, newValue))) + res case Nil() => (newKey, newValue) :: Nil() } }.ensuring(res => - invariantList(res) && containsKey(res, newKey) && res.contains( - (newKey, newValue) - ) + invariantList(res) && containsKey(res, newKey) && + res.contains((newKey, newValue)) && + getKeysList(res).content == getKeysList(l).content ++ Set(newKey) ) - def removeStrictlySorted[K, B]( + def removePresrvNoDuplicatedKeys[K, B]( l: List[(K, B)], key: K - )(implicit ord: Ordering[K]): List[(K, B)] = { + ): List[(K, B)] = { require(invariantList(l)) decreases(l) - l match { - case Cons(a, Cons(b, Nil())) => { - check(ord.inverse(a._1, b._1)) - } - case Cons(a, Cons(b, Cons(c, _))) => { - check(ord.inverse(a._1, b._1)) - check(ord.inverse(b._1, c._1)) - check(ord.inverse(a._1, c._1)) - - check(ord.transitive(c._1, b._1, a._1)) - check(ord.transitive(a._1, b._1, c._1)) - check(ord.consistent(c._1, b._1, a._1)) - check(ord.consistent(c._1, a._1, b._1)) - check(ord.consistent(a._1, b._1, c._1)) - check(ord.consistent(a._1, c._1, b._1)) - check(ord.consistent(b._1, c._1, a._1)) - check(ord.consistent(b._1, a._1, c._1)) - } - case _ => () - } - - l match { - case Cons(head, tl) if (head._1 == key) => tl + case Cons(head, tl) if (head._1 == key) => + if(containsKey(l, key)){ + val h = (key, getValueByKey(l, key).get) + assert(l.head == (key, getValueByKey(l, key).get)) + if(tl.contains(h)){ + lemmaContainsTupleThenContainsKey(tl, h._1, h._2) + check(false) + } + assert(!tl.contains(head)) + check(tl.content == l.content - (key, getValueByKey(l, key).get)) + } else { + check(tl.content == l.content) + } + tl case Cons(head, tl) if (head._1 != key) => - head :: removeStrictlySorted(tl, key)(ord) + val res = head :: removePresrvNoDuplicatedKeys(tl, key) + if(getKeysList(tl).contains(head._1)){ + lemmaInGetKeysListThenContainsKey(tl, head._1) + check(false) + } + if(containsKey(removePresrvNoDuplicatedKeys(tl, key), head._1)){ + lemmaInListThenGetKeysListContains(removePresrvNoDuplicatedKeys(tl, key), head._1) + check(false) + } + res case Nil() => Nil[(K, B)]() } - }.ensuring(res => invariantList(res) && !containsKey(res, key)) + }.ensuring(res => + invariantList(res) && + !containsKey(res, key) && + getKeysList(res).content == getKeysList(l).content -- Set(key) && + (if(containsKey(l, key)) { + lemmaContainsKeyImpliesGetValueByKeyDefined(l, key) + res.content == l.content - (key, getValueByKey(l, key).get) + } else { + res.content == l.content + }) + ) - def isStrictlySorted[K, B](l: List[(K, B)])(implicit ord: Ordering[K]): Boolean = { + def noDuplicatedKeys[K, B](l: List[(K, B)]): Boolean = { decreases(l) l match { - case Nil() => true - case Cons(_, Nil()) => true - case Cons(h1, Cons(h2, _)) if (h1._1 >= h2._1) => false - case Cons(_, t) => isStrictlySorted(t) + case Nil() => true + case Cons(_, Nil()) => true + case Cons(h1, t) if (containsKey(t, h1._1)) => false + case Cons(_, t) => noDuplicatedKeys(t) } } - def isStrictlySortedK[K](l: List[K])(implicit ord: Ordering[K]): Boolean = { + def containsKey[K, B](l: List[(K, B)], key: K): Boolean = { decreases(l) l match { - case Nil() => true - case Cons(_, Nil()) => true - case Cons(h1, Cons(h2, _)) if (h1 >= h2) => false - case Cons(_, t) => isStrictlySortedK(t) + case Cons(head, _) if (head._1 == key) => true + case Cons(_, tl) => containsKey(tl, key) + case Nil() => false + } } - def containsKey[K, B](l: List[(K, B)], key: K)(implicit ord: Ordering[K]): Boolean = { + // ----------- LEMMAS ----------------------------------------------------- + + @opaque + @inlineOnce + def lemmaContainsTwoDifferentTuplesSameKeyImpossible[K, B]( + l: List[(K, B)], + key: K, + v1: B, + v2: B + ): Unit = { + require(l.contains((key, v1)) && l.contains((key, v2))) require(invariantList(l)) decreases(l) + l match { - case Cons(a, Cons(b, Nil())) => { - check(ord.equalsMeansEquals(a._1, key)) - check(ord.inverse(a._1, b._1)) - check(ord.inverse(a._1, key)) - check(ord.transitive(a._1, b._1, key)) - check(ord.transitive(a._1, key, b._1)) - check(ord.transitive(key, a._1, b._1)) - } - case Cons(a, Cons(b, Cons(c, _))) => { - check(ord.equalsMeansEquals(a._1, key)) - check(ord.inverse(a._1, b._1)) - check(ord.inverse(b._1, c._1)) - check(ord.inverse(a._1, c._1)) - - check(ord.transitive(c._1, b._1, a._1)) - check(ord.transitive(a._1, b._1, c._1)) - check(ord.consistent(c._1, b._1, a._1)) - check(ord.consistent(c._1, a._1, b._1)) - check(ord.consistent(a._1, b._1, c._1)) - check(ord.consistent(a._1, c._1, b._1)) - check(ord.consistent(b._1, c._1, a._1)) - check(ord.consistent(b._1, a._1, c._1)) - } - case Cons(a, _) => { - check(ord.inverse(a._1, key)) - check(ord.equalsMeansEquals(a._1, key)) - } + case Cons(head, tl) if (head._1 != key) => + lemmaContainsTwoDifferentTuplesSameKeyImpossible(tl, key, v1, v2) + case Cons(head, tl) if (head._1 == key) => + if (head._2 == v1) { + if(v1 != v2){ + lemmaContainsTupleThenContainsKey(tl, key, v2) + check(false) + } + } else { + if(head._2 != v2){ + lemmaContainsTupleThenContainsKey(tl, key, v2) + lemmaContainsTupleThenContainsKey(tl, key, v1) + check(false) + } + assert(head._2 == v2) + if (v1 != v2) { + lemmaContainsTupleThenContainsKey(tl, key, v1) + check(false) + } + } case _ => () } + }.ensuring(_ => v1 == v2) + + @opaque + @inlineOnce + def lemmaInListThenGetKeysListContains[K, B](l: List[(K, B)], key: K): Unit = { + require(invariantList(l)) + require(containsKey(l, key)) + decreases(l) l match { - case Cons(head, tl) if (head._1 == key) => true - case Cons(head, tl) if (head._1 > key) => false - case Cons(head, tl) if (head._1 < key) => containsKey(tl, key) - case Nil() => false + case Cons(head, tl) if (head._1 != key) => + lemmaInListThenGetKeysListContains(tl, key) + case _ => () + } + }.ensuring(_ => getKeysList(l).contains(key)) + + @inlineOnce + @opaque + def lemmaInGetKeysListThenContainsKey[K, B](l: List[(K, B)], key: K): Unit = { + require(invariantList(l)) + require(getKeysList(l).contains(key)) + decreases(l) + l match { + case Cons(head, tl) if (head._1 != key) => + lemmaInGetKeysListThenContainsKey(tl, key) + case _ => () } - } + }.ensuring(_ => containsKey(l, key)) - // ----------- LEMMAS ----------------------------------------------------- + @inlineOnce + @opaque + def lemmaSubseqRefl[B](l: List[B]): Unit = { + decreases(l.size) + l match { + case Nil() => () + case Cons(hd, tl) => lemmaSubseqRefl(tl) + } + } ensuring (_ => ListSpecs.subseq(l, l)) + + @opaque + @inlineOnce + def lemmaForallContainsAddHeadPreserves[K, B](l: List[(K, B)], keys: List[K], other: (K, B)): Unit = { + require(invariantList(l)) + require(keys.forall(k => containsKey(l, k))) + require(!containsKey(l, other._1)) + decreases(keys) + + keys match { + case Cons(head, tl) => { + lemmaAddHeadStillContainsKey(l, other._1, other._2, head) + lemmaForallContainsAddHeadPreserves(l, tl, other) + } + case _ => () + } + + } ensuring (_ => keys.forall(k => containsKey(Cons(other, l), k))) @opaque @inlineOnce - def lemmaGetValueByKeyImpliesContainsTuple[K, B](l: List[(K, B)], key: K, v: B)(implicit ord: Ordering[K]): Unit = { + def lemmaGetValueByKeyImpliesContainsTuple[K, B](l: List[(K, B)], key: K, v: B): Unit = { require(invariantList(l)) require(getValueByKey(l, key) == Some[B](v)) decreases(l) @@ -380,60 +457,41 @@ object TupleListOpsGenK { @opaque @inlineOnce - def lemmaInTailThenBigger[K, B]( - head: (K, B), - tail: List[(K, B)], - test: (K, B) - )(implicit ord: Ordering[K]): Unit = { - require(invariantList(Cons(head, tail))) - require(tail.contains(test)) - decreases(tail) - - tail match { - case Cons(hd, tl) if (hd._1 != test._1) => - check(ord.transitive(head._1, hd._1, tl.head._1)) - lemmaInTailThenBigger(head, tl, test) - case _ => () - } - }.ensuring(_ => head._1 < test._1) - - @opaque - @inlineOnce - def lemmaInsertAndRemoveStrictlySortedCommutative[K, B]( + def lemmaInsertAndremovePresrvNoDuplicatedKeysCommutative[K, B]( l: List[(K, B)], key1: K, v1: B, key2: K - )(implicit ord: Ordering[K]): Unit = { + ): Unit = { require(key1 != key2) require(invariantList(l)) decreases(l) l match { case Cons(head, tl) => { - lemmaInsertAndRemoveStrictlySortedCommutative(tl, key1, v1, key2) + lemmaInsertAndremovePresrvNoDuplicatedKeysCommutative(tl, key1, v1, key2) } case _ => () } }.ensuring(_ => - insertStrictlySorted( - removeStrictlySorted(l, key2), + insertNoDuplicatedKeys( + removePresrvNoDuplicatedKeys(l, key2), key1, v1 - ) == removeStrictlySorted( - insertStrictlySorted(l, key1, v1), + ) == removePresrvNoDuplicatedKeys( + insertNoDuplicatedKeys(l, key1, v1), key2 ) ) @opaque @inlineOnce - def lemmaInsertStrictlySortedThenRemoveIsSame[K, B]( + def lemmainsertNoDuplicatedKeysThenRemoveIsSame[K, B]( l: List[(K, B)], key1: K, v1: B - )(implicit ord: Ordering[K]): Unit = { + ): Unit = { require(invariantList(l)) require(!containsKey(l, key1)) decreases(l) @@ -441,97 +499,99 @@ object TupleListOpsGenK { l match { case Cons(head, tl) => { lemmaTailStillNotContainsKey(l, key1) - lemmaInsertStrictlySortedThenRemoveIsSame(tl, key1, v1) + lemmainsertNoDuplicatedKeysThenRemoveIsSame(tl, key1, v1) } case _ => () } - }.ensuring(_ => removeStrictlySorted(insertStrictlySorted(l, key1, v1), key1) == l) + }.ensuring(_ => removePresrvNoDuplicatedKeys(insertNoDuplicatedKeys(l, key1, v1), key1) == l) @opaque @inlineOnce - def lemmaRemoveThenInsertStrictlySortedIsSameAsInsert[K, B]( + def lemmaRemoveTheninsertNoDuplicatedKeysIsSameAsInsert[K, B]( l: List[(K, B)], key1: K, v1: B - )(implicit ord: Ordering[K]): Unit = { + ): Unit = { require(invariantList(l)) decreases(l) l match { + case Cons(head, tl) if (head._1 == key1) => () case Cons(head, tl) => { - lemmaRemoveThenInsertStrictlySortedIsSameAsInsert(tl, key1, v1) + lemmaRemoveTheninsertNoDuplicatedKeysIsSameAsInsert(tl, key1, v1) } case _ => () } - }.ensuring(_ => insertStrictlySorted(removeStrictlySorted(l, key1), key1, v1) == insertStrictlySorted(l, key1, v1)) + }.ensuring(_ => insertNoDuplicatedKeys(removePresrvNoDuplicatedKeys(l, key1), key1, v1).content == insertNoDuplicatedKeys(l, key1, v1).content) @opaque @inlineOnce - def lemmaInsertStrictlySortedCommutative[K, B]( + def lemmainsertNoDuplicatedKeysCommutative[K, B]( l: List[(K, B)], key1: K, v1: B, key2: K, v2: B - )(implicit ord: Ordering[K]): Unit = { + ): Unit = { require(key1 != key2) require(invariantList(l)) decreases(l) l match { - case Cons(head, tl) if (head._1 < key1 && head._1 < key2) => { - lemmaInsertStrictlySortedCommutative(tl, key1, v1, key2, v2) + case Cons(head, tl) if (head._1 == key1) => () + case Cons(head, tl) => { + lemmainsertNoDuplicatedKeysCommutative(tl, key1, v1, key2, v2) } case _ => () } }.ensuring(_ => - insertStrictlySorted( - insertStrictlySorted(l, key1, v1), + insertNoDuplicatedKeys( + insertNoDuplicatedKeys(l, key1, v1), key2, v2 - ) == insertStrictlySorted( - insertStrictlySorted(l, key2, v2), + ).content == insertNoDuplicatedKeys( + insertNoDuplicatedKeys(l, key2, v2), key1, v1 - ) + ).content ) @opaque @inlineOnce - def lemmaRemoveStrictlySortedCommutative[K, B]( + def lemmaremovePresrvNoDuplicatedKeysCommutative[K, B]( l: List[(K, B)], key1: K, key2: K - )(implicit ord: Ordering[K]): Unit = { + ): Unit = { require(invariantList(l)) decreases(l) l match { case Cons(head, tl) => { - lemmaRemoveStrictlySortedCommutative(tl, key1, key2) + lemmaremovePresrvNoDuplicatedKeysCommutative(tl, key1, key2) } case _ => () } }.ensuring(_ => - removeStrictlySorted( - removeStrictlySorted(l, key1), + removePresrvNoDuplicatedKeys( + removePresrvNoDuplicatedKeys(l, key1), key2 - ) == removeStrictlySorted( - removeStrictlySorted(l, key2), + ) == removePresrvNoDuplicatedKeys( + removePresrvNoDuplicatedKeys(l, key2), key1 ) ) @opaque @inlineOnce - def lemmaRemoveStrictlySortedNotPresentPreserves[K, B]( + def lemmaremovePresrvNoDuplicatedKeysNotPresentPreserves[K, B]( l: List[(K, B)], key: K - )(implicit ord: Ordering[K]): Unit = { + ): Unit = { require(invariantList(l)) require(!containsKey(l, key)) decreases(l) @@ -540,37 +600,37 @@ object TupleListOpsGenK { case Cons(head, tl) => { lemmaTailStillNotContainsKey(l, key) assert(!containsKey(tl, key)) - lemmaRemoveStrictlySortedNotPresentPreserves(tl, key) + lemmaremovePresrvNoDuplicatedKeysNotPresentPreserves(tl, key) } case _ => () } - }.ensuring(_ => removeStrictlySorted(l, key) == l) + }.ensuring(_ => removePresrvNoDuplicatedKeys(l, key) == l) @opaque @inlineOnce - def lemmaInsertStrictlySortedErasesIfSameKey[K, B]( + def lemmainsertNoDuplicatedKeysErasesIfSameKey[K, B]( l: List[(K, B)], key1: K, v1: B, v2: B - )(implicit ord: Ordering[K]): Unit = { + ): Unit = { require(invariantList(l)) decreases(l) l match { - case Cons(head, tl) if (head._1 < key1) => { - lemmaInsertStrictlySortedErasesIfSameKey(tl, key1, v1, v2) + case Cons(head, tl) => { + lemmainsertNoDuplicatedKeysErasesIfSameKey(tl, key1, v1, v2) } case _ => () } }.ensuring(_ => - insertStrictlySorted( - insertStrictlySorted(l, key1, v1), + insertNoDuplicatedKeys( + insertNoDuplicatedKeys(l, key1, v1), key1, v2 - ) == insertStrictlySorted( + ) == insertNoDuplicatedKeys( l, key1, v2 @@ -583,14 +643,14 @@ object TupleListOpsGenK { l: List[(K, B)], key: K, value: B - )(implicit ord: Ordering[K]): Unit = { + ): Unit = { require(invariantList(l)) require(!containsKey(l, key)) decreases(l) - val inserted = insertStrictlySorted(l, key, value) + val inserted = insertNoDuplicatedKeys(l, key, value) l match { - case Cons(head, tl) if (head._1 < key) => { + case Cons(head, tl) if (head._1 != key) => { lemmaAddNewKeyIncrementSize(tl, key, value) } @@ -598,7 +658,7 @@ object TupleListOpsGenK { case _ => } - }.ensuring(_ => insertStrictlySorted(l, key, value).length == l.length + 1) + }.ensuring(_ => insertNoDuplicatedKeys(l, key, value).length == l.length + 1) @opaque @inlineOnce @@ -606,14 +666,14 @@ object TupleListOpsGenK { l: List[(K, B)], key: K, value: B - )(implicit ord: Ordering[K]): Unit = { + ): Unit = { decreases(l) require(invariantList(l)) require(containsKey(l, key)) - val inserted = insertStrictlySorted(l, key, value) + val inserted = insertNoDuplicatedKeys(l, key, value) l match { - case Cons(head, tl) if (head._1 < key) => { + case Cons(head, tl) if (head._1 != key) => { lemmaAddExistingKeyPreservesSize(tl, key, value) } case Cons(head, tl) if (head._1 == key) => { @@ -622,14 +682,14 @@ object TupleListOpsGenK { case _ => } - }.ensuring(_ => insertStrictlySorted(l, key, value).length == l.length) + }.ensuring(_ => insertNoDuplicatedKeys(l, key, value).length == l.length) @opaque @inlineOnce def lemmaGetValueByKeyIsDefinedImpliesContainsKey[K, B]( l: List[(K, B)], key: K - )(implicit ord: Ordering[K]): Unit = { + ): Unit = { require(invariantList(l) && getValueByKey(l, key).isDefined) decreases(l) l match { @@ -645,7 +705,7 @@ object TupleListOpsGenK { def lemmaContainsKeyImpliesGetValueByKeyDefined[K, B]( l: List[(K, B)], key: K - )(implicit ord: Ordering[K]): Unit = { + ): Unit = { require(invariantList(l)) require(containsKey(l, key)) decreases(l) @@ -658,16 +718,16 @@ object TupleListOpsGenK { @opaque @inlineOnce - def lemmaForallGetValueByKeySameWithASmallerHead[K, B]( + def lemmaForallGetValueByKeySameWithADiffHead[K, B]( l: List[(K, B)], keys: List[K], value: B, newHead: (K, B) - )(implicit ord: Ordering[K]): Unit = { + ): Unit = { require(invariantList(l)) require(!l.isEmpty) require(keys.forall(getValueByKey(l, _) == Some[B](value))) - require(newHead._1 < l.head._1) + require(!containsKey(l, newHead._1)) decreases(keys) keys match { @@ -675,7 +735,7 @@ object TupleListOpsGenK { lemmaGetValueByKeyIsDefinedImpliesContainsKey(l, head) lemmaAddHeadStillContainsKey(l, newHead._1, newHead._2, head) lemmaContainsKeyImpliesGetValueByKeyDefined(Cons(newHead, l), head) - lemmaForallGetValueByKeySameWithASmallerHead(l, tl, value, newHead) + lemmaForallGetValueByKeySameWithADiffHead(l, tl, value, newHead) } case _ => () } @@ -689,15 +749,14 @@ object TupleListOpsGenK { key: K, value: B, test: K - )(implicit ord: Ordering[K]): Unit = { + ): Unit = { require(invariantList(l)) require(containsKey(l, test)) - require(key < l.head._1) + require(!containsKey(l, key)) decreases(l) l match { - case Cons(head, tl) if (head._1 < test) => - check(ord.transitive(key, head._1, tl.head._1)) + case Cons(head, tl) if (head._1 != test) => lemmaAddHeadStillContainsKey(tl, key, value, test) case _ => () } @@ -709,7 +768,7 @@ object TupleListOpsGenK { def lemmaTailStillNotContainsKey[K, B]( l: List[(K, B)], test: K - )(implicit ord: Ordering[K]): Unit = { + ): Unit = { require(invariantList(l)) require(!containsKey(l, test)) require(!l.isEmpty) @@ -730,18 +789,18 @@ object TupleListOpsGenK { @opaque @inlineOnce - def lemmaInsertStrictlySortedDoesNotModifyOtherKeyValues[K, B]( + def lemmainsertNoDuplicatedKeysDoesNotModifyOtherKeyValues[K, B]( l: List[(K, B)], newKey: K, newValue: B, otherKey: K - )(implicit ord: Ordering[K]): Unit = { + ): Unit = { require(invariantList(l) && newKey != otherKey) decreases(l) l match { case Cons(head, tl) if (head._1 != otherKey) => - lemmaInsertStrictlySortedDoesNotModifyOtherKeyValues( + lemmainsertNoDuplicatedKeysDoesNotModifyOtherKeyValues( tl, newKey, newValue, @@ -752,11 +811,11 @@ object TupleListOpsGenK { }.ensuring(_ => containsKey( - insertStrictlySorted(l, newKey, newValue), + insertNoDuplicatedKeys(l, newKey, newValue), otherKey ) == containsKey(l, otherKey) && getValueByKey( - insertStrictlySorted(l, newKey, newValue), + insertNoDuplicatedKeys(l, newKey, newValue), otherKey ) == getValueByKey( l, @@ -766,12 +825,12 @@ object TupleListOpsGenK { @opaque @inlineOnce - def lemmaInsertStrictlySortedDoesNotModifyOtherKeysNotContained[K, B]( + def lemmainsertNoDuplicatedKeysDoesNotModifyOtherKeysNotContained[K, B]( l: List[(K, B)], newKey: K, newValue: B, otherKey: K - )(implicit ord: Ordering[K]): Unit = { + ): Unit = { require(invariantList(l)) require(!containsKey(l, otherKey)) require(otherKey != newKey) @@ -781,7 +840,7 @@ object TupleListOpsGenK { case Cons(head, tl) => lemmaTailStillNotContainsKey(l, otherKey) assert(!containsKey(tl, otherKey)) - lemmaInsertStrictlySortedDoesNotModifyOtherKeysNotContained( + lemmainsertNoDuplicatedKeysDoesNotModifyOtherKeysNotContained( tl, newKey, newValue, @@ -789,22 +848,22 @@ object TupleListOpsGenK { ) case _ => () } - }.ensuring(_ => !containsKey(insertStrictlySorted(l, newKey, newValue), otherKey)) + }.ensuring(_ => !containsKey(insertNoDuplicatedKeys(l, newKey, newValue), otherKey)) @opaque @inlineOnce - def lemmaInsertStrictlySortedDoesNotModifyOtherKeysContained[K, B]( + def lemmainsertNoDuplicatedKeysDoesNotModifyOtherKeysContained[K, B]( l: List[(K, B)], newKey: K, newValue: B, otherKey: K - )(implicit ord: Ordering[K]): Unit = { + ): Unit = { require(invariantList(l) && containsKey(l, otherKey) && otherKey != newKey) decreases(l) l match { case Cons(head, tl) if (head._1 != otherKey) => - lemmaInsertStrictlySortedDoesNotModifyOtherKeysContained( + lemmainsertNoDuplicatedKeysDoesNotModifyOtherKeysContained( tl, newKey, newValue, @@ -812,15 +871,15 @@ object TupleListOpsGenK { ) case _ => () } - }.ensuring(_ => containsKey(insertStrictlySorted(l, newKey, newValue), otherKey)) + }.ensuring(_ => containsKey(insertNoDuplicatedKeys(l, newKey, newValue), otherKey)) @opaque @inlineOnce - def lemmaInsertStrictlySortedNotContainedContent[K, B]( + def lemmainsertNoDuplicatedKeysNotContainedContent[K, B]( l: List[(K, B)], newKey: K, newValue: B - )(implicit ord: Ordering[K]): Unit = { + ): Unit = { require(invariantList(l)) require(!containsKey(l, newKey)) decreases(l) @@ -828,13 +887,13 @@ object TupleListOpsGenK { l match { case Cons(head, tl) => { lemmaTailStillNotContainsKey(l, newKey) - lemmaInsertStrictlySortedNotContainedContent(tl, newKey, newValue) + lemmainsertNoDuplicatedKeysNotContainedContent(tl, newKey, newValue) } case _ => () } } ensuring (_ => - l.content ++ Set((newKey, newValue)) == insertStrictlySorted( + l.content ++ Set((newKey, newValue)) == insertNoDuplicatedKeys( l, newKey, newValue @@ -847,7 +906,7 @@ object TupleListOpsGenK { l: List[(K, B)], key: K, value: B - )(implicit ord: Ordering[K]): Unit = { + ): Unit = { require(invariantList(l)) require(!containsKey(l, key)) decreases(l) @@ -866,17 +925,11 @@ object TupleListOpsGenK { l: List[(K, B)], key: K, value: B - )(implicit ord: Ordering[K]): Unit = { + ): Unit = { require(invariantList(l)) require(l.contains((key, value))) decreases(l) - l match { - case Cons(a, Cons(b, _)) => - check(ord.transitive(a._1, b._1, key)) - case _ => () - } - l match { case Cons(head, tl) if (head != (key, value)) => lemmaContainsTupleThenContainsKey(tl, key, value) @@ -890,7 +943,7 @@ object TupleListOpsGenK { l: List[(K, B)], key: K, value: B - )(implicit ord: Ordering[K]): Unit = { + ): Unit = { require(invariantList(l) && containsKey(l, key) && l.contains((key, value))) decreases(l) @@ -906,7 +959,7 @@ object TupleListOpsGenK { object ListMap { // def apply[K, B](l: List[(K, B)])(using ord: Ordering[K]): ListMap[K, B] = ListMap(l, ord, ()) - def empty[K, B](implicit ord: Ordering[K]): ListMap[K, B] = ListMap[K, B](List.empty[(K, B)], ord) + def empty[K, B]: ListMap[K, B] = ListMap[K, B](List.empty[(K, B)]) } object ListMapLemmas { @@ -916,7 +969,7 @@ object ListMapLemmas { @inlineOnce def removeNotPresentStillSame[K, B](lm: ListMap[K, B], a: K): Unit = { require(!lm.contains(a)) - TupleListOpsGenK.lemmaRemoveStrictlySortedNotPresentPreserves(lm.toList, a)(lm.ordd) + TupleListOpsGenK.lemmaremovePresrvNoDuplicatedKeysNotPresentPreserves(lm.toList, a) }.ensuring(_ => lm - a == lm) @opaque @@ -927,7 +980,7 @@ object ListMapLemmas { b1: B, b2: B ): Unit = { - TupleListOpsGenK.lemmaInsertStrictlySortedErasesIfSameKey(lm.toList, a, b1, b2)(lm.ordd) + TupleListOpsGenK.lemmainsertNoDuplicatedKeysErasesIfSameKey(lm.toList, a, b1, b2) }.ensuring(_ => lm + (a, b2) == (lm + (a, b1) + (a, b2))) @opaque @@ -939,12 +992,12 @@ object ListMapLemmas { a2: K ): Unit = { require(a1 != a2) - TupleListOpsGenK.lemmaInsertAndRemoveStrictlySortedCommutative( + TupleListOpsGenK.lemmaInsertAndremovePresrvNoDuplicatedKeysCommutative( lm.toList, a1, b1, a2 - )(lm.ordd) + ) }.ensuring(_ => lm + (a1, b1) - a2 == lm - a2 + (a1, b1)) @opaque @@ -955,7 +1008,7 @@ object ListMapLemmas { b1: B ): Unit = { require(!lm.contains(a1)) - TupleListOpsGenK.lemmaInsertStrictlySortedThenRemoveIsSame(lm.toList, a1, b1)(lm.ordd) + TupleListOpsGenK.lemmainsertNoDuplicatedKeysThenRemoveIsSame(lm.toList, a1, b1) }.ensuring(_ => lm + (a1, b1) - a1 == lm) @opaque @@ -965,13 +1018,13 @@ object ListMapLemmas { a1: K, b1: B ): Unit = { - TupleListOpsGenK.lemmaRemoveThenInsertStrictlySortedIsSameAsInsert(lm.toList, a1, b1)(lm.ordd) - }.ensuring(_ => lm - a1 + (a1, b1) == lm + (a1, b1)) + TupleListOpsGenK.lemmaRemoveTheninsertNoDuplicatedKeysIsSameAsInsert(lm.toList, a1, b1) + }.ensuring(_ => (lm - a1 + (a1, b1)).eq(lm + (a1, b1))) @opaque @inlineOnce def removeCommutative[K, B](lm: ListMap[K, B], a1: K, a2: K): Unit = { - TupleListOpsGenK.lemmaRemoveStrictlySortedCommutative(lm.toList, a1, a2)(lm.ordd) + TupleListOpsGenK.lemmaremovePresrvNoDuplicatedKeysCommutative(lm.toList, a1, a2) }.ensuring(_ => lm - a1 - a2 == lm - a2 - a1) @opaque @@ -984,8 +1037,8 @@ object ListMapLemmas { b2: B ): Unit = { require(a1 != a2) - TupleListOpsGenK.lemmaInsertStrictlySortedCommutative(lm.toList, a1, b1, a2, b2)(lm.ordd) - }.ensuring(_ => lm + (a1, b1) + (a2, b2) == lm + (a2, b2) + (a1, b1)) + TupleListOpsGenK.lemmainsertNoDuplicatedKeysCommutative(lm.toList, a1, b1, a2, b2) + }.ensuring(_ => (lm + (a1, b1) + (a2, b2)).eq(lm + (a2, b2) + (a1, b1))) @opaque @inlineOnce @@ -1072,14 +1125,14 @@ object ListMapLemmas { a0: K ): Unit = { require(lm.contains(a0) && a0 != a) - assert(TupleListOpsGenK.containsKey(lm.toList, a0)(lm.ordd)) - TupleListOpsGenK.lemmaInsertStrictlySortedDoesNotModifyOtherKeyValues( + assert(TupleListOpsGenK.containsKey(lm.toList, a0)) + TupleListOpsGenK.lemmainsertNoDuplicatedKeysDoesNotModifyOtherKeyValues( lm.toList, a, b, a0 - )(lm.ordd) - TupleListOpsGenK.lemmaContainsKeyImpliesGetValueByKeyDefined(lm.toList, a0)(lm.ordd) + ) + TupleListOpsGenK.lemmaContainsKeyImpliesGetValueByKeyDefined(lm.toList, a0) }.ensuring(_ => (lm + (a -> b)).apply(a0) == lm(a0)) @@ -1094,12 +1147,12 @@ object ListMapLemmas { require(lm.contains(a0)) if (a != a0) - TupleListOpsGenK.lemmaInsertStrictlySortedDoesNotModifyOtherKeysContained( + TupleListOpsGenK.lemmainsertNoDuplicatedKeysDoesNotModifyOtherKeysContained( lm.toList, a, b, a0 - )(lm.ordd) + ) }.ensuring(_ => (lm + (a, b)).contains(a0)) @@ -1113,12 +1166,12 @@ object ListMapLemmas { ): Unit = { require(!lm.contains(a0) && a != a0) - TupleListOpsGenK.lemmaInsertStrictlySortedDoesNotModifyOtherKeysNotContained( + TupleListOpsGenK.lemmainsertNoDuplicatedKeysDoesNotModifyOtherKeysNotContained( lm.toList, a, b, a0 - )(lm.ordd) + ) }.ensuring(_ => !(lm + (a, b)).contains(a0)) @@ -1157,8 +1210,8 @@ object ListMapLemmas { def uniqueImage[K, B](lm: ListMap[K, B], a: K, b: B): Unit = { require(lm.toList.contains((a, b))) - TupleListOpsGenK.lemmaContainsTupleThenContainsKey(lm.toList, a, b)(lm.ordd) - TupleListOpsGenK.lemmaContainsTupThenGetReturnValue(lm.toList, a, b)(lm.ordd) + TupleListOpsGenK.lemmaContainsTupleThenContainsKey(lm.toList, a, b) + TupleListOpsGenK.lemmaContainsTupThenGetReturnValue(lm.toList, a, b) }.ensuring(_ => lm.get(a) == Some[B](b)) @@ -1168,9 +1221,14 @@ object ListMapLemmas { decreases(lm.toList.size) lm.toList match case Cons(h, t) => { - check(ListMap(t, lm.ordd) + (h._1, h._2) == lm) // Needed - lemmaContainsAllItsOwnKeys(ListMap(t, lm.ordd)) - lemmaInsertPairStillContainsAll(ListMap(t, lm.ordd), t, h._1, h._2) + lemmaContainsAllItsOwnKeys(ListMap(t)) + assert(t.forall(p => ListMap(t).contains(p._1))) + assert(lm == ListMap(t) + h) + lemmaInsertPairStillContainsAll(ListMap(t), t, h._1, h._2) + lemmaInsertPairStillContainsAllEq(ListMap(t), lm, t, h._1, h._2) + assert(t.forall(p => (ListMap(t) + (h._1, h._2)).contains(p._1))) + assert(t.forall(p => lm.contains(p._1))) + assert(Cons(h, t).forall(p => lm.contains(p._1))) } case Nil() => @@ -1184,13 +1242,29 @@ object ListMapLemmas { l match { case Cons(h, t) => if (h._1 != k) { - TupleListOpsGenK.lemmaInsertStrictlySortedDoesNotModifyOtherKeyValues(lm.toList, k, v, h._1)(lm.ordd) + TupleListOpsGenK.lemmainsertNoDuplicatedKeysDoesNotModifyOtherKeyValues(lm.toList, k, v, h._1) } lemmaInsertPairStillContainsAll(lm, t, k, v) case Nil() => () } } ensuring (_ => l.forall(p => (lm + (k, v)).contains(p._1))) + @opaque + @inlineOnce + def lemmaInsertPairStillContainsAllEq[K, B](lm: ListMap[K, B], lm2: ListMap[K, B], l: List[(K, B)], k: K, v: B): Unit = { + require(lm + (k, v) == lm2) + require(l.forall(p => lm.contains(p._1))) + decreases(l) + l match { + case Cons(h, t) => + if (h._1 != k) { + TupleListOpsGenK.lemmainsertNoDuplicatedKeysDoesNotModifyOtherKeyValues(lm.toList, k, v, h._1) + } + lemmaInsertPairStillContainsAllEq(lm, lm2, t, k, v) + case Nil() => () + } + } ensuring (_ => l.forall(p => (lm + (k, v)).contains(p._1)) && l.forall(p => (lm2).contains(p._1))) + @opaque @inlineOnce def addForallContainsKeyThenBeforeAdding[K, B]( @@ -1202,8 +1276,11 @@ object ListMapLemmas { require((lm + (a, b)).toList.forall(p => other.contains(p._1))) decreases(lm.toList.size) - if (!lm.isEmpty) { - addForallContainsKeyThenBeforeAdding(lm.tail, other, a, b) + lm.toList match { + case Cons(head, tl) if (head._1 == a) => () + case Cons(head, tl) => + addForallContainsKeyThenBeforeAdding(lm.tail, other, a, b) + case Nil() => () } }.ensuring { _ => @@ -1216,13 +1293,13 @@ object ListMapLemmas { require(lm.contains(a)) require(lm.get(a) == Some[B](b)) - TupleListOpsGenK.lemmaGetValueByKeyImpliesContainsTuple(lm.toList, a, b)(lm.ordd) + TupleListOpsGenK.lemmaGetValueByKeyImpliesContainsTuple(lm.toList, a, b) } ensuring (_ => lm.toList.contains((a, b))) @opaque def keysOfSound[K, B](@induct lm: ListMap[K, B], value: B): Unit = { // trivial by postcondition of getKeysOf - assert(TupleListOpsGenK.getKeysOf(lm.toList, value)(lm.ordd).forall(k => lm.get(k) == Some[B](value))) + assert(TupleListOpsGenK.getKeysOf(lm.toList, value).forall(k => lm.get(k) == Some[B](value))) }.ensuring(_ => lm.keysOf(value).forall((key: K) => lm.get(key) == Some[B](value))) @opaque @@ -1233,14 +1310,208 @@ object ListMapLemmas { value: B ): Unit = { require(!lm.contains(key)) - TupleListOpsGenK.lemmaInsertStrictlySortedNotContainedContent( + TupleListOpsGenK.lemmainsertNoDuplicatedKeysNotContainedContent( lm.toList, key, value - )(lm.ordd) + ) } ensuring (_ => lm.toList.content ++ Set( (key, value) ) == (lm + (key, value)).toList.content ) + + // Equivalence LEMMAS ---------------------------------------------------------------------------------------------------------------- + @opaque + @inlineOnce + def lemmaAddToEqMapsPreservesEq[K, B]( + lm1: ListMap[K, B], + lm2: ListMap[K, B], + key: K, + value: B + ): Unit = { + require(lm1.eq(lm2)) + if(lm1.contains(key)) { + lemmaAddToEqMapsPreservesEqIfContainsKey(lm1, lm2, key, value) + check((lm1 + (key, value)).eq(lm2 + (key, value))) + } else { + lemmaAddToEqMapsPreservesEqIfDoesNotContainKey(lm1, lm2, key, value) + check((lm1 + (key, value)).eq(lm2 + (key, value))) + } + + }.ensuring(_ => (lm1 + (key, value)).eq(lm2 + (key, value))) + + @opaque + @inlineOnce + def lemmaAddToEqMapsPreservesEqIfDoesNotContainKey[K, B]( + lm1: ListMap[K, B], + lm2: ListMap[K, B], + key: K, + value: B + ): Unit = { + require(lm1.eq(lm2)) + require(!lm1.contains(key)) + decreases(lm1.toList.size) + + assert(!lm1.contains(key)) + if(lm2.contains(key)){ + TupleListOpsGenK.lemmaContainsKeyImpliesGetValueByKeyDefined(lm2.toList, key) + TupleListOpsGenK.lemmaGetValueByKeyImpliesContainsTuple(lm2.toList, key, lm2.apply(key)) + assert(lm2.toList.contains((key, lm2.apply(key)))) + assert(lm1.toList.contains((key, lm2.apply(key)))) + TupleListOpsGenK.lemmaContainsTupleThenContainsKey(lm1.toList, key, lm2.apply(key)) + check(false) + } + assert(!lm2.contains(key)) + + + + check((lm1 + (key, value)).eq(lm2 + (key, value))) + + }.ensuring(_ => (lm1 + (key, value)).eq(lm2 + (key, value))) + + @opaque + @inlineOnce + def lemmaAddToEqMapsPreservesEqIfContainsKey[K, B]( + lm1: ListMap[K, B], + lm2: ListMap[K, B], + key: K, + value: B + ): Unit = { + require(lm1.eq(lm2)) + require(lm1.contains(key)) + decreases(lm1.toList.size) + + lemmaEquivalentThenSameContains(lm1, lm2, key) + lemmaEquivalentGetSameValue(lm1, lm2, key) + + val v = lm1.apply(key) + val v2 = lm2.apply(key) + TupleListOpsGenK.lemmaGetValueByKeyImpliesContainsTuple(lm2.toList, key, v2) + assert(lm1.toList.contains((key, v))) + assert(lm2.toList.contains((key, v))) + assert(lm1.apply(key) == lm2.apply(key)) + assert(lm1.apply(key) == v) + + val lm1WithoutKey = lm1 - key + val lm2WithoutKey = lm2 - key + lemmaRemovePreservesEq(lm1, lm2, key) + check(lm1WithoutKey.eq(lm2WithoutKey)) + check(lm1WithoutKey.contains(key) == false) + check(lm2WithoutKey.contains(key) == false) + + val lm1After = lm1WithoutKey + (key, value) + val lm2After = lm2WithoutKey + (key, value) + lemmaAddToEqMapsPreservesEqIfDoesNotContainKey(lm1WithoutKey, lm2WithoutKey, key, value) + + check(lm1After.eq(lm2After)) + + removeThenAddForSameKeyIsSameAsAdd(lm1, key, value) + removeThenAddForSameKeyIsSameAsAdd(lm2, key, value) + + check(lm1After.eq(lm1 + (key, value))) + check(lm2After.eq(lm2 + (key, value))) + + check((lm1 + (key, value)).eq(lm2 + (key, value))) + + }.ensuring(_ => (lm1 + (key, value)).eq(lm2 + (key, value))) + + @opaque + @inlineOnce + def lemmaEquivalentThenSameContains[K, B]( + lm1: ListMap[K, B], + lm2: ListMap[K, B], + key: K + ): Unit = { + require(lm1.eq(lm2)) + if(lm1.contains(key)){ + val v = lm1.apply(key) + TupleListOpsGenK.lemmaGetValueByKeyImpliesContainsTuple(lm1.toList, key, v) + assert(lm1.toList.contains((key, v))) + assert(lm2.toList.contains((key, v))) + TupleListOpsGenK.lemmaContainsTupleThenContainsKey(lm2.toList, key, v) + + assert(lm1.contains(key) == true) + assert(lm2.contains(key) == true) + + } else { + if(lm2.contains(key)){ + TupleListOpsGenK.lemmaContainsKeyImpliesGetValueByKeyDefined(lm2.toList, key) + TupleListOpsGenK.lemmaGetValueByKeyImpliesContainsTuple(lm2.toList, key, lm2.apply(key)) + assert(lm2.toList.contains((key, lm2.apply(key)))) + assert(lm1.toList.contains((key, lm2.apply(key)))) + TupleListOpsGenK.lemmaContainsTupleThenContainsKey(lm1.toList, key, lm2.apply(key)) + check(false) + } + assert(lm1.contains(key) == false) + assert(lm2.contains(key) == false) + } + }.ensuring(_ => lm1.contains(key) == lm2.contains(key)) + + @opaque + @inlineOnce + def lemmaEquivalentGetSameValue[K, B]( + lm1: ListMap[K, B], + lm2: ListMap[K, B], + key: K + ): Unit = { + require(lm1.eq(lm2)) + lemmaEquivalentThenSameContains(lm1, lm2, key) + if(lm1.contains(key)){ + val v = lm1.apply(key) + TupleListOpsGenK.lemmaGetValueByKeyImpliesContainsTuple(lm1.toList, key, v) + assert(lm1.toList.contains((key, v))) + assert(lm2.toList.contains((key, v))) + TupleListOpsGenK.lemmaContainsTupleThenContainsKey(lm2.toList, key, v) + assert(lm2.contains(key)) + val v2 = lm2.apply(key) + TupleListOpsGenK.lemmaGetValueByKeyImpliesContainsTuple(lm2.toList, key, v2) + if(v2 != v){ + assert(lm2.toList.contains((key, v2))) + assert(lm2.toList.contains((key, v))) + TupleListOpsGenK.lemmaContainsTwoDifferentTuplesSameKeyImpossible(lm2.toList, key, v, v2) + check(false) + } + assert(lm1.get(key).get == v) + assert(lm2.get(key).get == v) + } + else{ + if(lm1.get(key).isDefined){ + TupleListOpsGenK.lemmaGetValueByKeyImpliesContainsTuple(lm1.toList, key, lm1.get(key).get) + TupleListOpsGenK.lemmaContainsTupleThenContainsKey(lm1.toList, key, lm1.get(key).get) + check(false) + } + if(lm2.get(key).isDefined){ + TupleListOpsGenK.lemmaGetValueByKeyImpliesContainsTuple(lm2.toList, key, lm2.get(key).get) + TupleListOpsGenK.lemmaContainsTupleThenContainsKey(lm2.toList, key, lm2.get(key).get) + check(false) + } + assert(lm1.get(key).isEmpty) + assert(lm2.get(key).isEmpty) + } + + }.ensuring(_ => lm1.get(key) == lm2.get(key)) + + @opaque + @inlineOnce + def lemmaRemovePreservesEq[K, B]( + lm1: ListMap[K, B], + lm2: ListMap[K, B], + key: K + ): Unit = { + require(lm1.eq(lm2)) + lemmaEquivalentThenSameContains(lm1, lm2, key) + if(lm1.contains(key)) { + val v = lm1.apply(key) + val v2 = lm2.apply(key) + lemmaEquivalentGetSameValue(lm1, lm2, key) + check((lm1 - key).toList.content == lm1.toList.content -- Set((key, v))) + check((lm2 - key).toList.content == lm2.toList.content -- Set((key, v2))) + } else { + check((lm1 - key).toList.content == lm1.toList.content) + check((lm2 - key).toList.content == lm2.toList.content) + } + + }.ensuring(_ => (lm1 - key).eq(lm2 - key)) } + diff --git a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/chassot/MutableHashMap.scala b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/chassot/MutableHashMap.scala index 5e3acd98..54f64dbc 100644 --- a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/chassot/MutableHashMap.scala +++ b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/chassot/MutableHashMap.scala @@ -11,11 +11,14 @@ import scala.annotation.tailrec import stainless.lang.Cell import MutableLongMap._ import LongMapFixedSize.validMask -import ch.epfl.chassot.Hashable import stainless.lang.StaticChecks.* // Comment out when using the OptimisedEnsuring object below // import OptimisedChecks.* // Import to remove `ensuring` and `require` from the code for the benchmarks +trait Hashable[K] { + @pure + def hash(k: K): Long +} object MutableHashMap { @@ -24,18 +27,18 @@ object MutableHashMap { * @param defaultValue * @return */ - def getEmptyHashMap[K, V](defaultValue: K => V, hashF: Hashable[K], ordering: Ordering[K]): HashMap[K, V] = { + def getEmptyHashMap[K, V](defaultValue: K => V, hashF: Hashable[K]): HashMap[K, V] = { val initialSize = 16 - HashMap(Cell(MutableLongMap.getEmptyLongMap[List[(K, V)]]((l: Long) => Nil[(K, V)](), initialSize)), hashF, 0, defaultValue, ordering) + HashMap(Cell(MutableLongMap.getEmptyLongMap[List[(K, V)]]((l: Long) => Nil[(K, V)](), initialSize)), hashF, 0, defaultValue) } ensuring (res => res.valid && res.size == 0) + @mutable final case class HashMap[K, V]( val underlying: Cell[LongMap[List[(K, V)]]], val hashF: Hashable[K], var _size: Int, - val defaultValue: K => V, - val ordering: Ordering[K] + val defaultValue: K => V ) { @pure @@ -67,11 +70,11 @@ object MutableHashMap { } }) ghostExpr({ - if (extractMap(underlying.v.map.toList, ordering).contains(key)) { - lemmaInGenericMapThenInLongMap(underlying.v.map, key, hashF, ordering) + if (extractMap(underlying.v.map.toList).contains(key)) { + lemmaInGenericMapThenInLongMap(underlying.v.map, key, hashF) } else { if (((underlying.v.map.contains(hashF.hash(key)) && getPair(underlying.v.map.apply(hashF.hash(key)), key).isDefined))) { - lemmaInLongMapThenInGenericMap(underlying.v.map, key, hashF, ordering) + lemmaInLongMapThenInGenericMap(underlying.v.map, key, hashF) check(false) } } @@ -95,7 +98,7 @@ object MutableHashMap { ListSpecs.forallContained(underlying.v.map.toList, (k, v) => noDuplicateKeys(v), (hash, underlying.v.apply(hash))) }) - ghostExpr(lemmaGetPairGetSameValueAsMap(underlying.v.map, key, getPair(underlying.v.apply(hash), key).get._2, hashF, ordering)) + ghostExpr(lemmaGetPairGetSameValueAsMap(underlying.v.map, key, getPair(underlying.v.apply(hash), key).get._2, hashF)) assert(getPair(underlying.v.apply(hash), key).get._2 == map.get(key).get) getPair(underlying.v.apply(hash), key).get._2 } @@ -126,11 +129,12 @@ object MutableHashMap { check(noDuplicateKeys(removePairForKey(currentBucket, key))) check(!containsKey(removePairForKey(currentBucket, key), key)) check(noDuplicateKeys(newBucket)) - lemmaChangeOneBucketByAValidOnePreservesForallNoDuplicatesAndHash(oldLongListMap, hash, newBucket, hashF, ordering) - lemmaChangeOneBucketToUpdateAnExistingKeyUpdateThisKeyInGenMap(oldLongListMap, hash, newBucket, key, v, hashF, ordering) + lemmaChangeOneBucketByAValidOnePreservesForallNoDuplicatesAndHash(oldLongListMap, hash, newBucket, hashF) + lemmaChangeOneBucketToUpdateAnExistingKeyUpdateThisKeyInGenMap(oldLongListMap, hash, newBucket, key, v, hashF) + ListMapLemmas.lemmaEquivalentThenSameContains(map, oldMap + (key, v), key) check(underlying.v.map.toList.forall((k, v) => noDuplicateKeys(v))) - check(allKeysSameHashInMap(underlying.v.map, hashF)) // TODO - check(map == oldMap + (key, v)) + check(allKeysSameHashInMap(underlying.v.map, hashF)) + check(map.eq(oldMap + (key, v))) } else { check(valid) check(map == oldMap) @@ -152,11 +156,15 @@ object MutableHashMap { lemmaInLongMapAllKeySameHashThenForTuple(oldLongListMap.toList, hash, currentBucket, hashF) lemmaRemovePairForKeyPreservesHash(currentBucket, key, hash, hashF) } - lemmaChangeOneBucketByAValidOnePreservesForallNoDuplicatesAndHash(oldLongListMap, hash, newBucket, hashF, ordering) - lemmaChangeOneBucketToUpdateANewKeyUpdateThisKeyInGenMap(oldLongListMap, hash, newBucket, key, v, hashF, ordering) + lemmaChangeOneBucketByAValidOnePreservesForallNoDuplicatesAndHash(oldLongListMap, hash, newBucket, hashF) + lemmaChangeOneBucketToUpdateANewKeyUpdateThisKeyInGenMap(oldLongListMap, hash, newBucket, key, v, hashF) check(underlying.v.map.toList.forall((k, v) => noDuplicateKeys(v))) - check(allKeysSameHashInMap(underlying.v.map, hashF)) // TODO - check(map == oldMap + (key, v)) + check(allKeysSameHashInMap(underlying.v.map, hashF)) + check(map.eq(oldMap + (key, v))) + check(res) + ListMapLemmas.lemmaEquivalentThenSameContains(map, oldMap + (key, v), key) + check((oldMap + (key, v)).contains(key)) + check(map.contains(key)) } else { check(valid) check(map == oldMap) @@ -168,14 +176,14 @@ object MutableHashMap { } res - } ensuring (res => valid && (if (res) map.contains(key) && (map == old(this).map + (key, v)) else map == old(this).map)) + } ensuring (res => valid && (if (res) map.contains(key) && (map.eq(old(this).map + (key, v))) else map.eq(old(this).map))) def remove(key: K): Boolean = { require(valid) val contained = contains(key) if (!contained) { ghostExpr({ - lemmaRemoveNotContainedDoesNotChange(underlying.v.map, key, hashF, ordering) + lemmaRemoveNotContainedDoesNotChange(underlying.v.map, key, hashF) }) true } else { @@ -196,13 +204,13 @@ object MutableHashMap { if (res) { lemmaInLongMapAllKeySameHashThenForTuple(oldLongListMap.toList, hash, currentBucket, hashF) lemmaRemovePairForKeyPreservesHash(currentBucket, key, hash, hashF) - lemmaChangeOneBucketByAValidOnePreservesForallNoDuplicatesAndHash(oldLongListMap, hash, newBucket, hashF, ordering) + lemmaChangeOneBucketByAValidOnePreservesForallNoDuplicatesAndHash(oldLongListMap, hash, newBucket, hashF) check(underlying.v.map.toList.forall((k, v) => noDuplicateKeys(v))) check(allKeysSameHashInMap(underlying.v.map, hashF)) check(valid) check(oldMap.contains(key)) - lemmaChangeOneBucketToRemoveAKeyRemoveThisKeyInGenMap(oldLongListMap, hash, newBucket, key, hashF, ordering) - check(map == oldMap - key) + lemmaChangeOneBucketToRemoveAKeyRemoveThisKeyInGenMap(oldLongListMap, hash, newBucket, key, hashF) + check(map.eq(oldMap - key)) } else { check(valid) check(map == oldMap) @@ -211,7 +219,7 @@ object MutableHashMap { res } - } ensuring (res => valid && (if (res) map == old(this).map - key else map == old(this).map)) + } ensuring (res => valid && (if (res) map.eq(old(this).map - key) else map.eq(old(this).map))) @ghost def valid: Boolean = underlying.v.valid && @@ -222,23 +230,17 @@ object MutableHashMap { @ghost def map: ListMap[K, V] = { require(valid) - extractMap(underlying.v.map.toList, ordering) - } - - @ghost - def forall(p: ((K, V)) => Boolean): Boolean = { - require(valid) - map.forall(p) + extractMap(underlying.v.map.toList) } } @ghost - def extractMap[K, V](l: List[(Long, List[(K, V)])], ordering: Ordering[K]): ListMap[K, V] = { + def extractMap[K, V](l: List[(Long, List[(K, V)])]): ListMap[K, V] = { require(l.forall((k, v) => noDuplicateKeys(v))) decreases(l) l match { - case Cons((k, v), tl) => addToMapMapFromBucket(v, extractMap(tl, ordering)) - case Nil() => ListMap.empty[K, V](ordering) + case Cons((k, v), tl) => addToMapMapFromBucket(v, extractMap(tl)) + case Nil() => ListMap.empty[K, V] } } ensuring (res => true) @@ -338,13 +340,13 @@ object MutableHashMap { @opaque @inlineOnce @ghost - def lemmaInGenericMapThenInLongMap[K, V](lm: ListLongMap[List[(K, V)]], key: K, hashF: Hashable[K], ordering: Ordering[K]): Unit = { + def lemmaInGenericMapThenInLongMap[K, V](lm: ListLongMap[List[(K, V)]], key: K, hashF: Hashable[K]): Unit = { require(lm.toList.forall((k, v) => noDuplicateKeys(v))) require(allKeysSameHashInMap(lm, hashF)) - require(extractMap(lm.toList, ordering).contains(key)) + require(extractMap(lm.toList).contains(key)) - val map = extractMap(lm.toList, ordering) - lemmaInGenMapThenLongMapContainsHash(lm, key, hashF, ordering) + val map = extractMap(lm.toList) + lemmaInGenMapThenLongMapContainsHash(lm, key, hashF) assert(lm.contains(hashF.hash(key))) ghostExpr({ val hash = hashF.hash(key) @@ -359,7 +361,7 @@ object MutableHashMap { }) ListSpecs.forallContained(lm.toList, (k, v) => noDuplicateKeys(v), (hashF.hash(key), lm.apply(hashF.hash(key)))) - lemmaInGenMapThenGetPairDefined(lm, key, hashF, ordering) + lemmaInGenMapThenGetPairDefined(lm, key, hashF) assert(getPair(lm.apply(hashF.hash(key)), key).isDefined) } ensuring (_ => (lm.contains(hashF.hash(key)) && getPair(lm.apply(hashF.hash(key)), key).isDefined)) @@ -367,7 +369,7 @@ object MutableHashMap { @opaque @inlineOnce @ghost - def lemmaInLongMapThenInGenericMap[K, V](lm: ListLongMap[List[(K, V)]], key: K, hashF: Hashable[K], ordering: Ordering[K]): Unit = { + def lemmaInLongMapThenInGenericMap[K, V](lm: ListLongMap[List[(K, V)]], key: K, hashF: Hashable[K]): Unit = { require(lm.toList.forall((k, v) => noDuplicateKeys(v))) require(allKeysSameHashInMap(lm, hashF)) require(lm.contains(hashF.hash(key))) @@ -377,51 +379,51 @@ object MutableHashMap { getPair(lm.apply(hashF.hash(key)), key).isDefined }) - lemmaInLongMapThenContainsKeyBiggerList(lm, key, hashF, ordering) - lemmaListContainsThenExtractedMapContains(lm, key, hashF, ordering) + lemmaInLongMapThenContainsKeyBiggerList(lm, key, hashF) + lemmaListContainsThenExtractedMapContains(lm, key, hashF) - } ensuring (_ => extractMap(lm.toList, ordering).contains(key)) + } ensuring (_ => extractMap(lm.toList).contains(key)) @opaque @inlineOnce @ghost - def lemmaGetPairGetSameValueAsMap[K, V](lm: ListLongMap[List[(K, V)]], key: K, v: V, hashF: Hashable[K], ordering: Ordering[K]): Unit = { + def lemmaGetPairGetSameValueAsMap[K, V](lm: ListLongMap[List[(K, V)]], key: K, v: V, hashF: Hashable[K]): Unit = { require(lm.toList.forall((k, v) => noDuplicateKeys(v))) require(allKeysSameHashInMap(lm, hashF)) - require(extractMap(lm.toList, ordering).contains(key)) + require(extractMap(lm.toList).contains(key)) require({ - lemmaInGenMapThenLongMapContainsHash(lm, key, hashF, ordering) + lemmaInGenMapThenLongMapContainsHash(lm, key, hashF) ListLongMapLemmas.lemmaGetValueImpliesTupleContained(lm, hashF.hash(key), lm.apply(hashF.hash(key))) ListSpecs.forallContained(lm.toList, (k, v) => noDuplicateKeys(v), (hashF.hash(key), lm.apply(hashF.hash(key)))) - lemmaInGenMapThenGetPairDefined(lm, key, hashF, ordering) + lemmaInGenMapThenGetPairDefined(lm, key, hashF) getPair(lm.apply(hashF.hash(key)), key).get._2 == v }) - lemmaInLongMapThenContainsKeyBiggerList(lm, key, hashF, ordering) + lemmaInLongMapThenContainsKeyBiggerList(lm, key, hashF) check(containsKeyBiggerList(lm.toList, key)) - lemmaGetValueEquivToGetPair(lm, key, v, hashF, ordering) - lemmaExtractMapPreservesMapping(lm, key, v, hashF, ordering) + lemmaGetValueEquivToGetPair(lm, key, v, hashF) + lemmaExtractMapPreservesMapping(lm, key, v, hashF) } ensuring (_ => { - lemmaInGenMapThenLongMapContainsHash(lm, key, hashF, ordering) + lemmaInGenMapThenLongMapContainsHash(lm, key, hashF) ListLongMapLemmas.lemmaGetValueImpliesTupleContained(lm, hashF.hash(key), lm.apply(hashF.hash(key))) ListSpecs.forallContained(lm.toList, (k, v) => noDuplicateKeys(v), (hashF.hash(key), lm.apply(hashF.hash(key)))) - lemmaInGenMapThenGetPairDefined(lm, key, hashF, ordering) - getPair(lm.apply(hashF.hash(key)), key).get._2 == extractMap(lm.toList, ordering).get(key).get + lemmaInGenMapThenGetPairDefined(lm, key, hashF) + getPair(lm.apply(hashF.hash(key)), key).get._2 == extractMap(lm.toList).get(key).get }) @opaque @inlineOnce @ghost - def lemmaChangeOneBucketToRemoveAKeyRemoveThisKeyInGenMap[K, V](lm: ListLongMap[List[(K, V)]], hash: Long, newBucket: List[(K, V)], key: K, hashF: Hashable[K], ordering: Ordering[K]): Unit = { + def lemmaChangeOneBucketToRemoveAKeyRemoveThisKeyInGenMap[K, V](lm: ListLongMap[List[(K, V)]], hash: Long, newBucket: List[(K, V)], key: K, hashF: Hashable[K]): Unit = { require(lm.toList.forall((k, v) => noDuplicateKeys(v))) require(allKeysSameHashInMap(lm, hashF)) - require(extractMap(lm.toList, ordering).contains(key)) + require(extractMap(lm.toList).contains(key)) require(hashF.hash(key) == hash) require(allKeysSameHash(newBucket, hash, hashF)) require({ - lemmaInGenMapThenLongMapContainsHash(lm, key, hashF, ordering) + lemmaInGenMapThenLongMapContainsHash(lm, key, hashF) TupleListOps.lemmaGetValueByKeyImpliesContainsTuple(lm.toList, hash, lm.apply(hash)) ListSpecs.forallContained(lm.toList, (k, v) => noDuplicateKeys(v), (hash, lm.apply(hash))) newBucket == removePairForKey(lm.apply(hash), key) @@ -433,61 +435,62 @@ object MutableHashMap { case Nil() => check(false) case Cons(hd, tl) if hd._1 == hash => { if (!containsKey(hd._2, key)) { - lemmaNotInItsHashBucketThenNotInMap(lm, key, hashF, ordering) + lemmaNotInItsHashBucketThenNotInMap(lm, key, hashF) check(false) } - check(containsKey(hd._2, key)) - check(containsKeyBiggerList(List((hd._1, hd._2)), key)) - lemmaListContainsThenExtractedMapContains(ListLongMap(List((hd._1, hd._2))), key, hashF, ordering) - check(extractMap(List((hd._1, hd._2)), ordering).contains(key)) - lemmaChangeOneBucketToRemoveAKeyRemoveThisKeyInGenMapOneHash(hash, hd._2, newBucket, key, hashF, ordering) - check(tl == (lm + (hash, newBucket)).toList.tail) - check(extractMap(List((hash, newBucket)), ordering) == extractMap(List((hash, hd._2)), ordering) - key) - check(lm + (hash, newBucket) == lm.tail + (hash, newBucket)) - check(extractMap((lm.tail).toList, ordering) == extractMap(tl, ordering)) - - if (extractMap((lm.tail).toList, ordering).contains(key)) { - lemmaInGenMapThenLongMapContainsHash(lm.tail, key, hashF, ordering) + assert(containsKey(hd._2, key)) + assert(containsKeyBiggerList(List((hd._1, hd._2)), key)) + lemmaListContainsThenExtractedMapContains(ListLongMap(List((hd._1, hd._2))), key, hashF) + assert(extractMap(List((hd._1, hd._2))).contains(key)) + lemmaChangeOneBucketToRemoveAKeyRemoveThisKeyInGenMapOneHash(hash, hd._2, newBucket, key, hashF) + assert(tl == (lm + (hash, newBucket)).toList.tail) + assert(extractMap(List((hash, newBucket))).eq(extractMap(List((hash, hd._2))) - key)) + assert(lm + (hash, newBucket) == lm.tail + (hash, newBucket)) + assert(extractMap((lm.tail).toList) == extractMap(tl)) + + if (extractMap((lm.tail).toList).contains(key)) { + lemmaInGenMapThenLongMapContainsHash(lm.tail, key, hashF) check(false) } - check(!extractMap((lm.tail).toList, ordering).contains(key)) + assert(!extractMap((lm.tail).toList).contains(key)) val oldBucket = hd._2 - lemmaChangeOneBucketToRemoveAKeyRemoveThisKeyInGenMapOneHashIsHead(lm, hash, oldBucket, newBucket, key, hashF, ordering) + lemmaChangeOneBucketToRemoveAKeyRemoveThisKeyInGenMapOneHashIsHead(lm, hash, oldBucket, newBucket, key, hashF) - check((lm + (hash, newBucket)).toList == Cons((hash, newBucket), tl)) - check(extractMap((lm + (hash, newBucket)).toList, ordering) == addToMapMapFromBucket(newBucket, extractMap(tl, ordering))) - check(extractMap((lm + (hash, oldBucket)).toList, ordering) == addToMapMapFromBucket(oldBucket, extractMap(tl, ordering))) - check(extractMap((lm + (hash, oldBucket)).toList, ordering) == extractMap(lm.toList, ordering)) - check(extractMap((lm + (hash, newBucket)).toList, ordering) == extractMap(lm.toList, ordering) - key) + assert((lm + (hash, newBucket)).toList == Cons((hash, newBucket), tl)) + assert(extractMap((lm + (hash, newBucket)).toList).eq(addToMapMapFromBucket(newBucket, extractMap(tl)))) + assert(extractMap((lm + (hash, oldBucket)).toList).eq(addToMapMapFromBucket(oldBucket, extractMap(tl)))) + assert(extractMap((lm + (hash, oldBucket)).toList).eq(extractMap(lm.toList))) + assert(extractMap((lm + (hash, newBucket)).toList).eq(extractMap(lm.toList) - key)) } case Cons(hd, tl) => { val oldBucket = lm.apply(hash) - check(lm.tail.contains(hash)) - check(lm.tail.apply(hash) == oldBucket) - check(tl.contains((hash, oldBucket))) + assert(lm.tail.contains(hash)) + assert(lm.tail.apply(hash) == oldBucket) + assert(tl.contains((hash, oldBucket))) if (!containsKey(oldBucket, key)) { - lemmaNotInItsHashBucketThenNotInMap(lm, key, hashF, ordering) + lemmaNotInItsHashBucketThenNotInMap(lm, key, hashF) check(false) } - check(containsKey(oldBucket, key)) - lemmaInLongMapThenContainsKeyBiggerList(lm.tail, key, hashF, ordering) - check(containsKeyBiggerList(tl, key)) - lemmaListContainsThenExtractedMapContains(lm.tail, key, hashF, ordering) - check(extractMap(tl, ordering).contains(key)) - lemmaChangeOneBucketToRemoveAKeyRemoveThisKeyInGenMap(lm.tail, hash, newBucket, key, hashF, ordering) - check(extractMap((lm.tail + (hash, newBucket)).toList, ordering) == extractMap(lm.tail.toList, ordering) - key) - - check(extractMap((lm.tail + lm.head).toList, ordering) == extractMap(lm.toList, ordering)) - check(extractMap(lm.toList, ordering) == extractMap((lm.tail + lm.head).toList, ordering)) - check(lm.head._1 < hash) - check(lm.tail + (hash, newBucket) + lm.head == lm + (hash, newBucket)) - check((lm.tail + (hash, newBucket) + lm.head).head == lm.head) - - check((extractMap((lm.tail + (hash, newBucket) + lm.head).toList, ordering) == addToMapMapFromBucket(lm.head._2, extractMap((lm.tail + (hash, newBucket)).toList, ordering)))) - check((extractMap((lm.tail + (hash, newBucket) + lm.head).toList, ordering) == addToMapMapFromBucket(lm.head._2, (extractMap(lm.tail.toList, ordering) - key)))) + assert(containsKey(oldBucket, key)) + lemmaInLongMapThenContainsKeyBiggerList(lm.tail, key, hashF) + assert(containsKeyBiggerList(tl, key)) + lemmaListContainsThenExtractedMapContains(lm.tail, key, hashF) + assert(extractMap(tl).contains(key)) + lemmaChangeOneBucketToRemoveAKeyRemoveThisKeyInGenMap(lm.tail, hash, newBucket, key, hashF) + assert(extractMap((lm.tail + (hash, newBucket)).toList).eq(extractMap(lm.tail.toList) - key)) + + assert(extractMap((lm.tail + lm.head).toList).eq(extractMap(lm.toList))) + assert(extractMap(lm.toList).eq(extractMap((lm.tail + lm.head).toList))) + assert(lm.head._1 < hash) + assert(lm.tail + (hash, newBucket) + lm.head == lm + (hash, newBucket)) + assert((lm.tail + (hash, newBucket) + lm.head).head == lm.head) + + assert((extractMap((lm.tail + (hash, newBucket) + lm.head).toList).eq(addToMapMapFromBucket(lm.head._2, extractMap((lm.tail + (hash, newBucket)).toList))))) + lemmaAddToMapFromBucketPreservesEquivalence(extractMap((lm.tail + (hash, newBucket)).toList), extractMap(lm.tail.toList) - key, lm.head._2) + assert((extractMap((lm.tail + (hash, newBucket) + lm.head).toList).eq(addToMapMapFromBucket(lm.head._2, (extractMap(lm.tail.toList) - key))))) ListSpecs.forallContained(lm.toList, (k, v) => noDuplicateKeys(v), (hd._1, hd._2)) - check(noDuplicateKeys(lm.head._2)) + assert(noDuplicateKeys(lm.head._2)) if (containsKey(lm.head._2, key)) { check(hash != lm.head._1) @@ -497,20 +500,20 @@ object MutableHashMap { ListSpecs.forallContained(hd._2, p => hashF.hash(p._1) == hd._1, (key, value)) check(false) } - check(!containsKey(lm.head._2, key)) - lemmaAddToMapFromBucketMinusKeyIsCommutative(extractMap(lm.tail.toList, ordering), key, lm.head._2) - check(addToMapMapFromBucket(lm.head._2, (extractMap(lm.tail.toList, ordering) - key)) == addToMapMapFromBucket(lm.head._2, (extractMap(lm.tail.toList, ordering))) - key) // TODO - check(addToMapMapFromBucket(lm.head._2, (extractMap(lm.tail.toList, ordering) - key)) == extractMap(lm.toList, ordering) - key) + assert(!containsKey(lm.head._2, key)) + lemmaAddToMapFromBucketMinusKeyIsCommutative(extractMap(lm.tail.toList), key, lm.head._2) + assert(addToMapMapFromBucket(lm.head._2, (extractMap(lm.tail.toList) - key)).eq(addToMapMapFromBucket(lm.head._2, (extractMap(lm.tail.toList))) - key)) + assert(addToMapMapFromBucket(lm.head._2, (extractMap(lm.tail.toList) - key)).eq(extractMap(lm.toList) - key)) - check(extractMap((lm.tail + (hash, newBucket) + lm.head).toList, ordering) == extractMap(lm.toList, ordering) - key) + assert(extractMap((lm.tail + (hash, newBucket) + lm.head).toList).eq(extractMap(lm.toList) - key)) - check(extractMap((lm.tail + (hash, newBucket) + lm.head).toList, ordering) == extractMap((lm.tail + lm.head).toList, ordering) - key) - check(extractMap((lm + (hash, newBucket)).toList, ordering) == extractMap(lm.toList, ordering) - key) + assert(extractMap((lm.tail + (hash, newBucket) + lm.head).toList).eq(extractMap((lm.tail + lm.head).toList) - key)) + assert(extractMap((lm + (hash, newBucket)).toList).eq(extractMap(lm.toList) - key)) } } } ensuring (_ => { - extractMap((lm + (hash, newBucket)).toList, ordering) == extractMap(lm.toList, ordering) - key + extractMap((lm + (hash, newBucket)).toList).eq(extractMap(lm.toList) - key) }) @opaque @@ -523,54 +526,96 @@ object MutableHashMap { key: K, newValue: V, hashF: Hashable[K], - ordering: Ordering[K] + ): Unit = { require(lm.toList.forall((k, v) => noDuplicateKeys(v))) require(allKeysSameHashInMap(lm, hashF)) require(hashF.hash(key) == hash) require(allKeysSameHash(newBucket, hash, hashF)) - require(!extractMap(lm.toList, ordering).contains(key)) + require(!extractMap(lm.toList).contains(key)) require(lm.contains(hash) && newBucket == Cons((key, newValue), lm.apply(hash)) || !lm.contains(hash) && newBucket == Cons((key, newValue), Nil())) require(noDuplicateKeys(newBucket)) decreases(lm.toList.size) - check(lm.toList.forall((k, v) => noDuplicateKeys(v))) + assert(lm.toList.forall((k, v) => noDuplicateKeys(v))) ListLongMapLemmas.addValidProp(lm, (k, v) => noDuplicateKeys(v), hash, newBucket) - check((lm + (hash, newBucket)).toList.forall((k, v) => noDuplicateKeys(v))) + assert((lm + (hash, newBucket)).toList.forall((k, v) => noDuplicateKeys(v))) lm.toList match { case Cons(hd, tl) if hd._1 == hash => - assert(lm.contains(hash)) - check((lm + (hash, newBucket)).toList.head == (hash, newBucket)) - check((lm + (hash, newBucket)).toList.tail == tl) - check(extractMap((lm + (hash, newBucket)).toList, ordering) == addToMapMapFromBucket(newBucket, extractMap(tl, ordering))) - - check(newBucket == Cons((key, newValue), lm.apply(hash))) - val newAcc = extractMap(tl, ordering) + (key, newValue) - check(addToMapMapFromBucket(newBucket, extractMap(tl, ordering)) == addToMapMapFromBucket(lm.apply(hash), extractMap(tl, ordering) + (key, newValue))) - lemmaAddToMapFromBucketPlusKeyValueIsCommutative(extractMap(tl, ordering), key, newValue, lm.apply(hash)) - check(addToMapMapFromBucket(newBucket, extractMap(tl, ordering)) == addToMapMapFromBucket(lm.apply(hash), extractMap(tl, ordering)) + (key, newValue)) - check(extractMap((lm + (hash, newBucket)).toList, ordering) == extractMap(lm.toList, ordering) + (key, newValue)) + assert(lm.contains(hash)) + assert((lm + (hash, newBucket)).toList.head == (hash, newBucket)) + assert((lm + (hash, newBucket)).toList.tail == tl) + assert(extractMap((lm + (hash, newBucket)).toList) == addToMapMapFromBucket(newBucket, extractMap(tl))) + + assert(newBucket == Cons((key, newValue), lm.apply(hash))) + val newAcc = extractMap(tl) + (key, newValue) + assert(addToMapMapFromBucket(newBucket, extractMap(tl)) == addToMapMapFromBucket(lm.apply(hash), extractMap(tl) + (key, newValue))) + lemmaAddToMapFromBucketPlusKeyValueIsCommutative(extractMap(tl), key, newValue, lm.apply(hash)) + assert(addToMapMapFromBucket(newBucket, extractMap(tl)).eq(addToMapMapFromBucket(lm.apply(hash), extractMap(tl)) + (key, newValue))) + assert(extractMap((lm + (hash, newBucket)).toList).eq(addToMapMapFromBucket(lm.apply(hash), extractMap(tl)) + (key, newValue))) + assert(extractMap((lm + (hash, newBucket)).toList).eq(extractMap(lm.toList) + (key, newValue))) case Cons(hd, tl) if hd._1 != hash => - check(!extractMap(lm.toList, ordering).contains(key)) - if (extractMap(lm.toList.tail, ordering).contains(key)) { - lemmaExtractTailMapContainsThenExtractMapDoes(lm, key, hashF, ordering) + assert(!extractMap(lm.toList).contains(key)) + if (extractMap(lm.toList.tail).contains(key)) { + lemmaExtractTailMapContainsThenExtractMapDoes(lm, key, hashF) check(false) } - lemmaChangeOneBucketToUpdateANewKeyUpdateThisKeyInGenMap(lm.tail, hash, newBucket, key, newValue, hashF, ordering) - check(extractMap((lm.tail + (hash, newBucket)).toList, ordering) == extractMap(lm.tail.toList, ordering) + (key, newValue)) - check(lm.head != (hash, newBucket)) - check(extractMap(lm.toList, ordering) == addToMapMapFromBucket(hd._2, extractMap(lm.tail.toList, ordering))) - - lemmaNotSameHashThenCannotContainKey(lm, key, hd._1, hashF, ordering) - check(!containsKey(hd._2, key)) - lemmaAddToMapFromBucketPlusKeyValueIsCommutative(extractMap(lm.tail.toList, ordering), key, newValue, hd._2) + lemmaChangeOneBucketToUpdateANewKeyUpdateThisKeyInGenMap(lm.tail, hash, newBucket, key, newValue, hashF) + assert(extractMap((lm.tail + (hash, newBucket)).toList).eq(extractMap(lm.tail.toList) + (key, newValue))) + assert(lm.head != (hash, newBucket)) + assert(extractMap(lm.toList).eq(addToMapMapFromBucket(hd._2, extractMap(lm.tail.toList)))) - case Nil() => () + lemmaNotSameHashThenCannotContainKey(lm, key, hd._1, hashF) + assert(!containsKey(hd._2, key)) + lemmaAddToMapFromBucketPlusKeyValueIsCommutative(extractMap(lm.tail.toList), key, newValue, hd._2) + assert(addToMapMapFromBucket(hd._2, (extractMap(lm.tail.toList) + (key, newValue))).eq(addToMapMapFromBucket(hd._2, extractMap(lm.tail.toList)) + (key, newValue))) + assert(addToMapMapFromBucket(hd._2, (extractMap(lm.tail.toList) + (key, newValue))).eq(addToMapMapFromBucket(hd._2, extractMap(lm.tail.toList)) + (key, newValue))) + + assert(extractMap(lm.toList).eq(addToMapMapFromBucket(hd._2, extractMap(lm.tail.toList)))) + ListMapLemmas.lemmaAddToEqMapsPreservesEq(extractMap(lm.toList), addToMapMapFromBucket(hd._2, extractMap(lm.tail.toList)), key, newValue) + assert((extractMap(lm.toList) + (key, newValue)).eq(addToMapMapFromBucket(hd._2, extractMap(lm.tail.toList)) + (key, newValue))) + + lemmaAddToMapFromBucketPreservesEquivalence(extractMap(lm.tail.toList) + (key, newValue), extractMap((lm.tail + (hash, newBucket)).toList), hd._2) + + // ----------------------------- This is to understand the proof -------------------------------- + // if(lm.contains(hash)){ + // assert(lm.head._1 != hash) + // assert(lm.tail.contains(hash)) + + // assert(lm.tail + (hash, newBucket) == (lm + (hash, newBucket)).tail) + // assert(extractMap((lm.tail + (hash, newBucket)).toList) == extractMap((lm + (hash, newBucket)).toList.tail)) + // assert(extractMap((lm.tail + hd).toList).eq(extractMap(lm.toList))) + + // assert(extractMap((lm + (hash, newBucket)).toList).eq(addToMapMapFromBucket(hd._2, extractMap((lm + (hash, newBucket)).tail.toList)))) + // assert(extractMap((lm + (hash, newBucket)).toList).eq(addToMapMapFromBucket(hd._2, extractMap((lm.tail + (hash, newBucket)).toList)))) + + // assert( extractMap((lm.tail + (hash, newBucket)).toList).eq(extractMap(lm.tail.toList) + (key, newValue))) + + // lemmaAddToMapFromBucketPreservesEquivalence(extractMap(lm.tail.toList) + (key, newValue), extractMap((lm.tail + (hash, newBucket)).toList), hd._2) + // assert(extractMap((lm + (hash, newBucket)).toList).eq(addToMapMapFromBucket(hd._2, extractMap(lm.tail.toList) + (key, newValue)))) + + // assert(extractMap((lm + (hash, newBucket)).toList).eq(addToMapMapFromBucket(hd._2, extractMap(lm.tail.toList)) + (key, newValue))) + // assert(extractMap((lm + (hash, newBucket)).toList).eq(extractMap(lm.toList) + (key, newValue))) + // } else { + // assert(lm + (hash, newBucket) == lm + hd + (hash, newBucket)) + // if(hash < hd._1){ + // assert(lm + (hash, newBucket) == ListLongMap(Cons((hash, newBucket), lm.toList))) + // assert(extractMap((lm + (hash, newBucket)).toList).eq(extractMap(lm.toList) + (key, newValue))) + // } else { + // assert(lm + (hash, newBucket) == ListLongMap(Cons(hd, (lm.tail + (hash, newBucket)).toList))) + // lemmaAddToMapFromBucketPreservesEquivalence(extractMap(lm.tail.toList) + (key, newValue), extractMap((lm.tail + (hash, newBucket)).toList), hd._2) + // assert(extractMap((lm + (hash, newBucket)).toList).eq(extractMap(lm.toList) + (key, newValue))) + // } + + + // } + + case Nil() => () } } ensuring (_ => { - extractMap((lm + (hash, newBucket)).toList, ordering) == extractMap(lm.toList, ordering) + (key, newValue) + extractMap((lm + (hash, newBucket)).toList).eq(extractMap(lm.toList) + (key, newValue)) }) @opaque @@ -583,13 +628,13 @@ object MutableHashMap { key: K, newValue: V, hashF: Hashable[K], - ordering: Ordering[K] + ): Unit = { require(lm.toList.forall((k, v) => noDuplicateKeys(v))) require(allKeysSameHashInMap(lm, hashF)) require(hashF.hash(key) == hash) require(allKeysSameHash(newBucket, hash, hashF)) - require(extractMap(lm.toList, ordering).contains(key)) + require(extractMap(lm.toList).contains(key)) require(lm.contains(hash)) require({ TupleListOps.lemmaGetValueByKeyImpliesContainsTuple(lm.toList, hash, lm.apply(hash)) @@ -605,93 +650,67 @@ object MutableHashMap { check((lm + (hash, newBucket)).toList.forall((k, v) => noDuplicateKeys(v))) lm.toList match { - case Cons(hd, tl) if hd._1 == hash => + case Cons(hd, tl) if hd._1 == hash => check(lm + (hash, newBucket) == ListLongMap(Cons((hash, newBucket), tl))) - check(extractMap((lm + (hash, newBucket)).toList, ordering) == addToMapMapFromBucket(newBucket, extractMap(tl, ordering))) - check(extractMap((lm + (hash, newBucket)).toList, ordering) == addToMapMapFromBucket(Cons((key, newValue), removePairForKey(lm.apply(hash), key)), extractMap(tl, ordering))) - check( - addToMapMapFromBucket(Cons((key, newValue), removePairForKey(lm.apply(hash), key)), extractMap(tl, ordering)) == addToMapMapFromBucket( - removePairForKey(lm.apply(hash), key), - extractMap(tl, ordering) + (key, newValue) - ) - ) - lemmaAddToMapFromBucketPlusKeyValueIsCommutative(extractMap(tl, ordering), key, newValue, removePairForKey(lm.apply(hash), key)) - check( - addToMapMapFromBucket(Cons((key, newValue), removePairForKey(lm.apply(hash), key)), extractMap(tl, ordering)) == addToMapMapFromBucket( - removePairForKey(lm.apply(hash), key), - extractMap(tl, ordering) - ) + (key, newValue) - ) + check(extractMap((lm + (hash, newBucket)).toList) == addToMapMapFromBucket(newBucket, extractMap(tl))) + check(extractMap((lm + (hash, newBucket)).toList) == addToMapMapFromBucket(Cons((key, newValue), removePairForKey(lm.apply(hash), key)), extractMap(tl))) + check(addToMapMapFromBucket(Cons((key, newValue), removePairForKey(lm.apply(hash), key)), extractMap(tl)) == addToMapMapFromBucket(removePairForKey(lm.apply(hash), key), extractMap(tl) + (key, newValue))) + lemmaAddToMapFromBucketPlusKeyValueIsCommutative(extractMap(tl), key, newValue, removePairForKey(lm.apply(hash), key)) + check(addToMapMapFromBucket(Cons((key, newValue), removePairForKey(lm.apply(hash), key)), extractMap(tl)).eq(addToMapMapFromBucket(removePairForKey(lm.apply(hash), key), extractMap(tl)) + (key, newValue))) val intermediateLm = lm + (hash, newBucket.tail) check(newBucket.tail == removePairForKey(lm.apply(hash), key)) - check(addToMapMapFromBucket(removePairForKey(lm.apply(hash), key), extractMap(tl, ordering)) == extractMap(intermediateLm.toList, ordering)) - lemmaChangeOneBucketToRemoveAKeyRemoveThisKeyInGenMap(lm, hash, newBucket.tail, key, hashF, ordering) - check(extractMap(intermediateLm.toList, ordering) == extractMap(lm.toList, ordering) - key) + check( addToMapMapFromBucket(removePairForKey(lm.apply(hash), key), extractMap(tl)) == extractMap(intermediateLm.toList)) + lemmaChangeOneBucketToRemoveAKeyRemoveThisKeyInGenMap(lm, hash, newBucket.tail, key, hashF) + check(extractMap(intermediateLm.toList).eq(extractMap(lm.toList) - key)) + ListMapLemmas.lemmaEquivalentThenSameContains(extractMap(intermediateLm.toList), extractMap(lm.toList) - key, key) check(intermediateLm.apply(hash) == newBucket.tail) - lemmaChangeOneBucketToUpdateANewKeyUpdateThisKeyInGenMap(intermediateLm, hash, newBucket, key, newValue, hashF, ordering) - check(extractMap((intermediateLm + (hash, newBucket)).toList, ordering) == extractMap(intermediateLm.toList, ordering) + (key, newValue)) - check(extractMap(intermediateLm.toList, ordering) == extractMap(lm.toList, ordering) - key) + assert(!extractMap(intermediateLm.toList).contains(key)) + lemmaChangeOneBucketToUpdateANewKeyUpdateThisKeyInGenMap(intermediateLm, hash, newBucket, key, newValue, hashF) + check(extractMap((intermediateLm + (hash, newBucket)).toList).eq(extractMap(intermediateLm.toList) + (key, newValue))) + check(extractMap(intermediateLm.toList).eq(extractMap(lm.toList) - key)) - ListMapLemmas.removeThenAddForSameKeyIsSameAsAdd(extractMap(lm.toList, ordering), key, newValue) + ListMapLemmas.removeThenAddForSameKeyIsSameAsAdd(extractMap(lm.toList), key, newValue) - check(extractMap((lm + (hash, newBucket)).toList, ordering) == extractMap(lm.toList, ordering) + (key, newValue)) - case Cons(hd, tl) if hd._1 != hash => - lemmaNotSameHashThenCannotContainKey(lm, key, hd._1, hashF, ordering) + check(extractMap((lm + (hash, newBucket)).toList).eq(extractMap(lm.toList) + (key, newValue))) + case Cons(hd, tl) if hd._1 != hash => + lemmaNotSameHashThenCannotContainKey(lm, key, hd._1, hashF) check(!containsKey(hd._2, key)) - lemmaAddToMapContainsAndNotInListThenInAcc(hd._2, key, newValue, extractMap(tl, ordering)) + lemmaAddToMapContainsAndNotInListThenInAcc(hd._2, key, newValue, extractMap(tl)) - check(extractMap(lm.tail.toList, ordering).contains(key)) - lemmaChangeOneBucketToUpdateAnExistingKeyUpdateThisKeyInGenMap(lm.tail, hash, newBucket, key, newValue, hashF, ordering) - check(extractMap((lm.tail + (hash, newBucket)).toList, ordering) == extractMap(lm.tail.toList, ordering) + (key, newValue)) + check(extractMap(lm.tail.toList).contains(key)) + lemmaChangeOneBucketToUpdateAnExistingKeyUpdateThisKeyInGenMap(lm.tail, hash, newBucket, key, newValue, hashF) + check(extractMap((lm.tail + (hash, newBucket)).toList).eq(extractMap(lm.tail.toList) + (key, newValue))) check(lm.head != (hash, newBucket)) - check(extractMap(lm.toList, ordering) == addToMapMapFromBucket(hd._2, extractMap(lm.tail.toList, ordering))) + check(extractMap(lm.toList) == addToMapMapFromBucket(hd._2, extractMap(lm.tail.toList))) + + lemmaAddToMapFromBucketPlusKeyValueIsCommutative(extractMap(lm.tail.toList), key, newValue, hd._2) + + assert(addToMapMapFromBucket(hd._2, (extractMap(lm.tail.toList) + (key, newValue))).eq(addToMapMapFromBucket(hd._2, extractMap(lm.tail.toList)) + (key, newValue))) + + assert(addToMapMapFromBucket(hd._2, extractMap(lm.tail.toList)).eq(extractMap(lm.toList))) + + ListMapLemmas.lemmaAddToEqMapsPreservesEq(extractMap(lm.toList), addToMapMapFromBucket(hd._2, extractMap(lm.tail.toList)), key, newValue) - lemmaAddToMapFromBucketPlusKeyValueIsCommutative(extractMap(lm.tail.toList, ordering), key, newValue, hd._2) + lemmaAddToMapFromBucketPreservesEquivalence(extractMap(lm.tail.toList) + (key, newValue), extractMap((lm.tail + (hash, newBucket)).toList), hd._2) + - check(extractMap((lm + (hash, newBucket)).toList, ordering) == extractMap(lm.toList, ordering) + (key, newValue)) + check(extractMap((lm + (hash, newBucket)).toList).eq(extractMap(lm.toList) + (key, newValue))) case Nil() => () } } ensuring (_ => { - extractMap((lm + (hash, newBucket)).toList, ordering) == extractMap(lm.toList, ordering) + (key, newValue) + extractMap((lm + (hash, newBucket)).toList).eq(extractMap(lm.toList) + (key, newValue)) }) - @opaque - @inlineOnce - @ghost - def lemmaForallThenForAPair[K, V](hashMap: HashMap[K, V], p: ((K, V)) => Boolean, k: K, v: V): Unit = { - require(hashMap.valid) - require(hashMap.forall(p)) - require(hashMap.contains(k)) - require(hashMap.apply(k) == v) - - ListMapLemmas.lemmaGetValueImpliesTupleContained(hashMap.map, k, v) - - check(hashMap.map.toList.contains((k, v))) - ListSpecs.forallContained(hashMap.map.toList, p, (k, v)) - check(p((k, v))) - - } ensuring (_ => p((k, v))) - - @opaque - @inlineOnce - @ghost - def lemmaUpdateValidPairMaintainsForall[K, V](hashMap: HashMap[K, V], p: ((K, V)) => Boolean, k: K, v: V): Unit = { - require(hashMap.valid) - require(hashMap.forall(p)) - require(p((k, v))) - - } ensuring (_ => (hashMap.map + (k, v)).forall(p)) - // ----------------------------------------------------- @opaque @inlineOnce - @ghost + @ghost def lemmaAddToMapContainsAndNotInListThenInAcc[K, V](l: List[(K, V)], key: K, value: V, acc: ListMap[K, V]): Unit = { require(noDuplicateKeys(l)) require(!containsKey(l, key)) @@ -699,15 +718,15 @@ object MutableHashMap { decreases(l.size) l match { - case Cons((k, v), t) => - val newAcc = acc + (k, v) - assert(k != key) - lemmaAddToMapContainsAndNotInListThenInAcc(t, key, value, newAcc) - check(newAcc.contains(k)) - if (!acc.contains(key)) { - ListMapLemmas.addStillNotContains(acc, k, v, key) - check(false) - } + case Cons((k, v), t) => + val newAcc = acc + (k, v) + assert(k != key) + lemmaAddToMapContainsAndNotInListThenInAcc(t, key, value, newAcc) + check(newAcc.contains(k)) + if(!acc.contains(key)){ + ListMapLemmas.addStillNotContains(acc, k, v, key) + check(false) + } case Nil() => () } @@ -717,50 +736,55 @@ object MutableHashMap { @opaque @inlineOnce @ghost - def lemmaExtractTailMapContainsThenExtractMapDoes[K, V](lm: ListLongMap[List[(K, V)]], key: K, hashF: Hashable[K], ordering: Ordering[K]): Unit = { + def lemmaExtractTailMapContainsThenExtractMapDoes[K, V](lm: ListLongMap[List[(K, V)]], key: K, hashF: Hashable[K]): Unit = { require(lm.toList.forall((k, v) => noDuplicateKeys(v))) require(allKeysSameHashInMap(lm, hashF)) require(!lm.toList.isEmpty) - require(extractMap(lm.tail.toList, ordering).contains(key)) + require(extractMap(lm.tail.toList).contains(key)) decreases(lm.toList.size) val hash = hashF.hash(key) lm.toList match { case Cons((k, v), tl) => - check(extractMap(lm.toList, ordering) == addToMapMapFromBucket(v, extractMap(lm.tail.toList, ordering))) - lemmaAddToMapMaintainsContains(lm.tail, key, v, hashF, ordering) + check(extractMap(lm.toList) == addToMapMapFromBucket(v, extractMap(lm.tail.toList))) + lemmaAddToMapMaintainsContains(lm.tail, key, v, hashF) case Nil() => () } - } ensuring (_ => extractMap(lm.toList, ordering).contains(key)) + } ensuring (_ => extractMap(lm.toList).contains(key)) @opaque @inlineOnce @ghost - def lemmaAddToMapMaintainsContains[K, V](lm: ListLongMap[List[(K, V)]], key: K, l: List[(K, V)], hashF: Hashable[K], ordering: Ordering[K]): Unit = { + def lemmaAddToMapMaintainsContains[K, V](lm: ListLongMap[List[(K, V)]], key: K, l: List[(K, V)], hashF: Hashable[K]): Unit = { require(lm.toList.forall((k, v) => noDuplicateKeys(v))) require(allKeysSameHashInMap(lm, hashF)) require(noDuplicateKeys(l)) - require(extractMap(lm.toList, ordering).contains(key)) + require(extractMap(lm.toList).contains(key)) decreases(l.size) val hash = hashF.hash(key) l match { case Cons(hd, tl) => - lemmaAddToMapMaintainsContains(lm, key, tl, hashF, ordering) - check(addToMapMapFromBucket(tl, extractMap(lm.toList, ordering)).contains(key)) - lemmaAddToMapFromBucketPlusKeyValueIsCommutative(extractMap(lm.toList, ordering), hd._1, hd._2, tl) - - check(addToMapMapFromBucket(tl, extractMap(lm.toList, ordering) + hd) == addToMapMapFromBucket(tl, extractMap(lm.toList, ordering)) + hd) - ListMapLemmas.addStillContains(addToMapMapFromBucket(tl, extractMap(lm.toList, ordering)), hd._1, hd._2, key) - check(addToMapMapFromBucket(tl, extractMap(lm.toList, ordering) + hd).contains(key)) + lemmaAddToMapMaintainsContains(lm, key, tl, hashF) + check(addToMapMapFromBucket(tl, extractMap(lm.toList)).contains(key)) + + lemmaAddToMapFromBucketPlusKeyValueIsCommutative(extractMap(lm.toList), hd._1, hd._2, tl) + val lm1 = addToMapMapFromBucket(tl, extractMap(lm.toList) + hd) + val lm2 = addToMapMapFromBucket(tl, extractMap(lm.toList)) + hd + check(lm1.eq(lm2)) + ListMapLemmas.addStillContains(addToMapMapFromBucket(tl, extractMap(lm.toList)), hd._1, hd._2, key) + assert(addToMapMapFromBucket(tl, extractMap(lm.toList)).contains(key)) + assert((addToMapMapFromBucket(tl, extractMap(lm.toList)) + hd).contains(key)) + ListMapLemmas.lemmaEquivalentThenSameContains(lm1, lm2, key) + check(addToMapMapFromBucket(tl, extractMap(lm.toList) + hd).contains(key)) case Nil() => () } - } ensuring (_ => addToMapMapFromBucket(l, extractMap(lm.toList, ordering)).contains(key)) + } ensuring (_ => addToMapMapFromBucket(l, extractMap(lm.toList)).contains(key)) @opaque @inlineOnce @@ -794,11 +818,39 @@ object MutableHashMap { val newAcc = lhm + (k, v) ListMapLemmas.addCommutativeForDiffKeys(lhm, k, v, key, value) lemmaAddToMapFromBucketPlusKeyValueIsCommutative(newAcc, key, value, t) + check(addToMapMapFromBucket(t, (newAcc + (key, value))).eq(addToMapMapFromBucket(t, newAcc) + (key, value))) + //addToMapMapFromBucket(tl, acc + (k, v)) + + check(addToMapMapFromBucket(l, (lhm + (key, value))) == addToMapMapFromBucket(t, (lhm + (key, value)) + (k, v))) + check(((lhm + (k, v)) + (key, value)).eq((lhm + (key, value)) + (k, v))) + val lhm1 = (lhm + (key, value)) + (k, v) + val lhm2 = (lhm + (k, v)) + (key, value) + lemmaAddToMapFromBucketPreservesEquivalence(lhm1, lhm2, t) + check(addToMapMapFromBucket(t, lhm1).eq(addToMapMapFromBucket(t, lhm2))) case Nil() => check(addToMapMapFromBucket(l, (lhm + (key, value))) == addToMapMapFromBucket(l, lhm) + (key, value)) } - } ensuring (_ => addToMapMapFromBucket(l, (lhm + (key, value))) == addToMapMapFromBucket(l, lhm) + (key, value)) + } ensuring (_ => addToMapMapFromBucket(l, (lhm + (key, value))).eq(addToMapMapFromBucket(l, lhm) + (key, value))) + + @opaque + @inlineOnce + def lemmaAddToMapFromBucketPreservesEquivalence[K, V](lhm1: ListMap[K, V], lhm2: ListMap[K, V], l: List[(K, V)]): Unit = { + require(noDuplicateKeys(l)) + require(lhm1.eq(lhm2)) + decreases(l) + + l match { + case Cons((k, v), t) => + val newAcc1 = lhm1 + (k, v) + val newAcc2 = lhm2 + (k, v) + ListMapLemmas.lemmaAddToEqMapsPreservesEq(lhm1, lhm2, k, v) + check(newAcc1.eq(newAcc2)) + lemmaAddToMapFromBucketPreservesEquivalence(newAcc1, newAcc2, t) + case Nil() => + check(lhm1.eq(lhm2)) + } + } ensuring (_ => addToMapMapFromBucket(l, lhm1).eq(addToMapMapFromBucket(l, lhm2))) @opaque @inlineOnce @@ -810,14 +862,14 @@ object MutableHashMap { newBucket: List[(K, V)], key: K, hashF: Hashable[K], - ordering: Ordering[K] + ): Unit = { require(lm.toList.forall((k, v) => noDuplicateKeys(v))) require(noDuplicateKeys(oldBucket)) require(noDuplicateKeys(newBucket)) require(removePairForKey(oldBucket, key) == newBucket) require(allKeysSameHash(oldBucket, hash, hashF)) - require(extractMap(lm.toList, ordering).contains(key)) + require(extractMap(lm.toList).contains(key)) require(hashF.hash(key) == hash) require(allKeysSameHash(newBucket, hash, hashF)) require(allKeysSameHashInMap(lm, hashF)) @@ -829,69 +881,89 @@ object MutableHashMap { l match case Cons(h, t) => check(t == lm.tail.toList) - check(extractMap(l, ordering) == addToMapMapFromBucket(h._2, extractMap(t, ordering))) - // check(extractMap(t, ordering) == ListMap.empty[K, V](ordering)) + check(extractMap(l) == addToMapMapFromBucket(h._2, extractMap(t))) + // check(extractMap(t) == ListMap.empty[K, V]) oldBucket match { case Cons(hd, tl) if hd._1 == key => assert(oldBucket.tail == newBucket) - check(extractMap(Cons((hash, oldBucket.tail), t), ordering) == extractMap(Cons((hash, newBucket), t), ordering)) - check(addToMapMapFromBucket(oldBucket.tail, extractMap(t, ordering)) == addToMapMapFromBucket(newBucket, extractMap(t, ordering))) + assert(extractMap(Cons((hash, oldBucket.tail), t)) == extractMap(Cons((hash, newBucket), t))) + assert(addToMapMapFromBucket(oldBucket.tail, extractMap(t)) == addToMapMapFromBucket(newBucket, extractMap(t))) - lemmaAddToMapFromBucketTlPlusHeadIsSameAsList(oldBucket.head, oldBucket.tail, extractMap(t, ordering)) - check(addToMapMapFromBucket(oldBucket.tail, extractMap(t, ordering)) + hd == addToMapMapFromBucket(oldBucket, extractMap(t, ordering))) + lemmaAddToMapFromBucketTlPlusHeadIsSameAsList(oldBucket.head, oldBucket.tail, extractMap(t)) + assert((addToMapMapFromBucket(oldBucket.tail, extractMap(t)) + hd).eq(addToMapMapFromBucket(oldBucket, extractMap(t)))) - check(!containsKey(oldBucket.tail, key)) - val m = addToMapMapFromBucket(oldBucket.tail, extractMap(t, ordering)) - check(m == extractMap(Cons((hash, oldBucket.tail), t), ordering)) - lemmaNotInItsHashBucketThenNotInMap(ListLongMap(Cons((hash, oldBucket.tail), t)), key, hashF, ordering) - check(!m.contains(key)) + assert(!containsKey(oldBucket.tail, key)) + val m = addToMapMapFromBucket(oldBucket.tail, extractMap(t)) + assert(m == extractMap(Cons((hash, oldBucket.tail), t))) + lemmaNotInItsHashBucketThenNotInMap(ListLongMap(Cons((hash, oldBucket.tail), t)), key, hashF) + assert(!m.contains(key)) ListMapLemmas.addThenRemoveForNewKeyIsSame(m, key, hd._2) - check((m + hd) - key == m) - check(extractMap(Cons((hash, oldBucket), t), ordering) - key == extractMap(Cons((hash, oldBucket.tail), t), ordering)) - check(extractMap(Cons((hash, newBucket), t), ordering) == extractMap(Cons((hash, oldBucket), t), ordering) - key) + assert((m + hd) - key == m) + assert((m + hd).eq(extractMap(Cons((hash, oldBucket), t)))) + ListMapLemmas.lemmaRemovePreservesEq(m + hd, extractMap(Cons((hash, oldBucket), t)), key) + assert((extractMap(Cons((hash, oldBucket), t)) - key).eq(extractMap(Cons((hash, oldBucket.tail), t)))) + assert(extractMap(Cons((hash, newBucket), t)).eq(extractMap(Cons((hash, oldBucket), t)) - key)) case Cons(hd, tl) if hd._1 != key => - lemmaInGenMapThenGetPairDefined(lm, key, hashF, ordering) - lemmaGetPairDefinedThenContainsKey(oldBucket, key, hashF, ordering) - check(containsKey(oldBucket, key)) - check(containsKey(tl, key)) - check(removePairForKey(oldBucket, key) == newBucket) - check(removePairForKey(oldBucket.tail, key) == newBucket.tail) - check(removePairForKey(tl, key) == newBucket.tail) - - lemmaListContainsThenExtractedMapContains(ListLongMap(Cons((hash, tl), t)), key, hashF, ordering) - check(extractMap(Cons((hash, tl), t), ordering).contains(key)) - - lemmaAddToMapFromBucketTlPlusHeadIsSameAsList(oldBucket.head, oldBucket.tail, extractMap(t, ordering)) - lemmaAddToMapFromBucketTlPlusHeadIsSameAsList(newBucket.head, newBucket.tail, extractMap(t, ordering)) - check(extractMap(Cons((hash, oldBucket), t), ordering) == extractMap(Cons((hash, oldBucket.tail), t), ordering) + hd) - check(hd == newBucket.head) - check(extractMap(Cons((hash, oldBucket), t), ordering) == extractMap(Cons((hash, oldBucket.tail), t), ordering) + newBucket.head) - - check(extractMap(Cons((hash, tl), t), ordering).contains(key)) - lemmaChangeOneBucketToRemoveAKeyRemoveThisKeyInGenMapOneHashIsHead(ListLongMap(Cons((hash, tl), t)), hash, tl, newBucket.tail, key, hashF, ordering) - check(extractMap(Cons((hash, newBucket.tail), t), ordering) == extractMap(Cons((hash, tl), t), ordering) - key) - ListMapLemmas.addRemoveCommutativeForDiffKeys(extractMap(Cons((hash, tl), t), ordering), hd._1, hd._2, key) - check(extractMap(Cons((hash, newBucket), t), ordering) == extractMap(Cons((hash, oldBucket), t), ordering) - key) + lemmaInGenMapThenGetPairDefined(lm, key, hashF) + lemmaGetPairDefinedThenContainsKey(oldBucket, key, hashF) + assert(containsKey(oldBucket, key)) + assert(containsKey(tl, key)) + assert(removePairForKey(oldBucket, key) == newBucket) + assert(removePairForKey(oldBucket.tail, key) == newBucket.tail) + assert(removePairForKey(tl, key) == newBucket.tail) + + lemmaListContainsThenExtractedMapContains(ListLongMap(Cons((hash, tl), t)), key, hashF) + assert(extractMap(Cons((hash, tl), t)).contains(key)) + + lemmaAddToMapFromBucketTlPlusHeadIsSameAsList(oldBucket.head, oldBucket.tail, extractMap(t)) + lemmaAddToMapFromBucketTlPlusHeadIsSameAsList(newBucket.head, newBucket.tail, extractMap(t)) + assert(extractMap(Cons((hash, oldBucket), t)).eq(extractMap(Cons((hash, oldBucket.tail), t)) + hd)) + assert(hd == newBucket.head) + assert(extractMap(Cons((hash, oldBucket), t)).eq(extractMap(Cons((hash, oldBucket.tail), t)) + newBucket.head)) + + assert(extractMap(Cons((hash, tl), t)).contains(key)) + lemmaChangeOneBucketToRemoveAKeyRemoveThisKeyInGenMapOneHashIsHead(ListLongMap(Cons((hash, tl), t)), hash, tl, newBucket.tail, key, hashF) + + assert(extractMap(Cons((hash, newBucket.tail), t)).eq(extractMap(Cons((hash, tl), t)) - key)) + + ListMapLemmas.addRemoveCommutativeForDiffKeys(extractMap(Cons((hash, tl), t)), hd._1, hd._2, key) + //lm + (a1, b1) - a2 == lm - a2 + (a1, b1) + assert(extractMap(Cons((hash, tl), t)) + hd - key == extractMap(Cons((hash, tl), t)) - key + hd) + assert((extractMap(Cons((hash, tl), t)) + hd - key).eq(extractMap(Cons((hash, tl), t)) - key + hd)) + + assert((extractMap(Cons((hash, tl), t)) + hd).eq(extractMap(Cons((hash, oldBucket), t)))) + + ListMapLemmas.lemmaAddToEqMapsPreservesEq(extractMap(Cons((hash, tl), t)) - key, extractMap(Cons((hash, newBucket.tail), t)), hd._1, hd._2) + assert(((extractMap(Cons((hash, tl), t)) - key) + hd).eq(extractMap(Cons((hash, newBucket.tail), t)) + hd)) + + assert(((extractMap(Cons((hash, tl), t)) + hd)).eq(extractMap(Cons((hash, oldBucket), t)))) + ListMapLemmas.lemmaRemovePreservesEq((extractMap(Cons((hash, tl), t)) + hd), extractMap(Cons((hash, oldBucket), t)), key) + assert(((extractMap(Cons((hash, tl), t)) + hd) - key).eq(extractMap(Cons((hash, oldBucket), t)) - key)) + assert(((extractMap(Cons((hash, tl), t)) - key) + hd).eq(extractMap(Cons((hash, oldBucket), t)) - key)) + + assert((extractMap(Cons((hash, newBucket.tail), t)) + hd).eq(extractMap(Cons((hash, newBucket), t)))) + + assert(extractMap(Cons((hash, newBucket), t)).eq(extractMap(Cons((hash, oldBucket), t)) - key)) case Nil() => - lemmaNotInItsHashBucketThenNotInMap(lm, key, hashF, ordering) + lemmaNotInItsHashBucketThenNotInMap(lm, key, hashF) check(false) } case Nil() => check(false) } ensuring (_ => { - extractMap(Cons((hash, newBucket), lm.toList.tail), ordering) == extractMap(Cons((hash, oldBucket), lm.toList.tail), ordering) - key + extractMap(Cons((hash, newBucket), lm.toList.tail)).eq(extractMap(Cons((hash, oldBucket), lm.toList.tail)) - key) }) @opaque @inlineOnce @ghost - def lemmaChangeOneBucketToRemoveAKeyRemoveThisKeyInGenMapOneHash[K, V](hash: Long, oldBucket: List[(K, V)], newBucket: List[(K, V)], key: K, hashF: Hashable[K], ordering: Ordering[K]): Unit = { + def lemmaChangeOneBucketToRemoveAKeyRemoveThisKeyInGenMapOneHash[K, V](hash: Long, oldBucket: List[(K, V)], newBucket: List[(K, V)], key: K, hashF: Hashable[K]): Unit = { require(noDuplicateKeys(oldBucket)) require(noDuplicateKeys(newBucket)) require(removePairForKey(oldBucket, key) == newBucket) require(allKeysSameHash(oldBucket, hash, hashF)) - require(extractMap(List((hash, oldBucket)), ordering).contains(key)) + require(extractMap(List((hash, oldBucket))).contains(key)) require(hashF.hash(key) == hash) require(allKeysSameHash(newBucket, hash, hashF)) require(noDuplicateKeys(newBucket)) @@ -901,54 +973,67 @@ object MutableHashMap { l match case Cons(h, t) => check(t.isEmpty) - check(extractMap(l, ordering) == addToMapMapFromBucket(h._2, extractMap(t, ordering))) - check(extractMap(t, ordering) == ListMap.empty[K, V](ordering)) + check(extractMap(l) == addToMapMapFromBucket(h._2, extractMap(t))) + check(extractMap(t) == ListMap.empty[K, V]) oldBucket match { case Cons(hd, tl) if hd._1 == key => assert(oldBucket.tail == newBucket) - check(extractMap(List((hash, oldBucket.tail)), ordering) == extractMap(List((hash, newBucket)), ordering)) - check(addToMapMapFromBucket(oldBucket.tail, ListMap.empty[K, V](ordering)) == addToMapMapFromBucket(newBucket, ListMap.empty[K, V](ordering))) + assert(extractMap(List((hash, oldBucket.tail))) == extractMap(List((hash, newBucket)))) + assert(addToMapMapFromBucket(oldBucket.tail, ListMap.empty[K, V]) == addToMapMapFromBucket(newBucket, ListMap.empty[K, V])) - lemmaAddToMapFromBucketTlPlusHeadIsSameAsList(oldBucket.head, oldBucket.tail, ListMap.empty[K, V](ordering)) - check(addToMapMapFromBucket(oldBucket.tail, ListMap.empty[K, V](ordering)) + hd == addToMapMapFromBucket(oldBucket, ListMap.empty[K, V](ordering))) + lemmaAddToMapFromBucketTlPlusHeadIsSameAsList(oldBucket.head, oldBucket.tail, ListMap.empty[K, V]) + assert((addToMapMapFromBucket(oldBucket.tail, ListMap.empty[K, V]) + hd).eq(addToMapMapFromBucket(oldBucket, ListMap.empty[K, V]))) - check(!containsKey(oldBucket.tail, key)) - val m = addToMapMapFromBucket(oldBucket.tail, ListMap.empty[K, V](ordering)) - check(m == extractMap(List((hash, oldBucket.tail)), ordering)) - lemmaNotInItsHashBucketThenNotInMap(ListLongMap(List((hash, oldBucket.tail))), key, hashF, ordering) - check(!m.contains(key)) + assert(!containsKey(oldBucket.tail, key)) + val m = addToMapMapFromBucket(oldBucket.tail, ListMap.empty[K, V]) + assert(m == extractMap(List((hash, oldBucket.tail)))) + lemmaNotInItsHashBucketThenNotInMap(ListLongMap(List((hash, oldBucket.tail))), key, hashF) + assert(!m.contains(key)) ListMapLemmas.addThenRemoveForNewKeyIsSame(m, key, hd._2) - check((m + hd) - key == m) - check(extractMap(List((hash, oldBucket)), ordering) - key == extractMap(List((hash, newBucket)), ordering)) + assert((m + hd) - key == m) + assert(extractMap(List((hash, oldBucket))) == addToMapMapFromBucket(oldBucket, ListMap.empty[K, V])) + assert((m + hd).eq(extractMap(List((hash, oldBucket))))) + ListMapLemmas.lemmaRemovePreservesEq(m + hd, extractMap(List((hash, oldBucket))), key) + assert(extractMap(List((hash, newBucket))) == m) + assert((extractMap(List((hash, oldBucket))) - key).eq(extractMap(List((hash, newBucket))))) case Cons(hd, tl) if hd._1 != key => - lemmaInGenMapThenGetPairDefined(ListLongMap(List((hash, oldBucket))), key, hashF, ordering) - lemmaGetPairDefinedThenContainsKey(oldBucket, key, hashF, ordering) - check(containsKey(oldBucket, key)) - check(containsKey(tl, key)) - check(removePairForKey(oldBucket, key) == newBucket) - check(removePairForKey(oldBucket.tail, key) == newBucket.tail) - check(removePairForKey(tl, key) == newBucket.tail) - - lemmaListContainsThenExtractedMapContains(ListLongMap(List((hash, tl))), key, hashF, ordering) - check(extractMap(List((hash, tl)), ordering).contains(key)) - - lemmaAddToMapFromBucketTlPlusHeadIsSameAsList(oldBucket.head, oldBucket.tail, ListMap.empty[K, V](ordering)) - lemmaAddToMapFromBucketTlPlusHeadIsSameAsList(newBucket.head, newBucket.tail, ListMap.empty[K, V](ordering)) - check(extractMap(List((hash, oldBucket)), ordering) == extractMap(List((hash, oldBucket.tail)), ordering) + hd) - check(extractMap(List((hash, newBucket)), ordering) == extractMap(List((hash, newBucket.tail)), ordering) + newBucket.head) - - lemmaChangeOneBucketToRemoveAKeyRemoveThisKeyInGenMapOneHash(hash, tl, newBucket.tail, key, hashF, ordering) - check(extractMap(List((hash, newBucket.tail)), ordering) == extractMap(List((hash, tl)), ordering) - key) - ListMapLemmas.addRemoveCommutativeForDiffKeys(extractMap(List((hash, tl)), ordering), hd._1, hd._2, key) - check(extractMap(List((hash, newBucket)), ordering) == extractMap(List((hash, oldBucket)), ordering) - key) + lemmaInGenMapThenGetPairDefined(ListLongMap(List((hash, oldBucket))), key, hashF) + lemmaGetPairDefinedThenContainsKey(oldBucket, key, hashF) + assert(containsKey(oldBucket, key)) + assert(containsKey(tl, key)) + assert(removePairForKey(oldBucket, key) == newBucket) + assert(removePairForKey(oldBucket.tail, key) == newBucket.tail) + assert(removePairForKey(tl, key) == newBucket.tail) + + lemmaListContainsThenExtractedMapContains(ListLongMap(List((hash, tl))), key, hashF) + assert(extractMap(List((hash, tl))).contains(key)) + + lemmaAddToMapFromBucketTlPlusHeadIsSameAsList(oldBucket.head, oldBucket.tail, ListMap.empty[K, V]) + lemmaAddToMapFromBucketTlPlusHeadIsSameAsList(newBucket.head, newBucket.tail, ListMap.empty[K, V]) + assert(extractMap(List((hash, oldBucket))).eq(extractMap(List((hash, oldBucket.tail))) + hd)) + assert(extractMap(List((hash, newBucket))).eq(extractMap(List((hash, newBucket.tail))) + newBucket.head)) + + lemmaChangeOneBucketToRemoveAKeyRemoveThisKeyInGenMapOneHash(hash, tl, newBucket.tail, key, hashF) + assert(extractMap(List((hash, newBucket.tail))).eq(extractMap(List((hash, tl))) - key)) + ListMapLemmas.addRemoveCommutativeForDiffKeys(extractMap(List((hash, tl))), hd._1, hd._2, key) + //lm + (a1, b1) - a2 == lm - a2 + (a1, b1) + assert(extractMap(List((hash, tl))) + hd - key == extractMap(List((hash, tl))) - key + hd) + ListMapLemmas.lemmaRemovePreservesEq(extractMap(List((hash, tl))) + hd, extractMap(List((hash, oldBucket))), key) + assert((extractMap(List((hash, oldBucket))) - key).eq(extractMap(List((hash, tl))) - key + hd)) + assert(newBucket.head == hd) + assert(extractMap(List((hash, newBucket))).eq(extractMap(List((hash, newBucket.tail))) + hd)) + ListMapLemmas.lemmaAddToEqMapsPreservesEq(extractMap(List((hash, tl))) - key, extractMap(List((hash, newBucket.tail))), hd._1, hd._2) + assert(extractMap(List((hash, newBucket))).eq(extractMap(List((hash, tl))) - key + hd)) + assert(extractMap(List((hash, oldBucket))).eq(extractMap(List((hash, tl))) + hd)) + assert(extractMap(List((hash, newBucket))).eq(extractMap(List((hash, oldBucket))) - key)) case Nil() => check(false) } case Nil() => check(false) } ensuring (_ => { - extractMap(List((hash, newBucket)), ordering) == extractMap(List((hash, oldBucket)), ordering) - key + extractMap(List((hash, newBucket))).eq(extractMap(List((hash, oldBucket))) - key) }) @opaque @@ -975,7 +1060,7 @@ object MutableHashMap { @opaque @inlineOnce @ghost - def lemmaChangeOneBucketByAValidOnePreservesForallNoDuplicatesAndHash[K, V](lm: ListLongMap[List[(K, V)]], hash: Long, newBucket: List[(K, V)], hashF: Hashable[K], ordering: Ordering[K]): Unit = { + def lemmaChangeOneBucketByAValidOnePreservesForallNoDuplicatesAndHash[K, V](lm: ListLongMap[List[(K, V)]], hash: Long, newBucket: List[(K, V)], hashF: Hashable[K]): Unit = { require(lm.toList.forall((k, v) => noDuplicateKeys(v))) require(allKeysSameHashInMap(lm, hashF)) require(noDuplicateKeys(newBucket)) @@ -983,7 +1068,7 @@ object MutableHashMap { decreases(lm.toList.size) lm.toList match case Cons(h, t) => { - lemmaChangeOneBucketByAValidOnePreservesForallNoDuplicatesAndHash(ListLongMap(t), hash, newBucket, hashF, ordering) + lemmaChangeOneBucketByAValidOnePreservesForallNoDuplicatesAndHash(ListLongMap(t), hash, newBucket, hashF) } case Nil() => () @@ -995,14 +1080,14 @@ object MutableHashMap { @opaque @inlineOnce @ghost - def lemmaRemoveNotContainedDoesNotChange[K, V](lm: ListLongMap[List[(K, V)]], key: K, hashF: Hashable[K], ordering: Ordering[K]): Unit = { + def lemmaRemoveNotContainedDoesNotChange[K, V](lm: ListLongMap[List[(K, V)]], key: K, hashF: Hashable[K]): Unit = { require(lm.toList.forall((k, v) => noDuplicateKeys(v))) require(allKeysSameHashInMap(lm, hashF)) - require(!extractMap(lm.toList, ordering).contains(key)) + require(!extractMap(lm.toList).contains(key)) - ListMapLemmas.removeNotPresentStillSame(extractMap(lm.toList, ordering), key) + ListMapLemmas.removeNotPresentStillSame(extractMap(lm.toList), key) - } ensuring (_ => extractMap(lm.toList, ordering) == extractMap(lm.toList, ordering) - key) + } ensuring (_ => extractMap(lm.toList) == extractMap(lm.toList) - key) @opaque @inlineOnce @@ -1036,25 +1121,25 @@ object MutableHashMap { @opaque @inlineOnce @ghost - def lemmaInGenMapThenGetPairDefined[K, V](lm: ListLongMap[List[(K, V)]], key: K, hashF: Hashable[K], ordering: Ordering[K]): Unit = { + def lemmaInGenMapThenGetPairDefined[K, V](lm: ListLongMap[List[(K, V)]], key: K, hashF: Hashable[K]): Unit = { require(lm.toList.forall((k, v) => noDuplicateKeys(v))) require(allKeysSameHashInMap(lm, hashF)) - require(extractMap(lm.toList, ordering).contains(key)) + require(extractMap(lm.toList).contains(key)) decreases(lm.toList.size) - lemmaInGenMapThenLongMapContainsHash(lm, key, hashF, ordering) + lemmaInGenMapThenLongMapContainsHash(lm, key, hashF) ListLongMapLemmas.lemmaGetValueImpliesTupleContained(lm, hashF.hash(key), lm.apply(hashF.hash(key))) ListSpecs.forallContained(lm.toList, (k, v) => noDuplicateKeys(v), (hashF.hash(key), lm.apply(hashF.hash(key)))) if (getPair(lm.apply(hashF.hash(key)), key).isEmpty) { val l = lm.apply(hashF.hash(key)) - lemmaNotInItsHashBucketThenNotInMap(lm, key, hashF, ordering) + lemmaNotInItsHashBucketThenNotInMap(lm, key, hashF) check(false) } check(getPair(lm.apply(hashF.hash(key)), key).isDefined) } ensuring (_ => { - lemmaInGenMapThenLongMapContainsHash(lm, key, hashF, ordering) + lemmaInGenMapThenLongMapContainsHash(lm, key, hashF) ListLongMapLemmas.lemmaGetValueImpliesTupleContained(lm, hashF.hash(key), lm.apply(hashF.hash(key))) ListSpecs.forallContained(lm.toList, (k, v) => noDuplicateKeys(v), (hashF.hash(key), lm.apply(hashF.hash(key)))) getPair(lm.apply(hashF.hash(key)), key).isDefined @@ -1063,7 +1148,7 @@ object MutableHashMap { @opaque @inlineOnce @ghost - def lemmaNotInItsHashBucketThenNotInMap[K, V](lm: ListLongMap[List[(K, V)]], key: K, hashF: Hashable[K], ordering: Ordering[K]): Unit = { + def lemmaNotInItsHashBucketThenNotInMap[K, V](lm: ListLongMap[List[(K, V)]], key: K, hashF: Hashable[K]): Unit = { require(lm.toList.forall((k, v) => noDuplicateKeys(v))) require(allKeysSameHashInMap(lm, hashF)) require(lm.contains(hashF.hash(key))) @@ -1072,32 +1157,32 @@ object MutableHashMap { decreases(lm.toList.size) lm.toList match { case Cons(hd, tl) if hd._1 == hashF.hash(key) => - lemmaHashNotInLongMapThenNotInGenerated(lm.tail, key, hashF, ordering) - lemmaAddToMapFromBucketContainsIIFInAccOrL(hd._2, extractMap(tl, ordering), key) + lemmaHashNotInLongMapThenNotInGenerated(lm.tail, key, hashF) + lemmaAddToMapFromBucketContainsIIFInAccOrL(hd._2, extractMap(tl), key) case Cons(hd, tl) => assert(hd._1 != hashF.hash(key)) - lemmaNotSameHashThenCannotContainKey(lm, key, hd._1, hashF, ordering) - lemmaAddToMapFromBucketContainsIIFInAccOrL(hd._2, extractMap(tl, ordering), key) - lemmaNotInItsHashBucketThenNotInMap(lm.tail, key, hashF, ordering) + lemmaNotSameHashThenCannotContainKey(lm, key, hd._1, hashF) + lemmaAddToMapFromBucketContainsIIFInAccOrL(hd._2, extractMap(tl), key) + lemmaNotInItsHashBucketThenNotInMap(lm.tail, key, hashF) case Nil() => () } - } ensuring (_ => !extractMap(lm.toList, ordering).contains(key)) + } ensuring (_ => !extractMap(lm.toList).contains(key)) @opaque @inlineOnce @ghost - def lemmaInGenMapThenLongMapContainsHash[K, V](lm: ListLongMap[List[(K, V)]], key: K, hashF: Hashable[K], ordering: Ordering[K]): Unit = { + def lemmaInGenMapThenLongMapContainsHash[K, V](lm: ListLongMap[List[(K, V)]], key: K, hashF: Hashable[K]): Unit = { require(lm.toList.forall((k, v) => noDuplicateKeys(v))) require(allKeysSameHashInMap(lm, hashF)) - require(extractMap(lm.toList, ordering).contains(key)) + require(extractMap(lm.toList).contains(key)) decreases(lm.toList.size) val hash = hashF.hash(key) if (!lm.contains(hashF.hash(key))) { - lemmaHashNotInLongMapThenNotInGenerated(lm, key, hashF, ordering) - check(!extractMap(lm.toList, ordering).contains(key)) + lemmaHashNotInLongMapThenNotInGenerated(lm, key, hashF) + check(!extractMap(lm.toList).contains(key)) check(false) } @@ -1106,7 +1191,7 @@ object MutableHashMap { @opaque @inlineOnce @ghost - def lemmaHashNotInLongMapThenNotInGenerated[K, V](lm: ListLongMap[List[(K, V)]], key: K, hashF: Hashable[K], ordering: Ordering[K]): Unit = { + def lemmaHashNotInLongMapThenNotInGenerated[K, V](lm: ListLongMap[List[(K, V)]], key: K, hashF: Hashable[K]): Unit = { require(lm.toList.forall((k, v) => noDuplicateKeys(v))) require(allKeysSameHashInMap(lm, hashF)) require(!lm.contains(hashF.hash(key))) @@ -1114,13 +1199,13 @@ object MutableHashMap { lm.toList match { case Cons(hd, tl) => - lemmaAddToMapFromBucketContainsIIFInAccOrL(hd._2, extractMap(tl, ordering), key) - lemmaNotSameHashThenCannotContainKey(lm, key, hd._1, hashF, ordering) - lemmaHashNotInLongMapThenNotInGenerated(lm.tail, key, hashF, ordering) + lemmaAddToMapFromBucketContainsIIFInAccOrL(hd._2, extractMap(tl), key) + lemmaNotSameHashThenCannotContainKey(lm, key, hd._1, hashF) + lemmaHashNotInLongMapThenNotInGenerated(lm.tail, key, hashF) case Nil() => () } - } ensuring (_ => !extractMap(lm.toList, ordering).contains(key)) + } ensuring (_ => !extractMap(lm.toList).contains(key)) @opaque @inlineOnce @@ -1162,30 +1247,34 @@ object MutableHashMap { val res = addToMapMapFromBucket(tl, acc + (k, v)) lemmaAddToMapFromBucketTlPlusHeadIsSameAsList(t, tl, newAcc) - check(addToMapMapFromBucket(tl, newAcc) == addToMapMapFromBucket(l, acc)) - check(addToMapMapFromBucket(Cons(t, tl), newAcc) == addToMapMapFromBucket(tl, newAcc) + t) - check(addToMapMapFromBucket(Cons(t, tl), newAcc) == addToMapMapFromBucket(tl, newAcc + t)) - check(addToMapMapFromBucket(Cons(t, tl), newAcc) == addToMapMapFromBucket(tl, acc + (k, v) + t)) - check(addToMapMapFromBucket(Cons(t, l), acc) == addToMapMapFromBucket(l, acc + t)) - check(addToMapMapFromBucket(Cons(t, l), acc) == addToMapMapFromBucket(tl, acc + t + (k, v))) + check(addToMapMapFromBucket(tl, newAcc).eq(addToMapMapFromBucket(l, acc))) + check(addToMapMapFromBucket(Cons(t, tl), newAcc).eq(addToMapMapFromBucket(tl, newAcc) + t)) + check(addToMapMapFromBucket(Cons(t, tl), newAcc).eq(addToMapMapFromBucket(tl, newAcc + t))) + check(addToMapMapFromBucket(Cons(t, tl), newAcc).eq(addToMapMapFromBucket(tl, acc + (k, v) + t))) + check(addToMapMapFromBucket(Cons(t, l), acc).eq(addToMapMapFromBucket(l, acc + t))) + check(addToMapMapFromBucket(Cons(t, l), acc).eq(addToMapMapFromBucket(tl, acc + t + (k, v)))) ListMapLemmas.addCommutativeForDiffKeys(acc, k, v, t._1, t._2) - check(addToMapMapFromBucket(Cons(t, l), acc) == addToMapMapFromBucket(tl, acc + (k, v) + t)) - check(addToMapMapFromBucket(Cons(t, l), acc) == addToMapMapFromBucket(l, acc) + t) - + ListMapLemmas.addCommutativeForDiffKeys(acc, t._1, t._2, k, v) + check((acc + t + (k, v)).eq(acc + (k, v) + t)) + lemmaAddToMapFromBucketPreservesEquivalence(acc + (k, v) + t, acc + t + (k, v), tl) + check(addToMapMapFromBucket(Cons(t, l), acc).eq(addToMapMapFromBucket(tl, acc + (k, v) + t))) + check(addToMapMapFromBucket(Cons(t, l), acc).eq(addToMapMapFromBucket(l, acc) + t)) } - } ensuring (_ => addToMapMapFromBucket(Cons(t, l), acc) == addToMapMapFromBucket(l, acc) + t) + } ensuring (_ => addToMapMapFromBucket(Cons(t, l), acc).eq(addToMapMapFromBucket(l, acc) + t)) + + @opaque @inlineOnce @ghost - def lemmaExtractMapPreservesMapping[K, V](lm: ListLongMap[List[(K, V)]], key: K, value: V, hashF: Hashable[K], ordering: Ordering[K]): Unit = { + def lemmaExtractMapPreservesMapping[K, V](lm: ListLongMap[List[(K, V)]], key: K, value: V, hashF: Hashable[K]): Unit = { require(lm.toList.forall((k, v) => noDuplicateKeys(v))) require(allKeysSameHashInMap(lm, hashF)) - require(extractMap(lm.toList, ordering).contains(key)) + require(extractMap(lm.toList).contains(key)) require({ - lemmaInGenericMapThenInLongMap(lm, key, hashF, ordering) - lemmaInLongMapThenContainsKeyBiggerList(lm, key, hashF, ordering) + lemmaInGenericMapThenInLongMap(lm, key, hashF) + lemmaInLongMapThenContainsKeyBiggerList(lm, key, hashF) getValue(lm.toList, key) == value }) decreases(lm.toList.size) @@ -1195,24 +1284,24 @@ object MutableHashMap { if (containsKey(hd._2, key)) { ListSpecs.forallContained(lm.toList, (k, v) => allKeysSameHash(v, k, hashF), (hd._1, hd._2)) ListSpecs.forallContained(hd._2, p => hashF.hash(p._1) == hd._1, (key, value)) - lemmaHashNotInLongMapThenNotInGenerated(lm.tail, key, hashF, ordering) - lemmaAddToMapFromBucketMaintainsMapping(hd._2, extractMap(tl, ordering), key, value) + lemmaHashNotInLongMapThenNotInGenerated(lm.tail, key, hashF) + lemmaAddToMapFromBucketMaintainsMapping(hd._2, extractMap(tl), key, value) } else { check(!containsKey(hd._2, key)) if (!lm.tail.contains(hashF.hash(key))) { - lemmaHashNotInLongMapThenNotInGenerated(lm.tail, key, hashF, ordering) + lemmaHashNotInLongMapThenNotInGenerated(lm.tail, key, hashF) } - lemmaInLongMapThenContainsKeyBiggerList(lm, key, hashF, ordering) - lemmaListContainsThenExtractedMapContains(lm.tail, key, hashF, ordering) - lemmaExtractMapPreservesMapping(lm.tail, key, value, hashF, ordering) - lemmaAddToMapFromBucketMaintainsMapping(hd._2, extractMap(tl, ordering), key, value) + lemmaInLongMapThenContainsKeyBiggerList(lm, key, hashF) + lemmaListContainsThenExtractedMapContains(lm.tail, key, hashF) + lemmaExtractMapPreservesMapping(lm.tail, key, value, hashF) + lemmaAddToMapFromBucketMaintainsMapping(hd._2, extractMap(tl), key, value) } case Nil() => () } - } ensuring (_ => extractMap(lm.toList, ordering).apply(key) == value) + } ensuring (_ => extractMap(lm.toList).apply(key) == value) @opaque @inlineOnce @@ -1252,7 +1341,7 @@ object MutableHashMap { @opaque @inlineOnce @ghost - def lemmaInLongMapThenContainsKeyBiggerList[K, V](lm: ListLongMap[List[(K, V)]], key: K, hashF: Hashable[K], ordering: Ordering[K]): Unit = { + def lemmaInLongMapThenContainsKeyBiggerList[K, V](lm: ListLongMap[List[(K, V)]], key: K, hashF: Hashable[K]): Unit = { require(lm.toList.forall((k, v) => noDuplicateKeys(v))) require(allKeysSameHashInMap(lm, hashF)) require(lm.contains(hashF.hash(key))) @@ -1263,12 +1352,12 @@ object MutableHashMap { }) decreases(lm.toList.size) lm.toList match - case Cons(hd, tl) if hd._1 == hashF.hash(key) => lemmaGetPairDefinedThenContainsKey(hd._2, key, hashF, ordering) + case Cons(hd, tl) if hd._1 == hashF.hash(key) => lemmaGetPairDefinedThenContainsKey(hd._2, key, hashF) case Cons(hd, tl) => assert(hashF.hash(key) != hd._1) - lemmaNotSameHashThenCannotContainKey(lm, key, hd._1, hashF, ordering) + lemmaNotSameHashThenCannotContainKey(lm, key, hd._1, hashF) assert(!containsKey(hd._2, key)) - lemmaInLongMapThenContainsKeyBiggerList(lm.tail, key, hashF, ordering) + lemmaInLongMapThenContainsKeyBiggerList(lm.tail, key, hashF) check(containsKeyBiggerList(lm.toList, key)) case Nil() => check(containsKeyBiggerList(lm.toList, key)) @@ -1277,7 +1366,7 @@ object MutableHashMap { @opaque @inlineOnce @ghost - def lemmaListContainsThenExtractedMapContains[K, V](lm: ListLongMap[List[(K, V)]], key: K, hashF: Hashable[K], ordering: Ordering[K]): Unit = { + def lemmaListContainsThenExtractedMapContains[K, V](lm: ListLongMap[List[(K, V)]], key: K, hashF: Hashable[K]): Unit = { require(lm.toList.forall((k, v) => noDuplicateKeys(v))) require(allKeysSameHashInMap(lm, hashF)) require(containsKeyBiggerList(lm.toList, key)) @@ -1286,32 +1375,32 @@ object MutableHashMap { lm.toList match case Cons(hd, tl) if containsKey(hd._2, key) => { val v = getValue(lm.toList, key) - lemmaInPairListHeadThenGetValueInTuple(lm, key, v, hashF, ordering) + lemmaInPairListHeadThenGetValueInTuple(lm, key, v, hashF) check(hd._2.contains((key, v))) - check(extractMap(lm.toList, ordering) == addToMapMapFromBucket(hd._2, extractMap(tl, ordering))) - ListSpecs.forallContained(hd._2, p => addToMapMapFromBucket(hd._2, extractMap(tl, ordering)).contains(p._1), (key, v)) + check(extractMap(lm.toList) == addToMapMapFromBucket(hd._2, extractMap(tl))) + ListSpecs.forallContained(hd._2, p => addToMapMapFromBucket(hd._2, extractMap(tl)).contains(p._1), (key, v)) } case Cons(hd, tl) => { - assert(extractMap(lm.toList, ordering) == addToMapMapFromBucket(hd._2, extractMap(tl, ordering))) - lemmaInBiggerListButNotHeadThenTail(lm, key, hashF, ordering) + assert(extractMap(lm.toList) == addToMapMapFromBucket(hd._2, extractMap(tl))) + lemmaInBiggerListButNotHeadThenTail(lm, key, hashF) assert(containsKeyBiggerList(tl, key)) - lemmaListContainsThenExtractedMapContains(lm.tail, key, hashF, ordering) - assert(extractMap(tl, ordering).contains(key)) - val v = extractMap(tl, ordering).get(key).get - val m = extractMap(tl, ordering) - ListMapLemmas.lemmaGetValueImpliesTupleContained(extractMap(tl, ordering), key, v) - // lemmaGetValueInExtractMapToList(tl, key, v, hashF, ordering) - check(extractMap(tl, ordering).toList.contains((key, v))) - ListSpecs.forallContained(extractMap(tl, ordering).toList, p => extractMap(lm.toList, ordering).contains(p._1), (key, v)) + lemmaListContainsThenExtractedMapContains(lm.tail, key, hashF) + assert(extractMap(tl).contains(key)) + val v = extractMap(tl).get(key).get + val m = extractMap(tl) + ListMapLemmas.lemmaGetValueImpliesTupleContained(extractMap(tl), key, v) + // lemmaGetValueInExtractMapToList(tl, key, v, hashF) + check(extractMap(tl).toList.contains((key, v))) + ListSpecs.forallContained(extractMap(tl).toList, p => extractMap(lm.toList).contains(p._1), (key, v)) } case Nil() => () - } ensuring (_ => extractMap(lm.toList, ordering).contains(key)) + } ensuring (_ => extractMap(lm.toList).contains(key)) @opaque @inlineOnce @ghost - def lemmaInPairListHeadThenGetValueInTuple[K, V](lm: ListLongMap[List[(K, V)]], key: K, v: V, hashF: Hashable[K], ordering: Ordering[K]): Unit = { + def lemmaInPairListHeadThenGetValueInTuple[K, V](lm: ListLongMap[List[(K, V)]], key: K, v: V, hashF: Hashable[K]): Unit = { require(lm.toList.forall((k, v) => noDuplicateKeys(v))) require(allKeysSameHashInMap(lm, hashF)) require(containsKeyBiggerList(lm.toList, key) && containsKey(lm.toList.head._2, key)) @@ -1322,16 +1411,16 @@ object MutableHashMap { @opaque @inlineOnce @ghost - def lemmaGetValueEquivToGetPair[K, V](lm: ListLongMap[List[(K, V)]], key: K, v: V, hashF: Hashable[K], ordering: Ordering[K]): Unit = { + def lemmaGetValueEquivToGetPair[K, V](lm: ListLongMap[List[(K, V)]], key: K, v: V, hashF: Hashable[K]): Unit = { require(lm.toList.forall((k, v) => noDuplicateKeys(v))) require(allKeysSameHashInMap(lm, hashF)) require(containsKeyBiggerList(lm.toList, key)) require({ - lemmaListContainsThenExtractedMapContains(lm, key, hashF, ordering) - lemmaInGenMapThenLongMapContainsHash(lm, key, hashF, ordering) + lemmaListContainsThenExtractedMapContains(lm, key, hashF) + lemmaInGenMapThenLongMapContainsHash(lm, key, hashF) ListLongMapLemmas.lemmaGetValueImpliesTupleContained(lm, hashF.hash(key), lm.apply(hashF.hash(key))) ListSpecs.forallContained(lm.toList, (k, v) => noDuplicateKeys(v), (hashF.hash(key), lm.apply(hashF.hash(key)))) - lemmaInGenMapThenGetPairDefined(lm, key, hashF, ordering) + lemmaInGenMapThenGetPairDefined(lm, key, hashF) getPair(lm.apply(hashF.hash(key)), key).get._2 == v }) decreases(lm.toList.size) @@ -1340,15 +1429,15 @@ object MutableHashMap { case Cons(hd, tl) if hd._1 == hashF.hash(key) => ListSpecs.forallContained(lm.toList, (k, v) => allKeysSameHash(v, k, hashF), (hd._1, hd._2)) if (!containsKey(hd._2, key)) { - lemmaNotInItsHashBucketThenNotInMap(lm, key, hashF, ordering) - lemmaListContainsThenExtractedMapContains(lm, key, hashF, ordering) + lemmaNotInItsHashBucketThenNotInMap(lm, key, hashF) + lemmaListContainsThenExtractedMapContains(lm, key, hashF) check(false) } case Cons(hd, tl) => - lemmaNotSameHashThenCannotContainKey(lm, key, hd._1, hashF, ordering) + lemmaNotSameHashThenCannotContainKey(lm, key, hd._1, hashF) assert(!containsKey(hd._2, key)) - lemmaInBiggerListButNotHeadThenTail(lm, key, hashF, ordering) - lemmaGetValueEquivToGetPair(lm.tail, key, v, hashF, ordering) + lemmaInBiggerListButNotHeadThenTail(lm, key, hashF) + lemmaGetValueEquivToGetPair(lm.tail, key, v, hashF) case Nil() => () } @@ -1356,7 +1445,7 @@ object MutableHashMap { @opaque @inlineOnce @ghost - def lemmaInBiggerListButNotHeadThenTail[K, V](lm: ListLongMap[List[(K, V)]], key: K, hashF: Hashable[K], ordering: Ordering[K]): Unit = { + def lemmaInBiggerListButNotHeadThenTail[K, V](lm: ListLongMap[List[(K, V)]], key: K, hashF: Hashable[K]): Unit = { require(lm.toList.forall((k, v) => noDuplicateKeys(v))) require(allKeysSameHashInMap(lm, hashF)) require(containsKeyBiggerList(lm.toList, key)) @@ -1368,13 +1457,13 @@ object MutableHashMap { @opaque @inlineOnce @ghost - def lemmaGetPairDefinedThenContainsKey[K, V](l: List[(K, V)], key: K, hashF: Hashable[K], ordering: Ordering[K]): Unit = { + def lemmaGetPairDefinedThenContainsKey[K, V](l: List[(K, V)], key: K, hashF: Hashable[K]): Unit = { require(noDuplicateKeys(l)) require(getPair(l, key).isDefined) decreases(l) l match case Cons(hd, tl) if hd._1 == key => () - case Cons(_, tl) => lemmaGetPairDefinedThenContainsKey(tl, key, hashF, ordering) + case Cons(_, tl) => lemmaGetPairDefinedThenContainsKey(tl, key, hashF) case Nil() => () } ensuring (_ => containsKey(l, key)) @@ -1382,7 +1471,7 @@ object MutableHashMap { @opaque @inlineOnce @ghost - def lemmaNotSameHashThenCannotContainKey[K, V](lm: ListLongMap[List[(K, V)]], key: K, hash: Long, hashF: Hashable[K], ordering: Ordering[K]): Unit = { + def lemmaNotSameHashThenCannotContainKey[K, V](lm: ListLongMap[List[(K, V)]], key: K, hash: Long, hashF: Hashable[K]): Unit = { require(lm.toList.forall((k, v) => noDuplicateKeys(v))) require(allKeysSameHashInMap(lm, hashF)) // require(lm.contains(hashF.hash(key))) @@ -1414,3 +1503,4 @@ object MutableHashMap { } ensuring (_ => !l.contains((key, v))) } + diff --git a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/chassot/MutableLongMap.scala b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/chassot/MutableLongMap.scala index 3b0d9c61..a7c83429 100644 --- a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/chassot/MutableLongMap.scala +++ b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/chassot/MutableLongMap.scala @@ -1204,7 +1204,7 @@ object MutableLongMap { if (x >= MAX_ITER) Intermediate(true, ee, x) else if (q == k || q + q == 0) Intermediate(false, ee, x) else - seekKeyOrZeroOrLongMinValue(x + 1, nextIndex(ee, x, mask)) + seekKeyOrZeroOrLongMinValue(x + 1, nextIndex(ee, x + 1, mask)) } ensuring (res => (res match { case Intermediate(undefined, index, resx) if (undefined) => resx >= MAX_ITER @@ -1243,7 +1243,7 @@ object MutableLongMap { else seekKeyOrZeroReturnVacant( x + 1, - nextIndex(ee, x, mask), + nextIndex(ee, x + 1, mask), vacantSpotIndex ) @@ -5493,7 +5493,7 @@ object MutableLongMap { i, j, x + 1, - nextIndex(index, x, mask), + nextIndex(index, x + 1, mask), vacantIndex ) } @@ -5613,7 +5613,7 @@ object MutableLongMap { i, j, x + 1, - nextIndex(index, x, mask), + nextIndex(index, x + 1, mask), resX, resIndex ) @@ -5731,7 +5731,7 @@ object MutableLongMap { i, j, x + 1, - nextIndex(index, x, mask), + nextIndex(index, x + 1, mask), intermediateBeforeX, intermediateBeforeIndex, intermediateAfterX, @@ -5788,7 +5788,7 @@ object MutableLongMap { i, j, x + 1, - nextIndex(index, x, mask), + nextIndex(index, x + 1, mask), vacantBefore, vacantAfter ) @@ -5951,7 +5951,7 @@ object MutableLongMap { i, j, x + 1, - nextIndex(index, x, mask), + nextIndex(index, x + 1, mask), intermediateBeforeX, intermediateBeforeIndex, intermediateAfterX, @@ -5963,7 +5963,7 @@ object MutableLongMap { i, j, x + 1, - nextIndex(index, x, mask), + nextIndex(index, x + 1, mask), intermediateBeforeX, intermediateBeforeIndex, intermediateAfterX, @@ -6214,14 +6214,14 @@ object MutableLongMap { k, j, x + 1, - nextIndex(index, x, mask), + nextIndex(index, x + 1, mask), vacantSpotIndex ) check( seekKeyOrZeroReturnVacant(x, index, vacantSpotIndex)(a(j), a, mask) == seekKeyOrZeroReturnVacant( x + 1, - nextIndex(index, x, mask), + nextIndex(index, x + 1, mask), vacantSpotIndex )( a(j), @@ -6467,7 +6467,7 @@ object MutableLongMap { i, k, j, - nextIndex(index, x, mask), + nextIndex(index, x + 1, mask), x + 1, resIndex, resX @@ -6572,7 +6572,7 @@ object MutableLongMap { i, k, j, - nextIndex(index, x, mask), + nextIndex(index, x + 1, mask), x + 1, vacantBefore, vacantAfter @@ -6698,7 +6698,7 @@ object MutableLongMap { intermediateAfter match { case Intermediate(undefined, indexIntermediateAfter, xIntermediateAfter) => { if (x < xIntermediateAfter) { - val nextIndexVal = nextIndex(index, x, mask) + val nextIndexVal = nextIndex(index, x + 1, mask) val nextX = x + 1 if (nextX <= resIntermediateX) { @@ -6939,7 +6939,7 @@ object MutableLongMap { i, k, j, - nextIndex(index, x, mask), + nextIndex(index, x + 1, mask), x + 1, resIndex, resX @@ -7131,12 +7131,12 @@ object MutableLongMap { seekKeyOrZeroOrLongMinValue(x, index)(k, a, mask) == seekKeyOrZeroOrLongMinValue( x + 1, - nextIndex(index, x, mask) + nextIndex(index, x + 1, mask) )(k, a, mask) ) seekKeyOrZeroOrLongMinValue( x + 1, - nextIndex(index, x, mask) + nextIndex(index, x + 1, mask) )(k, a, mask) match { case Intermediate(undefined, tempIndex, tempX) => assert(tempX >= x + 1) case _ => check(false) @@ -7154,7 +7154,7 @@ object MutableLongMap { mask ) == seekKeyOrZeroOrLongMinValue( x + 1, - nextIndex(index, x, mask) + nextIndex(index, x + 1, mask) )(k, a, mask) ) check( @@ -7164,7 +7164,7 @@ object MutableLongMap { mask ) == seekKeyOrZeroOrLongMinValue( x + 1, - nextIndex(index, x, mask) + nextIndex(index, x + 1, mask) )( k, a.updated(i, k), @@ -7176,7 +7176,7 @@ object MutableLongMap { i, k, x + 1, - nextIndex(index, x, mask), + nextIndex(index, x + 1, mask), resIndex, resX ) @@ -7247,7 +7247,7 @@ object MutableLongMap { } else { seekKeyOrZeroOrLongMinValue( x + 1, - nextIndex(index, x, mask) + nextIndex(index, x + 1, mask) )(k, a, mask) match { case Intermediate(undefined, tempIndex, tempX) => assert(tempX >= x + 1) case _ => check(false) @@ -7263,7 +7263,7 @@ object MutableLongMap { i, k, x + 1, - nextIndex(index, x, mask), + nextIndex(index, x + 1, mask), resIndex, resX ) diff --git a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/chassot/Ordering.scala b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/chassot/Ordering.scala deleted file mode 100644 index adbd7149..00000000 --- a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/chassot/Ordering.scala +++ /dev/null @@ -1,27 +0,0 @@ -/** Author: Samuel Chassot -*/ - -package ch.epfl.chassot - -import stainless.annotation._ - - -trait Ordering[T]: - def compare(x: T, y: T): Int - - @law def inverse(x: T, y: T): Boolean = - sign(compare(x, y)) == -sign(compare(y, x)) - - @law def transitive(x: T, y: T, z: T): Boolean = - if (compare(x, y) > 0 && compare(y, z) > 0) then compare(x, z) > 0 else if (compare(x, y) < 0 && compare(y, z) < 0) then compare(x, z) < 0 else true - - @law def consistent(x: T, y: T, z: T): Boolean = - if compare(x, y) == 0 then sign(compare(x, z)) == sign(compare(y, z)) else true - - @law def equalsMeansEquals(x: T, y: T): Boolean = - (compare(x, y) == 0) == (x == y) - - final def sign(x: Int): BigInt = - if x < 0 then -1 else if x > 0 then 1 else 0 - -end Ordering \ No newline at end of file diff --git a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/Main.scala b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/Main.scala index 1feaa88a..8babfcab 100644 --- a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/Main.scala +++ b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/Main.scala @@ -1,7 +1,5 @@ package ch.epfl.lexer -import ch.epfl.chassot.Ordering -import ch.epfl.chassot.Hashable import ch.epfl.chassot.MutableHashMap import ch.epfl.lexer.VerifiedRegex._ import ch.epfl.lexer.VerifiedRegexMatcher._ @@ -10,6 +8,7 @@ import ch.epfl.benchmark.RegexUtils._ import stainless.annotation._ import stainless.lang._ import stainless.collection._ +import ch.epfl.chassot.Hashable object Main { def main(args: Array[String]): Unit = { @@ -18,7 +17,7 @@ object Main { } def testRegex(): Unit = { - val cache: Cache[Char] = Cache(MutableHashMap.getEmptyHashMap(_ => EmptyLang(), KeyHashable, KeyOrdering)) + val cache: Cache[Char] = Cache(MutableHashMap.getEmptyHashMap(_ => EmptyLang(), KeyHashable)) val r1 = ("a".r + "b".r).* println(f"r1 = ${r1}") println(f"list = ${toStainlessList("ab".toCharArray().toList)}") @@ -42,17 +41,6 @@ object KeyHashable extends Hashable[(Regex[Char], Char)] { override def hash(x: (Regex[Char], Char)): Long = CharHashable.hash(x._2) + RegexHashable(CharHashable).hash(x._1) } -object KeyOrdering extends Ordering[(Regex[Char], Char)] { - override def compare(x: (Regex[Char], Char), y: (Regex[Char], Char)): Int = { - val c = CharOrdering.compare(x._2, y._2) - if (c == 0) { - RegexOrdering(CharOrdering).compare(x._1, y._1) - } else { - c - } - } -} - object CharHashable extends Hashable[Char] { override def hash(x: Char): Long = x.toLong } diff --git a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedLexer.scala b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedLexer.scala index 5e1691bd..a26bfbd0 100644 --- a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedLexer.scala +++ b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedLexer.scala @@ -18,6 +18,7 @@ object MainLexer { import stainless.io.StdOut.println import stainless.io.State import VerifiedLexer._ + @extern def main(args: Array[String]): Unit = { diff --git a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedRegex.scala b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedRegex.scala index 55e6c6a9..436ee0f5 100644 --- a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedRegex.scala +++ b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedRegex.scala @@ -12,7 +12,6 @@ import ch.epfl.chassot.MutableLongMap._ import ch.epfl.chassot.ListLongMap import ch.epfl.chassot.ListMap import ch.epfl.chassot.MutableHashMap._ -import ch.epfl.chassot.Ordering import ch.epfl.chassot.Hashable import stainless.lang.StaticChecks._ @@ -21,9 +20,11 @@ object Memoisation { import VerifiedRegexMatcher._ @ghost def validCacheMap[C](m: HashMap[(Regex[C], C), Regex[C]]): Boolean = { - m.valid && m.forall { case ((r, c), res) => - validRegex(r) && res == derivativeStep(r, c) - } + m.valid && m.map.forall(_ match { + case ((r, c), res) => + validRegex(r) && res == derivativeStep(r, c) + } + ) } @mutable @@ -37,14 +38,20 @@ object Memoisation { require(validCacheMap(cache)) require(validRegex(r)) if (cache.contains((r, c))) { - lemmaForallThenForAPair( - cache, - { case ((r, c), res) => + ListSpecs.forallContained(cache.map.toList, { + _ match { + case ((r, c), res) => validRegex(r) && res == derivativeStep(r, c) - }, - (r, c), - cache((r, c)) - ) + } + }, ((r, c), cache((r, c)))) + // lemmaForallThenForAPair( + // cache, + // { case ((r, c), res) => + // validRegex(r) && res == derivativeStep(r, c) + // }, + // (r, c), + // cache((r, c)) + // ) } } ensuring (_ => cache.contains((r, c)) ==> (derivativeStep(r, c) == cache((r, c)))) @@ -70,22 +77,23 @@ object Memoisation { require(validRegex(r)) require(res == derivativeStep(r, c)) - ghostExpr( - lemmaUpdateValidPairMaintainsForall( - cache, - { case ((r, c), res) => - validRegex(r) && res == derivativeStep(r, c) - }, - (r, c), - res - ) - ) + // ghostExpr( + // lemmaUpdateValidPairMaintainsForall( + // cache, + // { case ((r, c), res) => + // validRegex(r) && res == derivativeStep(r, c) + // }, + // (r, c), + // res + // ) + // ) val _ = cache.update((r, c), res) } ensuring (_ => validCacheMap(this.cache)) } } + object VerifiedRegex { abstract sealed class Regex[C] {} @@ -185,7 +193,7 @@ object VerifiedRegex { } // case class RegexHashable[C](cHashable: Hashable[C]) extends Hashable[Regex[C]] { - // override def hash(r: Regex[C]): Long = { + // def hash(r: Regex[C]): Long = { // r match { // case ElementMatch(c) => 2L * cHashable.hash(c) // case Star(rInner) => 3L * hash(rInner) From ab43930e39b4470c7fc90200b7acb02512e707fb Mon Sep 17 00:00:00 2001 From: Samuel Chassot Date: Fri, 26 Jul 2024 10:30:13 +0200 Subject: [PATCH 14/78] add new lemmas in ListMaps for forall --- .../scala/ch/epfl/chassot/ListLongMap.scala | 21 ++++++++++++++++++ .../main/scala/ch/epfl/chassot/ListMap.scala | 22 +++++++++++++++++++ 2 files changed, 43 insertions(+) diff --git a/data-structures/maps/mutablemaps/src/main/scala/ch/epfl/chassot/ListLongMap.scala b/data-structures/maps/mutablemaps/src/main/scala/ch/epfl/chassot/ListLongMap.scala index b8cc322a..9a512bc4 100644 --- a/data-structures/maps/mutablemaps/src/main/scala/ch/epfl/chassot/ListLongMap.scala +++ b/data-structures/maps/mutablemaps/src/main/scala/ch/epfl/chassot/ListLongMap.scala @@ -283,6 +283,27 @@ object TupleListOps { // ----------- LEMMAS ----------------------------------------------------- + @opaque + @inlineOnce + def lemmainsertStrictlySortedPreservesForall[B]( + l: List[(Long, B)], + key: Long, + value: B, + p: ((Long, B)) => Boolean + ): Unit = { + require(invariantList(l)) + require(l.forall(p)) + require(p((key, value))) + decreases(l) + + l match { + case Cons(head, tl) if (head._1 != key) => + lemmainsertStrictlySortedPreservesForall(tl, key, value, p) + case _ => () + } + + }.ensuring(_ => insertStrictlySorted(l, key, value).forall(p)) + @opaque @inlineOnce def lemmaInsertAndRemoveStrictlySortedCommutative[B]( diff --git a/data-structures/maps/mutablemaps/src/main/scala/ch/epfl/chassot/ListMap.scala b/data-structures/maps/mutablemaps/src/main/scala/ch/epfl/chassot/ListMap.scala index bd3a4d7a..f6d8eba1 100644 --- a/data-structures/maps/mutablemaps/src/main/scala/ch/epfl/chassot/ListMap.scala +++ b/data-structures/maps/mutablemaps/src/main/scala/ch/epfl/chassot/ListMap.scala @@ -349,6 +349,28 @@ object TupleListOpsGenK { // ----------- LEMMAS ----------------------------------------------------- + @opaque + @inlineOnce + def lemmainsertNoDuplicatedKeysPreservesForall[K, B]( + l: List[(K, B)], + key: K, + value: B, + p: ((K, B)) => Boolean + ): Unit = { + require(invariantList(l)) + require(l.forall(p)) + require(p((key, value))) + decreases(l) + + l match { + case Cons(head, tl) if (head._1 != key) => + lemmainsertNoDuplicatedKeysPreservesForall(tl, key, value, p) + case _ => () + } + + }.ensuring(_ => insertNoDuplicatedKeys(l, key, value).forall(p)) + + @opaque @inlineOnce def lemmaContainsTwoDifferentTuplesSameKeyImpossible[K, B]( From cf005c2356e23593135fe6cc1056b77e1d655b57 Mon Sep 17 00:00:00 2001 From: Samuel Chassot Date: Fri, 26 Jul 2024 15:00:18 +0200 Subject: [PATCH 15/78] Formally verified cache for regex --- .../main/scala/ch/epfl/benchmark/Utils.scala | 7 ++ .../main/scala/ch/epfl/chassot/ListMap.scala | 72 +++++++++-- .../ch/epfl/chassot/MutableHashMap.scala | 116 +++++++++++++++++- .../src/main/scala/ch/epfl/lexer/Main.scala | 87 +++++++------ .../scala/ch/epfl/lexer/VerifiedRegex.scala | 64 ++++------ 5 files changed, 258 insertions(+), 88 deletions(-) diff --git a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/benchmark/Utils.scala b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/benchmark/Utils.scala index e5774b87..75f0f37a 100644 --- a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/benchmark/Utils.scala +++ b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/benchmark/Utils.scala @@ -23,4 +23,11 @@ object RegexUtils { extension (s: String) infix def ~ (s2: String): Regex[Char] = r(s) ~ r(s2) extension (s: String) def * : Regex[Char] = r(s).* extension (s: String) def anyOf: Regex[Char] = s.toCharArray().toList.foldRight[Regex[Char]](EmptyLang())((c, acc) => Union(ElementMatch(c), acc)) + extension (s: String) def toStainless: stainless.collection.List[Char] = toStainlessList(s.toCharArray().toList) + + + def toStainlessList(l: scala.collection.immutable.List[Char]): stainless.collection.List[Char] = l match { + case l: scala.collection.immutable.List[Char] if l.isEmpty => stainless.collection.Nil[Char]() + case l: scala.collection.immutable.List[Char] => stainless.collection.Cons(l.head, toStainlessList(l.tail)) + } } diff --git a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/chassot/ListMap.scala b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/chassot/ListMap.scala index 16fe67da..84dfe2e4 100644 --- a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/chassot/ListMap.scala +++ b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/chassot/ListMap.scala @@ -17,7 +17,7 @@ import scala.collection.mutable // import OptimisedChecks.* case class ListMap[K, B](toList: List[(K, B)]) { - require(TupleListOpsGenK.noDuplicatedKeys(toList)) + require(TupleListOpsGenK.invariantList(toList)) def isEmpty: Boolean = toList.isEmpty @@ -107,8 +107,8 @@ case class ListMap[K, B](toList: List[(K, B)]) { case Cons(key, rest) => (this - key) -- rest } } - @inline - def forall(p: ((K, B)) => Boolean): Boolean = { + + inline def forall(p: ((K, B)) => Boolean): Boolean = { toList.forall(p) } } @@ -116,7 +116,7 @@ case class ListMap[K, B](toList: List[(K, B)]) { object TupleListOpsGenK { // @inline - def invariantList[K, B](l: List[(K, B)]): Boolean = { + inline def invariantList[K, B](l: List[(K, B)]): Boolean = { noDuplicatedKeys(l) } @@ -349,6 +349,54 @@ object TupleListOpsGenK { // ----------- LEMMAS ----------------------------------------------------- + @opaque + @inlineOnce + def lemmaForallSubset[K, B]( + l1: List[(K, B)], + l2: List[(K, B)], + p: ((K, B)) => Boolean + ): Unit = { + require(invariantList(l1)) + require(invariantList(l2)) + require(l2.forall(p) && l1.content.subsetOf(l2.content)) + decreases(l1) + + l1 match { + case Cons(head, tl) => + ListSpecs.subsetContains(l1, l2) + ListSpecs.forallContained(l1, l2.contains , head) + ListSpecs.forallContained(l2, p, head) + assert(l2.contains(head)) + lemmaForallSubset(tl, l2, p) + assert(tl.forall(p)) + assert(p(head)) + case Nil() => () + } + }.ensuring(_ => l1.forall(p)) + + + @opaque + @inlineOnce + def lemmaInsertNoDuplicatedKeysPreservesForall[K, B]( + l: List[(K, B)], + key: K, + value: B, + p: ((K, B)) => Boolean + ): Unit = { + require(invariantList(l)) + require(l.forall(p)) + require(p((key, value))) + decreases(l) + + l match { + case Cons(head, tl) if (head._1 != key) => + lemmaInsertNoDuplicatedKeysPreservesForall(tl, key, value, p) + case _ => () + } + + }.ensuring(_ => insertNoDuplicatedKeys(l, key, value).forall(p)) + + @opaque @inlineOnce def lemmaContainsTwoDifferentTuplesSameKeyImpossible[K, B]( @@ -422,7 +470,7 @@ object TupleListOpsGenK { case Nil() => () case Cons(hd, tl) => lemmaSubseqRefl(tl) } - } ensuring (_ => ListSpecs.subseq(l, l)) + }.ensuring (_ => ListSpecs.subseq(l, l)) @opaque @inlineOnce @@ -440,7 +488,7 @@ object TupleListOpsGenK { case _ => () } - } ensuring (_ => keys.forall(k => containsKey(Cons(other, l), k))) + }.ensuring (_ => keys.forall(k => containsKey(Cons(other, l), k))) @opaque @inlineOnce @@ -892,7 +940,7 @@ object TupleListOpsGenK { case _ => () } - } ensuring (_ => + }.ensuring (_ => l.content ++ Set((newKey, newValue)) == insertNoDuplicatedKeys( l, newKey, @@ -1232,7 +1280,7 @@ object ListMapLemmas { } case Nil() => - } ensuring (_ => lm.toList.forall(p => lm.contains(p._1))) + }.ensuring (_ => lm.toList.forall(p => lm.contains(p._1))) @opaque @inlineOnce @@ -1247,7 +1295,7 @@ object ListMapLemmas { lemmaInsertPairStillContainsAll(lm, t, k, v) case Nil() => () } - } ensuring (_ => l.forall(p => (lm + (k, v)).contains(p._1))) + }.ensuring (_ => l.forall(p => (lm + (k, v)).contains(p._1))) @opaque @inlineOnce @@ -1263,7 +1311,7 @@ object ListMapLemmas { lemmaInsertPairStillContainsAllEq(lm, lm2, t, k, v) case Nil() => () } - } ensuring (_ => l.forall(p => (lm + (k, v)).contains(p._1)) && l.forall(p => (lm2).contains(p._1))) + }.ensuring (_ => l.forall(p => (lm + (k, v)).contains(p._1)) && l.forall(p => (lm2).contains(p._1))) @opaque @inlineOnce @@ -1294,7 +1342,7 @@ object ListMapLemmas { require(lm.get(a) == Some[B](b)) TupleListOpsGenK.lemmaGetValueByKeyImpliesContainsTuple(lm.toList, a, b) - } ensuring (_ => lm.toList.contains((a, b))) + }.ensuring (_ => lm.toList.contains((a, b))) @opaque def keysOfSound[K, B](@induct lm: ListMap[K, B], value: B): Unit = { @@ -1315,7 +1363,7 @@ object ListMapLemmas { key, value ) - } ensuring (_ => + }.ensuring (_ => lm.toList.content ++ Set( (key, value) ) == (lm + (key, value)).toList.content diff --git a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/chassot/MutableHashMap.scala b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/chassot/MutableHashMap.scala index 54f64dbc..43afacde 100644 --- a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/chassot/MutableHashMap.scala +++ b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/chassot/MutableHashMap.scala @@ -231,7 +231,7 @@ object MutableHashMap { def map: ListMap[K, V] = { require(valid) extractMap(underlying.v.map.toList) - } + }.ensuring(res => TupleListOpsGenK.invariantList(res.toList)) } @ghost @@ -337,6 +337,120 @@ object MutableHashMap { // ----------------- Lemmas ------------------------------------------------------------------------ + /** + * This lemma proves that a property `p` that holds for all pairs of the map, holds for a key and its value. + * + * Useful to build caches using this map. + * + * @param hm + * @param k + * @param p + */ + @opaque + @inlineOnce + @ghost + def lemmaForallPairsThenForLookup[K, V](hm: HashMap[K, V], k: K, p: ((K, V)) => Boolean): Unit = { + require(hm.valid) + require(hm.map.forall(p)) + require(hm.contains(k)) + + TupleListOpsGenK.lemmaGetValueByKeyImpliesContainsTuple(hm.map.toList, k, hm.apply(k)) + assert(hm.map.toList.contains((k, hm.apply(k)))) + assert(hm.map.toList.forall(p)) + ListSpecs.forallContained(hm.map.toList, p, (k, hm.apply(k))) + }.ensuring(_ => p((k, hm.apply(k)))) + + + /** + * This lemma proves that inserting a new pair preserves the property `p` that holds for all pairs of the map. + * + * Useful to build caches using this map. + * + * @param hm + * @param k + * @param v + * @param p + */ + @opaque + @inlineOnce + @ghost + def lemmaUpdatePreservesForallPairs[K, V](hm: HashMap[K, V], k: K, v: V, p: ((K, V)) => Boolean): Unit = { + require(hm.valid) + require(hm.map.forall(p)) + require(p((k, v))) + + val oldSnap = snapshot(hm) + val snap = snapshot(hm) + val oldMap = hm.map + val oldSize = hm.size + + val mapAfter = oldMap + (k, v) + + val success = snap.update(k, v) + + if(success) { + assert(snap.map.eq(mapAfter)) + TupleListOpsGenK.lemmaInsertNoDuplicatedKeysPreservesForall(oldMap.toList, k, v, p) + assert(mapAfter.forall(p)) + TupleListOpsGenK.lemmaForallSubset(snap.map.toList, mapAfter.toList, p) + } else { + assert(snap.map.eq(oldMap)) + } + + () + } ensuring (_ => { + val oldMap = snapshot(hm) + val afterUpdate = snapshot(hm) + afterUpdate.update(k, v) + afterUpdate.map.forall(p) + }) + + /** + * This lemma proves that removing a pair preserves the property `p` that holds for all pairs of the map. + * + * Useful to build caches using this map. + * + * @param hm + * @param k + * @param p + */ + @opaque + @inlineOnce + @ghost + def lemmaRemovePreservesForallPairs[K, V](hm: HashMap[K, V], k: K, p: ((K, V)) => Boolean): Unit = { + require(hm.valid) + require(hm.map.forall(p)) + + val oldSnap = snapshot(hm) + val snap = snapshot(hm) + val oldMap = hm.map + val oldSize = hm.size + + val mapAfter = oldMap - k + + val success = snap.remove(k) + + assert(snap.valid) + assert(TupleListOpsGenK.invariantList(snap.map.toList)) + + if(success) { + assert(snap.map.eq(mapAfter)) + assert(oldMap.forall(p)) + TupleListOpsGenK.lemmaForallSubset(mapAfter.toList, oldMap.toList, p) + TupleListOpsGenK.lemmaForallSubset(snap.map.toList, mapAfter.toList, p) + } else { + assert(snap.map.eq(oldMap)) + TupleListOpsGenK.lemmaForallSubset(mapAfter.toList, oldMap.toList, p) + } + + () + } ensuring (_ => { + val oldMap = snapshot(hm) + val afterUpdate = snapshot(hm) + afterUpdate.remove(k) + afterUpdate.map.forall(p) + }) + @opaque @inlineOnce @ghost diff --git a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/Main.scala b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/Main.scala index 8babfcab..bebd2f48 100644 --- a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/Main.scala +++ b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/Main.scala @@ -12,7 +12,9 @@ import ch.epfl.chassot.Hashable object Main { def main(args: Array[String]): Unit = { - testRegex() + RegexBenchmark.benchmark01() + RegexBenchmark.benchmark02() + RegexBenchmark.benchmark03() } } @@ -20,21 +22,17 @@ def testRegex(): Unit = { val cache: Cache[Char] = Cache(MutableHashMap.getEmptyHashMap(_ => EmptyLang(), KeyHashable)) val r1 = ("a".r + "b".r).* println(f"r1 = ${r1}") - println(f"list = ${toStainlessList("ab".toCharArray().toList)}") + println(f"list = ${"ab".toStainless}") println(f"matching a with r1 without cache: ${matchR(r1, Cons('a', Nil()))}") - println(f"matching a with r1: ${matchRMem(r1, toStainlessList("a".toCharArray().toList))(cache)}") - println(f"matching abababababababababbbababbababbbabab with r1: ${matchRMem(r1, toStainlessList("abababababababababbbababbababbbabab".toCharArray().toList))(cache)}") - println(f"matching abchihihi with r1: ${matchRMem(r1, toStainlessList("abchihihi".toCharArray().toList))(cache)}") + println(f"matching a with r1: ${matchRMem(r1, "a".toStainless)(cache)}") + println(f"matching abababababababababbbababbababbbabab with r1: ${matchRMem(r1, "abababababababababbbababbababbbabab".toStainless)(cache)}") + println(f"matching abchihihi with r1: ${matchRMem(r1, "abchihihi".toStainless)(cache)}") val r2 = "abcdedfghijklmnopqrstuvwxyz.".anyOf.+ ~ "@".r ~ "abcdedfghijklmnopqrstuvwxyz".anyOf.+ ~ ".".r ~ "abcdedfghijklmnopqrstuvwxyz".anyOf.+ println(f"r2 = ${r2}") val s21 = "samuel.chassot@gmail.com" - println(f"matching $s21 with r2: ${matchRMem(r2, toStainlessList(s21.toCharArray().toList))(cache)}") -} + println(f"matching $s21 with r2: ${matchRMem(r2, s21.toStainless)(cache)}") -def toStainlessList(l: scala.collection.immutable.List[Char]): stainless.collection.List[Char] = l match { - case l: scala.collection.immutable.List[Char] if l.isEmpty => stainless.collection.Nil[Char]() - case l: scala.collection.immutable.List[Char] => stainless.collection.Cons(l.head, toStainlessList(l.tail)) } object KeyHashable extends Hashable[(Regex[Char], Char)] { @@ -45,10 +43,6 @@ object CharHashable extends Hashable[Char] { override def hash(x: Char): Long = x.toLong } -object CharOrdering extends Ordering[Char] { - override def compare(x: Char, y: Char): Int = x - y -} - case class RegexHashable[C](hc: Hashable[C]) extends Hashable[Regex[C]] { override def hash(x: Regex[C]): Long = x match { case ElementMatch(c) => 2L * hc.hash(c) @@ -59,28 +53,47 @@ case class RegexHashable[C](hc: Hashable[C]) extends Hashable[Regex[C]] { } } -case class RegexOrdering[C](oc: Ordering[C]) extends Ordering[Regex[C]] { - override def compare(x: Regex[C], y: Regex[C]): Int = (x, y) match { - case (_, _) if x == y => 0 - case (ElementMatch(xc), ElementMatch(yc)) => oc.compare(xc, yc) - case (ElementMatch(_), _) => -1 - case (_, ElementMatch(_)) => 1 - case (Concat(lx, rx), Concat(ly, ry)) => - compare(lx, ly) match { - case 0 => compare(rx, ry) - case c => c - } - case (Concat(_, _), _) => -1 - case (_, Concat(_, _)) => 1 - case (Union(lx, rx), Union(ly, ry)) => - compare(lx, ly) match { - case 0 => compare(rx, ry) - case c => c - } - case (Union(_, _), _) => -1 - case (_, Union(_, _)) => 1 - case (Star(rx), Star(ry)) => compare(rx, ry) - case (Star(_), _) => -1 - case (_, Star(_)) => 1 + +object RegexBenchmark { + val cache: Cache[Char] = Cache(MutableHashMap.getEmptyHashMap(_ => EmptyLang(), KeyHashable)) + def benchmark01(): Unit = { + val r = ("a".r + "b".r).* + val s = "abababababababababbbababbababbbabab" + val match11 = matchRMem(r, s.toStainless)(cache) + println(s"Matching $s with r -> $match11") + assert(match11) + + val s2 = "abchihihi" + val match12 = matchRMem(r, s2.toStainless)(cache) + println(s"Matching $s2 with r -> $match12") + assert(!match12) } + + def benchmark02(): Unit = { + val r = "abcdedfghijklmnopqrstuvwxyz.".anyOf.+ ~ "@".r ~ "abcdedfghijklmnopqrstuvwxyz".anyOf.+ ~ ".".r ~ "abcdedfghijklmnopqrstuvwxyz".anyOf.+ + val s = "example.example@domain.com" + val match21 = matchRMem(r, s.toStainless)(cache) + println(s"Matching $s with r -> $match21") + assert(match21) + + val s2 = "example.example@domain" + val match22 = matchRMem(r, s2.toStainless)(cache) + println(s"Matching $s2 with r -> $match22") + assert(!match22) + } + + def benchmark03(): Unit = { + val r = ("a".r + "b".r).* + println(s"r = $r") + val s = "ababa" + val match31 = matchRMem(r, s.toStainless)(cache) + println(s"Matching $s with r -> $match31") + assert(match31) + + val s2 = "abbbabbabbababaaaaaaaaaaabababababababa" + val match32 = matchRMem(r, s2.toStainless)(cache) + println(s"Matching $s2 with r -> $match32") + assert(match32) + } + } diff --git a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedRegex.scala b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedRegex.scala index 436ee0f5..3b5ea46a 100644 --- a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedRegex.scala +++ b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedRegex.scala @@ -11,16 +11,21 @@ import stainless.proof._ import ch.epfl.chassot.MutableLongMap._ import ch.epfl.chassot.ListLongMap import ch.epfl.chassot.ListMap +import ch.epfl.chassot.TupleListOpsGenK import ch.epfl.chassot.MutableHashMap._ import ch.epfl.chassot.Hashable import stainless.lang.StaticChecks._ +import ch.epfl.chassot.TupleListOpsGenK.invariantList +import ch.epfl.chassot.MutableHashMap object Memoisation { import VerifiedRegex._ import VerifiedRegexMatcher._ @ghost def validCacheMap[C](m: HashMap[(Regex[C], C), Regex[C]]): Boolean = { - m.valid && m.map.forall(_ match { + m.valid && + TupleListOpsGenK.invariantList(m.map.toList) && // Why is this needed? Without it does not verify in update... + m.map.forall(_ match { case ((r, c), res) => validRegex(r) && res == derivativeStep(r, c) } @@ -38,21 +43,18 @@ object Memoisation { require(validCacheMap(cache)) require(validRegex(r)) if (cache.contains((r, c))) { - ListSpecs.forallContained(cache.map.toList, { - _ match { - case ((r, c), res) => - validRegex(r) && res == derivativeStep(r, c) + ghostExpr({ + MutableHashMap.lemmaForallPairsThenForLookup( + cache, + (r, c), { + _ match { + case ((r, c), res) => + validRegex(r) && res == derivativeStep(r, c) + } } - }, ((r, c), cache((r, c)))) - // lemmaForallThenForAPair( - // cache, - // { case ((r, c), res) => - // validRegex(r) && res == derivativeStep(r, c) - // }, - // (r, c), - // cache((r, c)) - // ) - } + ) + }) + } } ensuring (_ => cache.contains((r, c)) ==> (derivativeStep(r, c) == cache((r, c)))) def contains(r: Regex[C], c: C): Boolean = { @@ -66,6 +68,7 @@ object Memoisation { if (cache.contains((r, c))) { ghostExpr(lemmaIfInCacheThenValid(r, c)) + println("HIT") Some(cache((r, c))) } else { None() @@ -77,17 +80,15 @@ object Memoisation { require(validRegex(r)) require(res == derivativeStep(r, c)) - // ghostExpr( - // lemmaUpdateValidPairMaintainsForall( - // cache, - // { case ((r, c), res) => - // validRegex(r) && res == derivativeStep(r, c) - // }, - // (r, c), - // res - // ) - // ) + ghostExpr(MutableHashMap.lemmaUpdatePreservesForallPairs(cache, (r, c), res, { + _ match { + case ((r, c), res) => + validRegex(r) && res == derivativeStep(r, c) + } + })) + val _ = cache.update((r, c), res) + () } ensuring (_ => validCacheMap(this.cache)) @@ -192,19 +193,6 @@ object VerifiedRegex { } } - // case class RegexHashable[C](cHashable: Hashable[C]) extends Hashable[Regex[C]] { - // def hash(r: Regex[C]): Long = { - // r match { - // case ElementMatch(c) => 2L * cHashable.hash(c) - // case Star(rInner) => 3L * hash(rInner) - // case Union(r1, r2) => 5L * hash(r1) + 5L * hash(r2) - // case Concat(r1, r2) => 7L * hash(r1) + 7L * hash(r2) - // case EmptyExpr() => 11L - // case EmptyLang() => 13L - // } - // } - // } - @ghost def isEmptyExpr[C](r: Regex[C]): Boolean = { r match { From 1716137bd0bc721a51344d3ff48b8a1e0d267a1b Mon Sep 17 00:00:00 2001 From: Samuel Chassot Date: Fri, 26 Jul 2024 16:53:32 +0200 Subject: [PATCH 16/78] add removeUselessConcat simplification for regex + equivalence proof --- .../main/scala/ch/epfl/benchmark/Utils.scala | 4 +- .../src/main/scala/ch/epfl/lexer/Main.scala | 6 +- .../scala/ch/epfl/lexer/VerifiedRegex.scala | 219 ++++++++++++++++-- 3 files changed, 204 insertions(+), 25 deletions(-) diff --git a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/benchmark/Utils.scala b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/benchmark/Utils.scala index 75f0f37a..c3a5e339 100644 --- a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/benchmark/Utils.scala +++ b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/benchmark/Utils.scala @@ -15,11 +15,11 @@ import ch.epfl.lexer.VerifiedRegex._ object RegexUtils { extension (s: String) def r: Regex[Char] = s.toCharArray().toList.foldRight[Regex[Char]](EmptyExpr())((c, acc) => Concat(ElementMatch(c), acc)) - extension (r: Regex[Char]) infix def + (r2: Regex[Char]): Regex[Char] = Union(r, r2) + extension (r: Regex[Char]) infix def | (r2: Regex[Char]): Regex[Char] = Union(r, r2) extension (r: Regex[Char]) def * : Regex[Char] = Star(r) extension (r: Regex[Char]) def + : Regex[Char] = r ~ Star(r) extension (r: Regex[Char]) infix def ~ (r2: Regex[Char]): Regex[Char] = Concat(r, r2) - extension (s: String) infix def + (s2: String): Regex[Char] = r(s) + r(s2) + extension (s: String) infix def | (s2: String): Regex[Char] = r(s) | r(s2) extension (s: String) infix def ~ (s2: String): Regex[Char] = r(s) ~ r(s2) extension (s: String) def * : Regex[Char] = r(s).* extension (s: String) def anyOf: Regex[Char] = s.toCharArray().toList.foldRight[Regex[Char]](EmptyLang())((c, acc) => Union(ElementMatch(c), acc)) diff --git a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/Main.scala b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/Main.scala index bebd2f48..2d800748 100644 --- a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/Main.scala +++ b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/Main.scala @@ -20,7 +20,7 @@ object Main { def testRegex(): Unit = { val cache: Cache[Char] = Cache(MutableHashMap.getEmptyHashMap(_ => EmptyLang(), KeyHashable)) - val r1 = ("a".r + "b".r).* + val r1 = ("a".r | "b".r).* println(f"r1 = ${r1}") println(f"list = ${"ab".toStainless}") println(f"matching a with r1 without cache: ${matchR(r1, Cons('a', Nil()))}") @@ -57,7 +57,7 @@ case class RegexHashable[C](hc: Hashable[C]) extends Hashable[Regex[C]] { object RegexBenchmark { val cache: Cache[Char] = Cache(MutableHashMap.getEmptyHashMap(_ => EmptyLang(), KeyHashable)) def benchmark01(): Unit = { - val r = ("a".r + "b".r).* + val r = ("a".r | "b".r).* val s = "abababababababababbbababbababbbabab" val match11 = matchRMem(r, s.toStainless)(cache) println(s"Matching $s with r -> $match11") @@ -83,7 +83,7 @@ object RegexBenchmark { } def benchmark03(): Unit = { - val r = ("a".r + "b".r).* + val r = ("a".r | "b".r).* println(s"r = $r") val s = "ababa" val match31 = matchRMem(r, s.toStainless)(cache) diff --git a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedRegex.scala b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedRegex.scala index 3b5ea46a..de06714e 100644 --- a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedRegex.scala +++ b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedRegex.scala @@ -68,7 +68,6 @@ object Memoisation { if (cache.contains((r, c))) { ghostExpr(lemmaIfInCacheThenValid(r, c)) - println("HIT") Some(cache((r, c))) } else { None() @@ -101,7 +100,7 @@ object VerifiedRegex { @ghost def validRegex[C](r: Regex[C]): Boolean = r match { case ElementMatch(c) => true - case Star(r) => !nullable(r) && !isEmptyLang(r) && validRegex(r) + case Star(r) => !nullable(r) && validRegex(r) // && !isEmptyLang(r) case Union(rOne, rTwo) => validRegex(rOne) && validRegex(rTwo) case Concat(rOne, rTwo) => validRegex(rOne) && validRegex(rTwo) case EmptyExpr() => true @@ -175,24 +174,6 @@ object VerifiedRegex { } } - def hash[C](r: Regex[C])(implicit hashC: Hashable[C]): Long = { - // decreases(r) - r match { - case ElementMatch(c) => - 2L * hashC.hash(c) - case Star(r) => - 3L * hash(r) - case Union(rOne, rTwo) => - 5L * hash(rOne) + 5L * hash(rTwo) - case Concat(rOne, rTwo) => - 7L * hash(rOne) + 7L * hash(rTwo) - case EmptyExpr() => - 11L - case EmptyLang() => - 13L - } - } - @ghost def isEmptyExpr[C](r: Regex[C]): Boolean = { r match { @@ -353,6 +334,8 @@ object VerifiedRegexMatcher { } @ghost + @opaque + @inlineOnce def mainMatchTheorem[C](r: Regex[C], s: List[C]): Unit = { require(validRegex(r)) decreases(s.size + regexDepth(r)) @@ -555,6 +538,202 @@ object VerifiedRegexMatcher { } ensuring (_ => bigger == returnP || !matchR(baseR, bigger)) + // ---------------------------------------------------- Regex normalisation ---------------------------------------------------- + + def removeUselessConcat[C](r: Regex[C]): Regex[C] = { + require(validRegex(r)) + decreases(regexDepth(r)) + r match { + case Concat(EmptyExpr(), r2) => removeUselessConcat(r2) + case Concat(r1, EmptyExpr()) => removeUselessConcat(r1) + case Concat(r1, r2) => Concat(removeUselessConcat(r1), removeUselessConcat(r2)) + case Union(r1, r2) => Union(removeUselessConcat(r1), removeUselessConcat(r2)) + case Star(rInner) => Star(removeUselessConcat(rInner)) + case _ => r + } + } ensuring (res => validRegex(res) && nullable(res) == nullable(r)) + + @ghost + def lemmaRemoveUselessConcatSound[C](r: Regex[C], s: List[C]) : Unit = { + require(validRegex(r)) + decreases(regexDepth(r) + s.size) + + mainMatchTheorem(r, s) + + if(matchR(r, s)){ + r match { + case Concat(EmptyExpr(), rr) => + if(s.isEmpty) { + () + } else { + val (s1, s2) = findConcatSeparation(EmptyExpr(), rr, Nil(), s, s).get + assert(s1.isEmpty) + assert(matchR(rr, s2)) + mainMatchTheorem(rr, s2) + lemmaRemoveUselessConcatSound(rr, s2) + } + case Concat(rr, EmptyExpr()) => + if(s.isEmpty) { + () + } else { + val (s1, s2) = findConcatSeparation(rr, EmptyExpr(), Nil(), s, s).get + assert(s2.isEmpty) + assert(matchR(rr, s1)) + mainMatchTheorem(rr, s1) + lemmaRemoveUselessConcatSound(rr, s1) + } + + case Concat(r1, r2) => + if(s.isEmpty) { + () + } else { + val (s1, s2) = findConcatSeparation(r1, r2, Nil(), s, s).get + assert(matchR(r1, s1)) + assert(matchR(r2, s2)) + lemmaRemoveUselessConcatSound(r1, s1) + lemmaRemoveUselessConcatSound(r2, s2) + assert(matchR(removeUselessConcat(r1), s1)) + assert(matchR(removeUselessConcat(r2), s2)) + lemmaTwoRegexMatchThenConcatMatchesConcatString(removeUselessConcat(r1), removeUselessConcat(r2), s1, s2) + } + case Union(r1, r2) => + lemmaRegexUnionAcceptsThenOneOfTheTwoAccepts(r1, r2, s) + if(matchR(r1, s)) { + lemmaRemoveUselessConcatSound(r1, s) + assert(matchR(removeUselessConcat(r1), s)) + lemmaRegexAcceptsStringThenUnionWithAnotherAcceptsToo(removeUselessConcat(r1), removeUselessConcat(r2), s) + } else { + lemmaRemoveUselessConcatSound(r2, s) + assert(matchR(removeUselessConcat(r2), s)) + lemmaRegexAcceptsStringThenUnionWithAnotherAcceptsToo(removeUselessConcat(r2), removeUselessConcat(r1), s) + lemmaReversedUnionAcceptsSameString(removeUselessConcat(r2), removeUselessConcat(r1), s) + } + case Star(rInner) => + if(s.isEmpty) { + () + } else { + assert(findConcatSeparation(rInner, Star(rInner), Nil(), s, s).isDefined) + val r1 = rInner + val r2 = Star(rInner) + val (s1, s2) = findConcatSeparation(rInner, Star(rInner), Nil(), s, s).get + assert(matchR(rInner, s1)) + assert(matchR(Star(rInner), s2)) + lemmaRemoveUselessConcatSound(rInner, s1) + if(s2.size == s.size){ + assert(s1 ++ s2 == s) + assert(s1.size + s2.size == s.size) + assert(s1.size == 0) + assert(s1.isEmpty) + assert(matchR(rInner, s1)) + mainMatchTheorem(rInner, s1) + assert(nullable(rInner)) + check(false) + } + lemmaRemoveUselessConcatSound(Star(rInner), s2) + assert(matchR(removeUselessConcat(rInner), s1)) + assert(removeUselessConcat(Star(rInner)) == Star(removeUselessConcat(rInner))) + assert(matchR(Star(removeUselessConcat(rInner)), s2)) + lemmaStarApp(removeUselessConcat(rInner), s1, s2) + } + case _ => () + } + } else { + r match { + case Concat(EmptyExpr(), rr) => + if(s.isEmpty) { + () + } else { + assert(findConcatSeparation(EmptyExpr(), rr, Nil(), s, s).isEmpty) + lemmaRemoveUselessConcatSound(rr, s) + if(matchR(removeUselessConcat(rr), s)){ + assert(matchR(rr, s)) + lemmaR1MatchesS1AndR2MatchesS2ThenFindSeparationFindsAtLeastThem(EmptyExpr(), rr, Nil(), s, s, Nil(), s) + check(false) + } + } + case Concat(rr, EmptyExpr()) => + if(s.isEmpty) { + () + } else { + assert(findConcatSeparation(rr, EmptyExpr(), Nil(), s, s).isEmpty) + lemmaRemoveUselessConcatSound(rr, s) + if(matchR(removeUselessConcat(rr), s)){ + assert(matchR(rr, s)) + lemmaR1MatchesS1AndR2MatchesS2ThenFindSeparationFindsAtLeastThem(rr, EmptyExpr(), s, Nil(), s, Nil(), s) + check(false) + } + } + + case Concat(r1, r2) => + if(s.isEmpty) { + () + } else { + if(matchR(Concat(removeUselessConcat(r1), removeUselessConcat(r2)), s)){ + mainMatchTheorem(Concat(removeUselessConcat(r1), removeUselessConcat(r2)), s) + assert(findConcatSeparation(removeUselessConcat(r1), removeUselessConcat(r2), Nil(), s, s).isDefined) + val (s1, s2) = findConcatSeparation(removeUselessConcat(r1), removeUselessConcat(r2), Nil(), s, s).get + lemmaRemoveUselessConcatSound(r1, s1) + lemmaRemoveUselessConcatSound(r2, s2) + lemmaTwoRegexMatchThenConcatMatchesConcatString(r1, r2, s1, s2) + check(false) + } + } + case Union(r1, r2) => + if(matchR(Union(removeUselessConcat(r1), removeUselessConcat(r2)), s)) { + mainMatchTheorem(Union(removeUselessConcat(r1), removeUselessConcat(r2)), s) + lemmaRegexUnionAcceptsThenOneOfTheTwoAccepts(removeUselessConcat(r1), removeUselessConcat(r2), s) + if(matchR(removeUselessConcat(r1), s)) { + lemmaRemoveUselessConcatSound(r1, s) + assert(matchR(r1, s)) + lemmaRegexAcceptsStringThenUnionWithAnotherAcceptsToo(r1, r2, s) + } else { + lemmaRemoveUselessConcatSound(r2, s) + assert(matchR(r2, s)) + lemmaRegexAcceptsStringThenUnionWithAnotherAcceptsToo(r2, r1, s) + lemmaReversedUnionAcceptsSameString(r2, r1, s) + } + check(false) + } + case Star(rInner) => + if(matchR(Star(removeUselessConcat(rInner)), s)) { + mainMatchTheorem(Star(removeUselessConcat(rInner)), s) + if(s.isEmpty) { + () + } else { + assert(findConcatSeparation(removeUselessConcat(rInner), Star(removeUselessConcat(rInner)), Nil(), s, s).isDefined) + val (s1, s2) = findConcatSeparation(removeUselessConcat(rInner), Star(removeUselessConcat(rInner)), Nil(), s, s).get + assert(matchR(removeUselessConcat(rInner), s1)) + assert(matchR(Star(removeUselessConcat(rInner)), s2)) + lemmaRemoveUselessConcatSound(rInner, s1) + assert(matchR(rInner, s1)) + if(s2.size == s.size){ + assert(s1 ++ s2 == s) + assert(s1.size + s2.size == s.size) + assert(s1.size == 0) + assert(s1.isEmpty) + assert(matchR(rInner, s1)) + mainMatchTheorem(rInner, s1) + assert(nullable(rInner)) + check(false) + } + lemmaRemoveUselessConcatSound(Star(rInner), s2) + assert(matchR(rInner, s1)) + assert(matchR(Star(rInner), s2)) + lemmaStarApp(rInner, s1, s2) + } + } + case _ => () + } + } + + }.ensuring(_ => matchR(r, s) == matchR(removeUselessConcat(r), s)) + + + + + +// ---------------------------------------------------- Lemmas ---------------------------------------------------- + @ghost def lemmaIfMatchRThenLongestMatchFromThereReturnsAtLeastThis[C](baseR: Regex[C], r: Regex[C], input: List[C], testedP: List[C]): Unit = { require(validRegex(baseR)) From bea8a9383169e5623edc17b60f741f76d763c35b Mon Sep 17 00:00:00 2001 From: Samuel Chassot Date: Mon, 29 Jul 2024 10:57:49 +0200 Subject: [PATCH 17/78] add a simplification function that is sound --- .../src/main/scala/ch/epfl/lexer/Main.scala | 76 ++++- .../scala/ch/epfl/lexer/OptimisedChecks.scala | 10 + .../scala/ch/epfl/lexer/VerifiedRegex.scala | 285 +++++++++++++++++- 3 files changed, 358 insertions(+), 13 deletions(-) create mode 100644 lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/OptimisedChecks.scala diff --git a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/Main.scala b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/Main.scala index 2d800748..d61ab010 100644 --- a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/Main.scala +++ b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/Main.scala @@ -9,12 +9,18 @@ import stainless.annotation._ import stainless.lang._ import stainless.collection._ import ch.epfl.chassot.Hashable +import ch.epfl.lexer.RegexBenchmark.testSimp +import scala.collection.View.Empty object Main { def main(args: Array[String]): Unit = { RegexBenchmark.benchmark01() RegexBenchmark.benchmark02() RegexBenchmark.benchmark03() + RegexBenchmark.benchmark03Simp() + // testRegex() + // println("\n\n\n") + // testSimp() } } @@ -33,6 +39,8 @@ def testRegex(): Unit = { val s21 = "samuel.chassot@gmail.com" println(f"matching $s21 with r2: ${matchRMem(r2, s21.toStainless)(cache)}") + println(s"r1 = $r1\nremoveUselessConcat(r1) = ${removeUselessConcat(r1)}") + } object KeyHashable extends Hashable[(Regex[Char], Char)] { @@ -53,7 +61,6 @@ case class RegexHashable[C](hc: Hashable[C]) extends Hashable[Regex[C]] { } } - object RegexBenchmark { val cache: Cache[Char] = Cache(MutableHashMap.getEmptyHashMap(_ => EmptyLang(), KeyHashable)) def benchmark01(): Unit = { @@ -83,17 +90,80 @@ object RegexBenchmark { } def benchmark03(): Unit = { - val r = ("a".r | "b".r).* + val r = ("a".r | "b".r).* println(s"r = $r") val s = "ababa" val match31 = matchRMem(r, s.toStainless)(cache) println(s"Matching $s with r -> $match31") assert(match31) - val s2 = "abbbabbabbababaaaaaaaaaaabababababababa" + val s2 = "ababaabbabbababaaaabababbababbbababa" + println(s"Matching $s2 with r...") val match32 = matchRMem(r, s2.toStainless)(cache) + println(s"Done -> $match32") + assert(match32) + } + + def benchmark03Simp(): Unit = { + val r = removeUselessConcat((("a".r | "b".r).* | "c".r).*) + println(s"r = $r") + val s = "ababa" + val match31 = matchRMem(r, s.toStainless)(cache) + println(s"Matching $s with r -> $match31") + assert(match31) + + val s2 = "abbbabbabbababccaaaabababbababbbababa" + val match32 = matchRMemSimp(r, s2.toStainless)(cache) println(s"Matching $s2 with r -> $match32") assert(match32) } + def testSimp(): Unit = { + val r = Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Concat(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Concat(EmptyLang(),EmptyExpr()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Concat(EmptyLang(),EmptyExpr()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang())),Star(Union(Concat(ElementMatch('a'),EmptyExpr()),Concat(ElementMatch('b'),EmptyExpr())))),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Concat(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Concat(EmptyLang(),EmptyExpr()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Concat(EmptyLang(),EmptyExpr()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang())),Star(Union(Concat(ElementMatch('a'),EmptyExpr()),Concat(ElementMatch('b'),EmptyExpr())))),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Concat(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Concat(EmptyLang(),EmptyExpr()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Concat(EmptyLang(),EmptyExpr()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang())),Star(Union(Concat(ElementMatch('a'),EmptyExpr()),Concat(ElementMatch('b'),EmptyExpr())))),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Concat(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Concat(EmptyLang(),EmptyExpr()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Concat(EmptyLang(),EmptyExpr()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang())),Star(Union(Concat(ElementMatch('a'),EmptyExpr()),Concat(ElementMatch('b'),EmptyExpr())))),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Concat(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Concat(EmptyLang(),EmptyExpr()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Concat(EmptyLang(),EmptyExpr()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang())),Star(Union(Concat(ElementMatch('a'),EmptyExpr()),Concat(ElementMatch('b'),EmptyExpr())))),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Concat(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Concat(EmptyLang(),EmptyExpr()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Concat(EmptyLang(),EmptyExpr()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang())),Star(Union(Concat(ElementMatch('a'),EmptyExpr()),Concat(ElementMatch('b'),EmptyExpr())))),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Concat(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Concat(EmptyLang(),EmptyExpr()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Concat(EmptyLang(),EmptyExpr()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang())),Star(Union(Concat(ElementMatch('a'),EmptyExpr()),Concat(ElementMatch('b'),EmptyExpr())))),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()))))))) + + // val r = + // Union( + // ElementMatch('a'), + // Concat( + // EmptyExpr(), + // Concat( + // EmptyExpr(), + // Concat( + // EmptyExpr(), + // Concat( + // EmptyExpr(), + // Concat( + // EmptyExpr(), + // Concat( + // EmptyExpr(), + // Concat( + // EmptyExpr(), + // EmptyLang() + // ) + // ) + // ) + // ) + // ) + // ) + // ) + // ) + println(s"r = $r") + val rr = simplify(r) + println(s"rr = $rr") + + println(s"depth before simplification = ${regexDepth(r)}") + println(s"depth after simplification = ${regexDepth(rr)}") + } + + def regexDepth[C](r: Regex[C]): BigInt = { + decreases(r) + r match { + case ElementMatch(c) => BigInt(1) + case Star(r) => BigInt(1) + regexDepth(r) + case Union(rOne, rTwo) => BigInt(1) + Utils.maxBigInt(regexDepth(rOne), regexDepth(rTwo)) + case Concat(rOne, rTwo) => BigInt(1) + Utils.maxBigInt(regexDepth(rOne), regexDepth(rTwo)) + case EmptyExpr() => BigInt(1) + case EmptyLang() => BigInt(1) + } + } } diff --git a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/OptimisedChecks.scala b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/OptimisedChecks.scala new file mode 100644 index 00000000..5724ec28 --- /dev/null +++ b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/OptimisedChecks.scala @@ -0,0 +1,10 @@ +/** Author: Samuel Chassot + */ + +package ch.epfl.chassot + +object OptimisedChecks { + extension [T](inline value: T) inline def ensuring(condition: T => Boolean): T = value + inline def require(inline condition: Boolean): Unit = () + inline def assert(inline condition: Boolean): Unit = () +} diff --git a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedRegex.scala b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedRegex.scala index de06714e..d9b8b3c1 100644 --- a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedRegex.scala +++ b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedRegex.scala @@ -14,10 +14,12 @@ import ch.epfl.chassot.ListMap import ch.epfl.chassot.TupleListOpsGenK import ch.epfl.chassot.MutableHashMap._ import ch.epfl.chassot.Hashable -import stainless.lang.StaticChecks._ import ch.epfl.chassot.TupleListOpsGenK.invariantList import ch.epfl.chassot.MutableHashMap +import stainless.lang.StaticChecks._ +// import ch.epfl.chassot.OptimisedChecks.* + object Memoisation { import VerifiedRegex._ import VerifiedRegexMatcher._ @@ -107,7 +109,7 @@ object VerifiedRegex { case EmptyLang() => true } - @ghost + // @ghost def regexDepth[C](r: Regex[C]): BigInt = { decreases(r) r match { @@ -174,21 +176,21 @@ object VerifiedRegex { } } - @ghost + // @ghost def isEmptyExpr[C](r: Regex[C]): Boolean = { r match { case EmptyExpr() => true case _ => false } } - @ghost + // @ghost def isEmptyLang[C](r: Regex[C]): Boolean = { r match { case EmptyLang() => true case _ => false } } - @ghost + // @ghost def isElementMatch[C](r: Regex[C]): Boolean = { r match { case ElementMatch(_) => true @@ -202,14 +204,14 @@ object VerifiedRegex { case ElementMatch(cc) => c == cc } } - @ghost + // @ghost def isStar[C](r: Regex[C]): Boolean = { r match { case Star(_) => true case _ => false } } - @ghost + // @ghost def isUnion[C](r: Regex[C]): Boolean = { r match { case Union(_, _) => true @@ -224,7 +226,7 @@ object VerifiedRegex { } } - @ghost + // @ghost def isConcat[C](r: Regex[C]): Boolean = { r match { case Concat(_, _) => true @@ -281,6 +283,33 @@ object VerifiedRegexMatcher { } ensuring (res => res == derivativeStep(r, a)) + def derivativeStepMemSimp[C](r: Regex[C], a: C)(implicit cache: Cache[C]): Regex[C] = { + require(validRegex(r)) + require(cache.valid) + decreases(r) + + val rr = simplify(r) + cache.get(rr, a) match { + case Some(res) => res + case None() => { + val res: Regex[C] = r match { + case EmptyExpr() => EmptyLang() + case EmptyLang() => EmptyLang() + case ElementMatch(c) => if (a == c) EmptyExpr() else EmptyLang() + case Union(rOne, rTwo) => Union(derivativeStepMem(rOne, a)(cache), derivativeStepMem(rTwo, a)(cache)) + case Star(rInner) => Concat(derivativeStepMem(rInner, a)(cache), Star(rInner)) + case Concat(rOne, rTwo) => { + if (nullable(rOne)) Union(Concat(derivativeStepMem(rOne, a)(cache), rTwo), derivativeStepMem(rTwo, a)(cache)) + else Union(Concat(derivativeStepMem(rOne, a)(cache), rTwo), EmptyLang()) + } + } + cache.update(rr, a, res) + res + } + } + + } ensuring (res => res == derivativeStep(r, a)) + def derivative[C](r: Regex[C], input: List[C]): Regex[C] = { require(validRegex(r)) input match { @@ -316,7 +345,26 @@ object VerifiedRegexMatcher { require(validRegex(r)) require(cache.valid) decreases(input.size) - if (input.isEmpty) nullable(r) else matchR(derivativeStepMem(r, input.head)(cache: Cache[C]), input.tail) + if (input.isEmpty) nullable(r) else matchRMem(derivativeStepMem(r, input.head)(cache: Cache[C]), input.tail) + } ensuring (res => res == matchR(r, input)) + + def matchRMemSimp[C](r: Regex[C], input: List[C])(implicit cache: Cache[C]): Boolean = { + require(validRegex(r)) + require(cache.valid) + decreases(input.size) + val rr = simplify(r) + if(!input.isEmpty) { + // println(s"derivative wrt ${input.head}") + // println(s"r depth = ${regexDepth(r)}") + // println(s"rr depth = ${regexDepth(rr)}") + if(regexDepth(rr) >= 13) { + // println(s"r = $r") + // println("\n\n\n") + // println(s"rr = $rr") + return false + } + } + if (input.isEmpty) nullable(rr) else matchRMemSimp(derivativeStepMem(rr, input.head)(cache: Cache[C]), input.tail) } ensuring (res => res == matchR(r, input)) @ghost @@ -538,8 +586,13 @@ object VerifiedRegexMatcher { } ensuring (_ => bigger == returnP || !matchR(baseR, bigger)) - // ---------------------------------------------------- Regex normalisation ---------------------------------------------------- + // ---------------------------------------------------- Regex normalisation and simplification ---------------------------------------------------- + /** Removes useless Concatenation, i.e., Concat(EmptyExpr(), r) = r and Concat(r, EmptyExpr()) = r + * + * @param r + * @return + */ def removeUselessConcat[C](r: Regex[C]): Regex[C] = { require(validRegex(r)) decreases(regexDepth(r)) @@ -729,6 +782,218 @@ object VerifiedRegexMatcher { }.ensuring(_ => matchR(r, s) == matchR(removeUselessConcat(r), s)) + /** Simplifies the regex by + * - removing EmptyLang() recursively + * + * @param r + * @return + */ + def simplify[C](r: Regex[C]): Regex[C] = { + require(validRegex(r)) + decreases(regexDepth(r)) + r match { + case EmptyLang() => EmptyLang() + case ElementMatch(c) => ElementMatch(c) + case EmptyExpr() => EmptyExpr() + case Star(rInner) => + val simpInner = simplify(rInner) + if(isEmptyLang(simpInner) || isEmptyExpr(simpInner)) then EmptyExpr() else Star(simpInner) + case Union(r1, r2) => + val simpR1 = simplify(r1) + val simpR2 = simplify(r2) + if(isEmptyLang(simpR1)) then simpR2 else if(isEmptyLang(simpR2)) then simpR1 else Union(simpR1, simpR2) + case Concat(r1, r2) => + val simpR1 = simplify(r1) + val simpR2 = simplify(r2) + if(isEmptyLang(simpR1) || isEmptyLang(simpR2)) then + EmptyLang[C]() + else if(isEmptyExpr(simpR1)) then + simpR2 + else if(isEmptyExpr(simpR2)) then + simpR1 + else Concat(simpR1, simpR2) + } + } ensuring (res => validRegex(res) && nullable(res) == nullable(r)) + + @ghost + def lemmaSimplifySound[C](r: Regex[C], s: List[C]) : Unit = { + require(validRegex(r)) + decreases(regexDepth(r) + s.size) + + mainMatchTheorem(r, s) + + if(matchR(r, s)){ + r match { + case Concat(r1, r2) => + if(s.isEmpty) { + () + } else { + val (s1, s2) = findConcatSeparation(r1, r2, Nil(), s, s).get + assert(matchR(r1, s1)) + assert(matchR(r2, s2)) + lemmaSimplifySound(r1, s1) + lemmaSimplifySound(r2, s2) + assert(matchR(simplify(r1), s1)) + assert(matchR(simplify(r2), s2)) + lemmaTwoRegexMatchThenConcatMatchesConcatString(simplify(r1), simplify(r2), s1, s2) + } + case Union(r1, r2) => + lemmaRegexUnionAcceptsThenOneOfTheTwoAccepts(r1, r2, s) + if(matchR(r1, s)) { + lemmaSimplifySound(r1, s) + assert(matchR(simplify(r1), s)) + lemmaRegexAcceptsStringThenUnionWithAnotherAcceptsToo(simplify(r1), simplify(r2), s) + } else { + lemmaSimplifySound(r2, s) + assert(matchR(simplify(r2), s)) + lemmaRegexAcceptsStringThenUnionWithAnotherAcceptsToo(simplify(r2), simplify(r1), s) + lemmaReversedUnionAcceptsSameString(simplify(r2), simplify(r1), s) + } + case Star(rInner) => + if(s.isEmpty) { + () + } else { + assert(findConcatSeparation(rInner, Star(rInner), Nil(), s, s).isDefined) + val r1 = rInner + val r2 = Star(rInner) + val (s1, s2) = findConcatSeparation(rInner, Star(rInner), Nil(), s, s).get + assert(matchR(rInner, s1)) + assert(matchR(Star(rInner), s2)) + lemmaSimplifySound(rInner, s1) + if(s2.size == s.size){ + assert(s1 ++ s2 == s) + assert(s1.size + s2.size == s.size) + assert(s1.size == 0) + assert(s1.isEmpty) + assert(matchR(rInner, s1)) + mainMatchTheorem(rInner, s1) + assert(nullable(rInner)) + check(false) + } + lemmaSimplifySound(Star(rInner), s2) + assert(matchR(simplify(rInner), s1)) + assert(simplify(Star(rInner)) == Star(simplify(rInner))) + assert(matchR(Star(simplify(rInner)), s2)) + lemmaStarApp(simplify(rInner), s1, s2) + + } + case _ => () + } + } else { + r match { + case Concat(r1, r2) => + if(s.isEmpty) { + () + } else { + val simpR1 = simplify(r1) + val simpR2 = simplify(r2) + if(isEmptyLang(simpR1)) { + lemmaSimplifySound(r1, s) + assert(!matchR(r1, s)) + } + else if(isEmptyLang(simpR2)) { + lemmaSimplifySound(r2, s) + assert(!matchR(r2, s)) + } + else if(isEmptyExpr(simpR1)) { + lemmaSimplifySound(r2, s) + if(matchR(r2, s)) { + lemmaR1MatchesS1AndR2MatchesS2ThenFindSeparationFindsAtLeastThem(r1, r2, Nil(), s, s, Nil(), s) + check(false) + } + } + else if(isEmptyExpr(simpR2)) { + lemmaSimplifySound(r1, s) + lemmaSimplifySound(r2, Nil()) + if(matchR(r1, s)) { + lemmaR1MatchesS1AndR2MatchesS2ThenFindSeparationFindsAtLeastThem(r1, r2, s, Nil(), s, Nil(), s) + check(false) + } + } + else if(matchR(Concat(simplify(r1), simplify(r2)), s)){ + mainMatchTheorem(Concat(simplify(r1), simplify(r2)), s) + assert(findConcatSeparation(simplify(r1), simplify(r2), Nil(), s, s).isDefined) + val (s1, s2) = findConcatSeparation(simplify(r1), simplify(r2), Nil(), s, s).get + lemmaSimplifySound(r1, s1) + lemmaSimplifySound(r2, s2) + lemmaTwoRegexMatchThenConcatMatchesConcatString(r1, r2, s1, s2) + check(false) + } + } + case Union(r1, r2) => + val simpR1 = simplify(r1) + val simpR2 = simplify(r2) + if(isEmptyLang(simpR1)) { + lemmaSimplifySound(r2, s) + if(matchR(r2, s)) { + lemmaRegexAcceptsStringThenUnionWithAnotherAcceptsToo(r2, r1, s) + lemmaReversedUnionAcceptsSameString(r2, r1, s) + check(false) + } + } + else if(isEmptyLang(simpR2)) { + lemmaSimplifySound(r1, s) + if(matchR(r1, s)) { + lemmaRegexAcceptsStringThenUnionWithAnotherAcceptsToo(r1, r2, s) + check(false) + } + } else { + if(matchR(Union(simpR1, simpR2), s)){ + lemmaRegexUnionAcceptsThenOneOfTheTwoAccepts(simpR1, simpR2, s) + if(matchR(simpR1, s)){ + lemmaSimplifySound(r1, s) + assert(matchR(r1, s)) + lemmaRegexAcceptsStringThenUnionWithAnotherAcceptsToo(r1, r2, s) + } else { + lemmaSimplifySound(r2, s) + assert(matchR(r2, s)) + lemmaRegexAcceptsStringThenUnionWithAnotherAcceptsToo(r2, r1, s) + lemmaReversedUnionAcceptsSameString(r2, r1, s) + } + check(false) + } + } + case Star(rInner) => + val simpRInner = simplify(rInner) + if(isEmptyLang(simpRInner)) { + lemmaSimplifySound(rInner, s) + if(matchR(rInner, s)) { + lemmaStarApp(rInner, Nil(), s) + check(false) + } + } else { + if(matchR(Star(simplify(rInner)), s)) { + mainMatchTheorem(Star(simplify(rInner)), s) + if(s.isEmpty) { + () + } else { + assert(findConcatSeparation(simplify(rInner), Star(simplify(rInner)), Nil(), s, s).isDefined) + val (s1, s2) = findConcatSeparation(simplify(rInner), Star(simplify(rInner)), Nil(), s, s).get + assert(matchR(simplify(rInner), s1)) + assert(matchR(Star(simplify(rInner)), s2)) + lemmaSimplifySound(rInner, s1) + if(s2.size == s.size){ + assert(s1 ++ s2 == s) + assert(s1.size + s2.size == s.size) + assert(s1.size == 0) + assert(s1.isEmpty) + assert(matchR(rInner, s1)) + mainMatchTheorem(rInner, s1) + assert(nullable(rInner)) + check(false) + } + lemmaSimplifySound(Star(rInner), s2) + assert(matchR(rInner, s1)) + assert(matchR(Star(rInner), s2)) + lemmaStarApp(rInner, s1, s2) + } + } + } + case _ => () + } + } + + }.ensuring(_ => matchR(r, s) == matchR(simplify(r), s)) From 7936c5c90370536fb65589c86e592fda4eff9108 Mon Sep 17 00:00:00 2001 From: Samuel Chassot Date: Wed, 31 Jul 2024 12:35:13 +0200 Subject: [PATCH 18/78] working on regex --- .../chassot/BuggyNewMaskComputation.scala | 2 +- .../src/main/scala/ch/epfl/chassot/Main.scala | 32 +++++++++++++++---- .../ch/epfl/chassot/MutableLongMap.scala | 2 +- .../scala/ch/epfl/lexer/VerifiedRegex.scala | 1 + 4 files changed, 29 insertions(+), 8 deletions(-) diff --git a/data-structures/maps/mutablemaps/src/main/scala/ch/epfl/chassot/BuggyNewMaskComputation.scala b/data-structures/maps/mutablemaps/src/main/scala/ch/epfl/chassot/BuggyNewMaskComputation.scala index 64c21c99..040adc0e 100644 --- a/data-structures/maps/mutablemaps/src/main/scala/ch/epfl/chassot/BuggyNewMaskComputation.scala +++ b/data-structures/maps/mutablemaps/src/main/scala/ch/epfl/chassot/BuggyNewMaskComputation.scala @@ -53,7 +53,7 @@ object BuggyNewMaskComputation { if (2 * (_size + _vacant) >= oldMask && !(5 * _vacant > oldMask)) { m = ((m << 1) + 1) & MAX_MASK } - while (m > 8 && 8 * _size < m && ((m >> 1) & MAX_MASK) >= _size) { + while (m > 8 && _size < (m >>> 3)) { decreases(m) m = m >>> 1 } diff --git a/data-structures/maps/mutablemaps/src/main/scala/ch/epfl/chassot/Main.scala b/data-structures/maps/mutablemaps/src/main/scala/ch/epfl/chassot/Main.scala index 55bf0694..0cb10de0 100644 --- a/data-structures/maps/mutablemaps/src/main/scala/ch/epfl/chassot/Main.scala +++ b/data-structures/maps/mutablemaps/src/main/scala/ch/epfl/chassot/Main.scala @@ -4,14 +4,34 @@ import ch.epfl.chassot.MutableLongMap import ch.epfl.chassot.ListLongMap import stainless.collection.List import benchmark.BenchmarkUtil.* - +import benchmark.Key +import ch.epfl.chassot.MutableHashMap.* +import ch.epfl.chassot.MutableLongMap.ValueCellFull +import ch.epfl.chassot.MutableLongMap.EmptyCell +import scala.collection.mutable.HashMap object Main { def main(args: Array[String]): Unit = { - val mLongMap = MutableLongMap.getEmptyLongMap(_ => 0L) - mLongMap.update(1, 42L) - mLongMap.update(2, 43L) - mLongMap.remove(2) - println(f"mLongMap.apply(1) = ${mLongMap.apply(1)}") + // check the number of collisions of each levels: + + val mapFilled = benchmark.HashMapBenchmarkUtilBig.verifiedHashMapFilledWith2to22Values + println(f"mapFilled.size = ${mapFilled.size}") + var bucketSizes: HashMap[BigInt, Int] = HashMap() + var i = 0 + while (i < mapFilled.underlying.v.underlying.v.mask + 1) { + val key = mapFilled.underlying.v.underlying.v._keys(i) + if(key != 0 && key != Long.MinValue) { + val value = mapFilled.underlying.v.underlying.v._values(i) + println(value) + value match + case ValueCellFull(v) => bucketSizes(v.size) = bucketSizes.getOrElse(v.size, 0) + 1 + case EmptyCell() => () + + } + i += 1 + } + + println(f"bucketSizes = ${bucketSizes}") + } diff --git a/data-structures/maps/mutablemaps/src/main/scala/ch/epfl/chassot/MutableLongMap.scala b/data-structures/maps/mutablemaps/src/main/scala/ch/epfl/chassot/MutableLongMap.scala index 8d4f9032..13ead76e 100644 --- a/data-structures/maps/mutablemaps/src/main/scala/ch/epfl/chassot/MutableLongMap.scala +++ b/data-structures/maps/mutablemaps/src/main/scala/ch/epfl/chassot/MutableLongMap.scala @@ -101,7 +101,7 @@ object MutableLongMap { if (2 * (_size + _vacant) >= oldMask && !(5 * _vacant > oldMask)) { m = ((m << 1) + 1) & MAX_MASK } - while (m > 8 && 8 * _size < m && ((m >> 1) & MAX_MASK) + 1 >= _size) { + while (m > 8 && _size < (m >>> 3)) { decreases(m) m = m >>> 1 } diff --git a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedRegex.scala b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedRegex.scala index d9b8b3c1..98353899 100644 --- a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedRegex.scala +++ b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedRegex.scala @@ -784,6 +784,7 @@ object VerifiedRegexMatcher { /** Simplifies the regex by * - removing EmptyLang() recursively + * - removing EmptyExpr() in Concat * * @param r * @return From f8609979ea8543d44dd2b3993630a45f368814e6 Mon Sep 17 00:00:00 2001 From: Samuel Chassot Date: Fri, 9 Aug 2024 14:51:49 +0200 Subject: [PATCH 19/78] as symlink --- .../scala/ch/epfl/chassot/ListLongMap.scala | 997 +- .../main/scala/ch/epfl/chassot/ListMap.scala | 1566 +-- .../ch/epfl/chassot/MutableHashMap.scala | 1621 +--- .../ch/epfl/chassot/MutableLongMap.scala | 8408 +---------------- .../scala/ch/epfl/chassot/iMutableMaps.scala | 1 + 5 files changed, 5 insertions(+), 12588 deletions(-) mode change 100644 => 120000 lexers/regex/verifiedlexer/src/main/scala/ch/epfl/chassot/ListLongMap.scala mode change 100644 => 120000 lexers/regex/verifiedlexer/src/main/scala/ch/epfl/chassot/ListMap.scala mode change 100644 => 120000 lexers/regex/verifiedlexer/src/main/scala/ch/epfl/chassot/MutableHashMap.scala mode change 100644 => 120000 lexers/regex/verifiedlexer/src/main/scala/ch/epfl/chassot/MutableLongMap.scala create mode 120000 lexers/regex/verifiedlexer/src/main/scala/ch/epfl/chassot/iMutableMaps.scala diff --git a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/chassot/ListLongMap.scala b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/chassot/ListLongMap.scala deleted file mode 100644 index bb01e80a..00000000 --- a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/chassot/ListLongMap.scala +++ /dev/null @@ -1,996 +0,0 @@ -/** Author: Samuel Chassot - */ - -package ch.epfl.chassot - -import stainless.annotation._ -import stainless.collection._ -import stainless.equations._ -import stainless.lang._ -import stainless.proof.check -import scala.annotation.tailrec -import scala.collection.immutable - -// Uncomment the following import to run benchmarks -// import OptimisedChecks.* - -case class ListLongMap[B](toList: List[(Long, B)]) { - require(TupleListOps.isStrictlySorted(toList)) - - def isEmpty: Boolean = toList.isEmpty - - def head: (Long, B) = { - require(!isEmpty) - toList.head - } - - def size: Int = { - require(toList.size < Integer.MAX_VALUE) - TupleListOps.intSize(toList) - } - - def nKeys: Int = { - require(toList.size < Integer.MAX_VALUE) - TupleListOps.intSizeKeys(TupleListOps.getKeysList(toList)) - } - - def tail: ListLongMap[B] = { - require(!isEmpty) - ListLongMap(toList.tail) - } - - def contains(key: Long): Boolean = { - val res = TupleListOps.containsKey(toList, key) - if (res) { - TupleListOps.lemmaContainsKeyImpliesGetValueByKeyDefined(toList, key) - } - res - - }.ensuring(res => !res || this.get(key).isDefined) - - @inline - def get(key: Long): Option[B] = { - TupleListOps.getValueByKey(toList, key) - } - - @inline - def keysOf(value: B): List[Long] = { - TupleListOps.getKeysOf(toList, value) - } - - def keys(): List[Long] = { - TupleListOps.getKeysList(toList) - } - - def apply(key: Long): B = { - require(contains(key)) - get(key).get - } - - def +(keyValue: (Long, B)): ListLongMap[B] = { - - val newList = - TupleListOps.insertStrictlySorted(toList, keyValue._1, keyValue._2) - - TupleListOps.lemmaContainsTupThenGetReturnValue( - newList, - keyValue._1, - keyValue._2 - ) - ListLongMap(newList) - - }.ensuring(res => - res.contains(keyValue._1) && res.get(keyValue._1) == Some[B]( - keyValue._2 - ) && res.toList.contains(keyValue) - ) - - def ++(keyValues: List[(Long, B)]): ListLongMap[B] = { - decreases(keyValues) - keyValues match { - case Nil() => this - case Cons(keyValue, rest) => (this + keyValue) ++ rest - } - } - def -(key: Long): ListLongMap[B] = { - ListLongMap(TupleListOps.removeStrictlySorted(toList, key)) - }.ensuring(res => !res.contains(key)) - - def --(keys: List[Long]): ListLongMap[B] = { - decreases(keys) - keys match { - case Nil() => this - case Cons(key, rest) => (this - key) -- rest - } - } - @inline - def forall(p: ((Long, B)) => Boolean): Boolean = { - toList.forall(p) - } -} - -object TupleListOps { - - @inline - def invariantList[B](l: List[(Long, B)]): Boolean = { - isStrictlySorted(l) - } - - def getKeysList[B](l: List[(Long, B)]): List[Long] = { - require(invariantList(l)) - decreases(l) - l match { - case Cons(head, tl) => Cons(head._1, getKeysList(tl)) - case Nil() => Nil[Long]() - } - }.ensuring(res => isStrictlySortedLong(res) && res.length == l.length) - - def intSizeKeys(l: List[Long]): Int = { - require(l.length < Integer.MAX_VALUE) - decreases(l) - - l match { - case Cons(head, tl) => 1 + intSizeKeys(tl) - case Nil() => 0 - } - } - - def intSize[B](l: List[(Long, B)]): Int = { - decreases(l) - l match { - case Cons(head, tl) => { - val s1 = intSize(tl) - if (s1 < Integer.MAX_VALUE) { - 1 + s1 - } else { - 0 - } - } - - case Nil() => 0 - } - }.ensuring(res => res >= 0) - - def getKeysOf[B](l: List[(Long, B)], value: B): List[Long] = { - require(invariantList(l)) - decreases(l) - - l match { - case Cons(head, tl) if (head._2 == value) => { - if (!getKeysOf(tl, value).isEmpty) { - lemmaForallGetValueByKeySameWithASmallerHead( - tl, - getKeysOf(tl, value), - value, - head - ) - - } - Cons(head._1, getKeysOf(tl, value)) - } - case Cons(head, tl) if (head._2 != value) => { - val r = getKeysOf(tl, value) - if (!getKeysOf(tl, value).isEmpty) { - lemmaForallGetValueByKeySameWithASmallerHead( - tl, - getKeysOf(tl, value), - value, - head - ) - } - getKeysOf(tl, value) - } - case Nil() => Nil[Long]() - } - - }.ensuring(res => res.forall(getValueByKey(l, _) == Some[B](value))) - - def filterByValue[B](l: List[(Long, B)], value: B): List[(Long, B)] = { - require(invariantList(l)) - decreases(l) - - l match { - case Cons(head, tl) if (head._2 == value) => - head :: filterByValue(tl, value) - case Cons(head, tl) if (head._2 != value) => filterByValue(tl, value) - case Nil() => Nil[(Long, B)]() - } - }.ensuring(res => - invariantList(res) && res.forall(_._2 == value) && - (if (l.isEmpty) res.isEmpty else res.isEmpty || res.head._1 >= l.head._1) - ) - - def getValueByKey[B](l: List[(Long, B)], key: Long): Option[B] = { - require(invariantList(l)) - decreases(l) - - l match { - case Cons(head, tl) if (head._1 == key) => Some[B](head._2) - case Cons(head, tl) if (head._1 != key) => getValueByKey(tl, key) - case Nil() => None[B]() - } - - } - - def insertStrictlySorted[B]( - l: List[(Long, B)], - newKey: Long, - newValue: B - ): List[(Long, B)] = { - require(invariantList(l)) - decreases(l) - - l match { - case Cons(head, tl) if (head._1 < newKey) => - head :: insertStrictlySorted(tl, newKey, newValue) - case Cons(head, tl) if (head._1 == newKey) => (newKey, newValue) :: tl - case Cons(head, tl) if (head._1 > newKey) => - (newKey, newValue) :: Cons(head, tl) - case Nil() => (newKey, newValue) :: Nil() - } - }.ensuring(res => - invariantList(res) && containsKey(res, newKey) && res.contains( - (newKey, newValue) - ) - ) - - def removeStrictlySorted[B]( - l: List[(Long, B)], - key: Long - ): List[(Long, B)] = { - require(invariantList(l)) - decreases(l) - - l match { - case Cons(head, tl) if (head._1 == key) => tl - case Cons(head, tl) if (head._1 != key) => - head :: removeStrictlySorted(tl, key) - case Nil() => Nil[(Long, B)]() - } - }.ensuring(res => invariantList(res) && !containsKey(res, key)) - - def isStrictlySorted[B](l: List[(Long, B)]): Boolean = { - decreases(l) - l match { - case Nil() => true - case Cons(_, Nil()) => true - case Cons(h1, Cons(h2, _)) if (h1._1 >= h2._1) => false - case Cons(_, t) => isStrictlySorted(t) - } - } - - def isStrictlySortedLong(l: List[Long]): Boolean = { - decreases(l) - l match { - case Nil() => true - case Cons(_, Nil()) => true - case Cons(h1, Cons(h2, _)) if (h1 >= h2) => false - case Cons(_, t) => isStrictlySortedLong(t) - } - } - - def containsKey[B](l: List[(Long, B)], key: Long): Boolean = { - require(invariantList(l)) - decreases(l) - l match { - case Cons(head, tl) if (head._1 == key) => true - case Cons(head, tl) if (head._1 > key) => false - case Cons(head, tl) if (head._1 < key) => containsKey(tl, key) - case Nil() => false - - } - } - - // ----------- LEMMAS ----------------------------------------------------- - - @opaque - @inlineOnce - def lemmaInsertAndRemoveStrictlySortedCommutative[B]( - l: List[(Long, B)], - key1: Long, - v1: B, - key2: Long - ): Unit = { - require(key1 != key2) - require(invariantList(l)) - decreases(l) - - l match { - case Cons(head, tl) => { - lemmaInsertAndRemoveStrictlySortedCommutative(tl, key1, v1, key2) - } - case _ => () - } - - }.ensuring(_ => - insertStrictlySorted( - removeStrictlySorted(l, key2), - key1, - v1 - ) == removeStrictlySorted( - insertStrictlySorted(l, key1, v1), - key2 - ) - ) - - @opaque - @inlineOnce - def lemmaInsertStrictlySortedThenRemoveIsSame[B]( - l: List[(Long, B)], - key1: Long, - v1: B - ): Unit = { - require(invariantList(l)) - require(!containsKey(l, key1)) - decreases(l) - - l match { - case Cons(head, tl) => { - lemmaInsertStrictlySortedThenRemoveIsSame(tl, key1, v1) - } - case _ => () - } - - }.ensuring(_ => removeStrictlySorted(insertStrictlySorted(l, key1, v1), key1) == l) - - @opaque - @inlineOnce - def lemmaInsertStrictlySortedCommutative[B]( - l: List[(Long, B)], - key1: Long, - v1: B, - key2: Long, - v2: B - ): Unit = { - require(key1 != key2) - require(invariantList(l)) - decreases(l) - - l match { - case Cons(head, tl) if (head._1 < key1 && head._1 < key2) => { - lemmaInsertStrictlySortedCommutative(tl, key1, v1, key2, v2) - } - case _ => () - } - - }.ensuring(_ => - insertStrictlySorted( - insertStrictlySorted(l, key1, v1), - key2, - v2 - ) == insertStrictlySorted( - insertStrictlySorted(l, key2, v2), - key1, - v1 - ) - ) - - @opaque - @inlineOnce - def lemmaRemoveStrictlySortedCommutative[B]( - l: List[(Long, B)], - key1: Long, - key2: Long - ): Unit = { - require(invariantList(l)) - decreases(l) - - l match { - case Cons(head, tl) => { - lemmaRemoveStrictlySortedCommutative(tl, key1, key2) - } - case _ => () - } - - }.ensuring(_ => - removeStrictlySorted( - removeStrictlySorted(l, key1), - key2 - ) == removeStrictlySorted( - removeStrictlySorted(l, key2), - key1 - ) - ) - - @opaque - @inlineOnce - def lemmaRemoveStrictlySortedNotPresentPreserves[B]( - l: List[(Long, B)], - key: Long - ): Unit = { - require(invariantList(l)) - require(!containsKey(l, key)) - decreases(l) - - l match { - case Cons(head, tl) => { - lemmaRemoveStrictlySortedNotPresentPreserves(tl, key) - } - case _ => () - } - - }.ensuring(_ => removeStrictlySorted(l, key) == l) - - @opaque - @inlineOnce - def lemmaInsertStrictlySortedErasesIfSameKey[B]( - l: List[(Long, B)], - key1: Long, - v1: B, - v2: B - ): Unit = { - require(invariantList(l)) - decreases(l) - - l match { - case Cons(head, tl) if (head._1 < key1) => { - lemmaInsertStrictlySortedErasesIfSameKey(tl, key1, v1, v2) - } - case _ => () - } - - }.ensuring(_ => - insertStrictlySorted( - insertStrictlySorted(l, key1, v1), - key1, - v2 - ) == insertStrictlySorted( - l, - key1, - v2 - ) - ) - - @opaque - @inlineOnce - def lemmaAddNewKeyIncrementSize[B]( - l: List[(Long, B)], - key: Long, - value: B - ): Unit = { - require(invariantList(l)) - require(!containsKey(l, key)) - decreases(l) - - val inserted = insertStrictlySorted(l, key, value) - l match { - case Cons(head, tl) if (head._1 < key) => { - lemmaAddNewKeyIncrementSize(tl, key, value) - - } - case Cons(head, tl) if (head._1 == key) => check(false) - case _ => - } - - }.ensuring(_ => insertStrictlySorted(l, key, value).length == l.length + 1) - - @opaque - @inlineOnce - def lemmaAddExistingKeyPreservesSize[B]( - l: List[(Long, B)], - key: Long, - value: B - ): Unit = { - decreases(l) - require(invariantList(l)) - require(containsKey(l, key)) - - val inserted = insertStrictlySorted(l, key, value) - l match { - case Cons(head, tl) if (head._1 < key) => { - lemmaAddExistingKeyPreservesSize(tl, key, value) - } - case Cons(head, tl) if (head._1 == key) => { - assert(inserted == Cons((key, value), tl)) - } - case _ => - } - - }.ensuring(_ => insertStrictlySorted(l, key, value).length == l.length) - - @opaque - @inlineOnce - def lemmaGetValueByKeyIsDefinedImpliesContainsKey[B]( - l: List[(Long, B)], - key: Long - ): Unit = { - require(invariantList(l) && getValueByKey(l, key).isDefined) - decreases(l) - l match { - case Cons(head, tl) if (head._1 != key) => - lemmaGetValueByKeyIsDefinedImpliesContainsKey(tl, key) - case _ => () - } - }.ensuring(_ => containsKey(l, key)) - - - @opaque - @inlineOnce - def lemmaContainsKeyImpliesGetValueByKeyDefined[B]( - l: List[(Long, B)], - key: Long - ): Unit = { - require(invariantList(l) && containsKey(l, key)) - decreases(l) - l match { - case Cons(head, tl) if (head._1 != key) => - lemmaContainsKeyImpliesGetValueByKeyDefined(tl, key) - case _ => () - } - }.ensuring(_ => getValueByKey(l, key).isDefined) - - @opaque - @inlineOnce - def lemmaGetValueByKeyImpliesContainsTuple[B]( - l: List[(Long, B)], - key: Long, - v: B - ): Unit = { - require(invariantList(l) && getValueByKey(l, key) == Some[B](v)) - decreases(l) - l match { - case Cons(head, tl) if (head._1 != key) => - lemmaGetValueByKeyImpliesContainsTuple(tl, key, v) - case _ => () - } - }.ensuring(_ => l.contains((key, v))) - - @opaque - @inlineOnce - def lemmaForallGetValueByKeySameWithASmallerHead[B]( - l: List[(Long, B)], - keys: List[Long], - value: B, - newHead: (Long, B) - ): Unit = { - require( - invariantList(l) && !l.isEmpty && - keys.forall(getValueByKey(l, _) == Some[B](value)) && - newHead._1 < l.head._1 - ) - decreases(keys) - - keys match { - case Cons(head, tl) => { - lemmaGetValueByKeyIsDefinedImpliesContainsKey(l, head) - lemmaContainsKeyImpliesGetValueByKeyDefined(Cons(newHead, l), head) - lemmaForallGetValueByKeySameWithASmallerHead(l, tl, value, newHead) - } - case _ => () - } - - }.ensuring(_ => keys.forall(k => getValueByKey(Cons(newHead, l), k) == Some[B](value))) - - @opaque - @inlineOnce - def lemmaInsertStrictlySortedDoesNotModifyOtherKeyValues[B]( - l: List[(Long, B)], - newKey: Long, - newValue: B, - otherKey: Long - ): Unit = { - require(invariantList(l) && newKey != otherKey) - decreases(l) - - l match { - case Cons(head, tl) if (head._1 != otherKey) => - lemmaInsertStrictlySortedDoesNotModifyOtherKeyValues( - tl, - newKey, - newValue, - otherKey - ) - case _ => () - } - - }.ensuring(_ => - containsKey( - insertStrictlySorted(l, newKey, newValue), - otherKey - ) == containsKey(l, otherKey) && - getValueByKey( - insertStrictlySorted(l, newKey, newValue), - otherKey - ) == getValueByKey( - l, - otherKey - ) - ) - - @opaque - @inlineOnce - def lemmaInsertStrictlySortedDoesNotModifyOtherKeysNotContained[B]( - l: List[(Long, B)], - newKey: Long, - newValue: B, - otherKey: Long - ): Unit = { - require(invariantList(l) && !containsKey(l, otherKey) && otherKey != newKey) - decreases(l) - - l match { - case Cons(head, tl) => - lemmaInsertStrictlySortedDoesNotModifyOtherKeysNotContained( - tl, - newKey, - newValue, - otherKey - ) - case _ => () - } - }.ensuring(_ => !containsKey(insertStrictlySorted(l, newKey, newValue), otherKey)) - - @opaque - @inlineOnce - def lemmaInsertStrictlySortedDoesNotModifyOtherKeysContained[B]( - l: List[(Long, B)], - newKey: Long, - newValue: B, - otherKey: Long - ): Unit = { - require(invariantList(l) && containsKey(l, otherKey) && otherKey != newKey) - decreases(l) - - l match { - case Cons(head, tl) if (head._1 != otherKey) => - lemmaInsertStrictlySortedDoesNotModifyOtherKeysContained( - tl, - newKey, - newValue, - otherKey - ) - case _ => () - } - }.ensuring(_ => containsKey(insertStrictlySorted(l, newKey, newValue), otherKey)) - - @opaque - @inlineOnce - def lemmaInsertStrictlySortedNotContainedContent[B]( - @induct l: List[(Long, B)], - newKey: Long, - newValue: B - ): Unit = { - require(invariantList(l)) - require(!containsKey(l, newKey)) - - } ensuring (_ => - l.content ++ Set((newKey, newValue)) == insertStrictlySorted( - l, - newKey, - newValue - ).content - ) - - @opaque - @inlineOnce - def lemmaNotContainsKeyThenNotContainsTuple[B]( - @induct l: List[(Long, B)], - key: Long, - value: B - ): Unit = { - require(invariantList(l) && !containsKey(l, key)) - - }.ensuring(_ => !l.contains((key, value))) - - @opaque - @inlineOnce - def lemmaContainsTupleThenContainsKey[B]( - l: List[(Long, B)], - key: Long, - value: B - ): Unit = { - require(invariantList(l) && l.contains((key, value))) - decreases(l) - - l match { - case Cons(head, tl) if (head != (key, value)) => - lemmaContainsTupleThenContainsKey(tl, key, value) - case _ => () - } - }.ensuring(_ => containsKey(l, key)) - - @opaque - @inlineOnce - def lemmaContainsTupThenGetReturnValue[B]( - l: List[(Long, B)], - key: Long, - value: B - ): Unit = { - require(invariantList(l) && containsKey(l, key) && l.contains((key, value))) - decreases(l) - - l match { - case head :: Nil() => () - case Cons(head, tl) if (head._1 == key) => - lemmaNotContainsKeyThenNotContainsTuple(tl, key, value) - case Cons(head, tl) => lemmaContainsTupThenGetReturnValue(tl, key, value) - case Nil() => () - } - }.ensuring(_ => getValueByKey(l, key) == Some[B](value)) -} - -object ListLongMap { - def empty[B]: ListLongMap[B] = ListLongMap[B](List.empty[(Long, B)]) -} - -object ListLongMapLemmas { - import ListSpecs._ - - @opaque - @inlineOnce - def removeNotPresentStillSame[B](lm: ListLongMap[B], a: Long): Unit = { - require(!lm.contains(a)) - TupleListOps.lemmaRemoveStrictlySortedNotPresentPreserves(lm.toList, a) - }.ensuring(_ => lm - a == lm) - - @opaque - @inlineOnce - def addSameAsAddTwiceSameKeyDiffValues[B]( - lm: ListLongMap[B], - a: Long, - b1: B, - b2: B - ): Unit = { - TupleListOps.lemmaInsertStrictlySortedErasesIfSameKey(lm.toList, a, b1, b2) - }.ensuring(_ => lm + (a, b2) == (lm + (a, b1) + (a, b2))) - - @opaque - @inlineOnce - def addRemoveCommutativeForDiffKeys[B]( - lm: ListLongMap[B], - a1: Long, - b1: B, - a2: Long - ): Unit = { - require(a1 != a2) - TupleListOps.lemmaInsertAndRemoveStrictlySortedCommutative( - lm.toList, - a1, - b1, - a2 - ) - }.ensuring(_ => lm + (a1, b1) - a2 == lm - a2 + (a1, b1)) - - @opaque - @inlineOnce - def addThenRemoveForNewKeyIsSame[B]( - lm: ListLongMap[B], - a1: Long, - b1: B - ): Unit = { - require(!lm.contains(a1)) - TupleListOps.lemmaInsertStrictlySortedThenRemoveIsSame(lm.toList, a1, b1) - }.ensuring(_ => lm + (a1, b1) - a1 == lm) - - @opaque - @inlineOnce - def removeCommutative[B](lm: ListLongMap[B], a1: Long, a2: Long): Unit = { - TupleListOps.lemmaRemoveStrictlySortedCommutative(lm.toList, a1, a2) - }.ensuring(_ => lm - a1 - a2 == lm - a2 - a1) - - @opaque - @inlineOnce - def addCommutativeForDiffKeys[B]( - lm: ListLongMap[B], - a1: Long, - b1: B, - a2: Long, - b2: B - ): Unit = { - require(a1 != a2) - TupleListOps.lemmaInsertStrictlySortedCommutative(lm.toList, a1, b1, a2, b2) - }.ensuring(_ => lm + (a1, b1) + (a2, b2) == lm + (a2, b2) + (a1, b1)) - - @opaque - @inlineOnce - def emptyContainsNothing[B](k: Long): Unit = {}.ensuring(_ => !ListLongMap.empty[B].contains(k)) - - @opaque - @inlineOnce - def addValidProp[B]( - lm: ListLongMap[B], - p: ((Long, B)) => Boolean, - a: Long, - b: B - ): Unit = { - require(lm.forall(p) && p(a, b)) - decreases(lm.toList.size) - - if (!lm.isEmpty) - addValidProp(lm.tail, p, a, b) - - }.ensuring { _ => - val nlm = lm + (a, b) - nlm.forall(p) - } - - @opaque - @inlineOnce - def removeValidProp[B]( - lm: ListLongMap[B], - p: ((Long, B)) => Boolean, - a: Long - ): Unit = { - require(lm.forall(p)) - decreases(lm.toList.size) - if (!lm.isEmpty) - removeValidProp(lm.tail, p, a) - - }.ensuring { _ => - val nlm = lm - a - nlm.forall(p) - } - - @opaque - @inlineOnce - def insertAllValidProp[B]( - lm: ListLongMap[B], - kvs: List[(Long, B)], - p: ((Long, B)) => Boolean - ): Unit = { - require(lm.forall(p) && kvs.forall(p)) - decreases(kvs) - - if (!kvs.isEmpty) { - addValidProp(lm, p, kvs.head._1, kvs.head._2) - insertAllValidProp(lm + kvs.head, kvs.tail, p) - } - - }.ensuring { _ => - val nlm = lm ++ kvs - nlm.forall(p) - } - - @opaque - @inlineOnce - def removeAllValidProp[B]( - lm: ListLongMap[B], - l: List[Long], - p: ((Long, B)) => Boolean - ): Unit = { - require(lm.forall(p)) - decreases(l) - - if (!l.isEmpty) { - removeValidProp(lm, p, l.head) - removeAllValidProp(lm - l.head, l.tail, p) - } - - }.ensuring { _ => - val nlm = lm -- l - nlm.forall(p) - } - - @opaque - @inlineOnce - def addApplyDifferent[B]( - lm: ListLongMap[B], - a: Long, - b: B, - a0: Long - ): Unit = { - require(lm.contains(a0) && a0 != a) - assert(TupleListOps.containsKey(lm.toList, a0)) - TupleListOps.lemmaInsertStrictlySortedDoesNotModifyOtherKeyValues( - lm.toList, - a, - b, - a0 - ) - TupleListOps.lemmaContainsKeyImpliesGetValueByKeyDefined(lm.toList, a0) - - }.ensuring(_ => (lm + (a -> b))(a0) == lm(a0)) - - @opaque - @inlineOnce - def addStillContains[B]( - lm: ListLongMap[B], - a: Long, - b: B, - a0: Long - ): Unit = { - require(lm.contains(a0)) - - if (a != a0) - TupleListOps.lemmaInsertStrictlySortedDoesNotModifyOtherKeysContained( - lm.toList, - a, - b, - a0 - ) - - }.ensuring(_ => (lm + (a, b)).contains(a0)) - - @opaque - @inlineOnce - def addStillNotContains[B]( - lm: ListLongMap[B], - a: Long, - b: B, - a0: Long - ): Unit = { - require(!lm.contains(a0) && a != a0) - - TupleListOps.lemmaInsertStrictlySortedDoesNotModifyOtherKeysNotContained( - lm.toList, - a, - b, - a0 - ) - - }.ensuring(_ => !(lm + (a, b)).contains(a0)) - - @opaque - @inlineOnce - def applyForall[B]( - lm: ListLongMap[B], - p: ((Long, B)) => Boolean, - k: Long - ): Unit = { - require(lm.forall(p) && lm.contains(k)) - decreases(lm.toList.size) - - if (!lm.isEmpty && lm.toList.head._1 != k) - applyForall(lm.tail, p, k) - - }.ensuring(_ => p(k, lm(k))) - - @opaque - @inlineOnce - def getForall[B]( - lm: ListLongMap[B], - p: ((Long, B)) => Boolean, - k: Long - ): Unit = { - require(lm.forall(p)) - decreases(lm.toList.size) - - if (!lm.isEmpty && lm.toList.head._1 != k) - getForall(lm.tail, p, k) - - }.ensuring(_ => lm.get(k).forall(v => p(k, v))) - - @opaque - @inlineOnce - def uniqueImage[B](lm: ListLongMap[B], a: Long, b: B): Unit = { - require(lm.toList.contains((a, b))) - - TupleListOps.lemmaContainsTupleThenContainsKey(lm.toList, a, b) - TupleListOps.lemmaContainsTupThenGetReturnValue(lm.toList, a, b) - - }.ensuring(_ => lm.get(a) == Some[B](b)) - - @opaque - @inlineOnce - def lemmaGetValueImpliesTupleContained[B](lm: ListLongMap[B], a: Long, b: B): Unit = { - require(lm.contains(a)) - require(lm.get(a) == Some[B](b)) - - TupleListOps.lemmaGetValueByKeyImpliesContainsTuple(lm.toList, a, b) - } ensuring (_ => lm.toList.contains((a, b))) - - @opaque - def keysOfSound[B](@induct lm: ListLongMap[B], value: B): Unit = { - // trivial by postcondition of getKeysOf - assert(TupleListOps.getKeysOf(lm.toList, value).forall(k => lm.get(k) == Some[B](value))) - }.ensuring(_ => lm.keysOf(value).forall(key => lm.get(key) == Some[B](value))) - - @opaque - @inlineOnce - def addNotContainedContent[B]( - lm: ListLongMap[B], - key: Long, - value: B - ): Unit = { - require(!lm.contains(key)) - TupleListOps.lemmaInsertStrictlySortedNotContainedContent( - lm.toList, - key, - value - ) - } ensuring (_ => - lm.toList.content ++ Set( - (key, value) - ) == (lm + (key, value)).toList.content - ) -} diff --git a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/chassot/ListLongMap.scala b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/chassot/ListLongMap.scala new file mode 120000 index 00000000..47da45f9 --- /dev/null +++ b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/chassot/ListLongMap.scala @@ -0,0 +1 @@ +../../../../../../../../../data-structures/maps/mutablemaps/src/main/scala/ch/epfl/chassot/ListLongMap.scala \ No newline at end of file diff --git a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/chassot/ListMap.scala b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/chassot/ListMap.scala deleted file mode 100644 index 84dfe2e4..00000000 --- a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/chassot/ListMap.scala +++ /dev/null @@ -1,1565 +0,0 @@ -/** Author: Samuel Chassot - */ - -package ch.epfl.chassot - -import stainless.annotation._ -import stainless.collection._ -import stainless.equations._ -import stainless.lang._ -import stainless.proof.check -import scala.annotation.tailrec -import scala.collection.immutable -import stainless.collection.ListOps.noDuplicate -import scala.collection.mutable - -// Uncomment the following import to run benchmarks -// import OptimisedChecks.* - -case class ListMap[K, B](toList: List[(K, B)]) { - require(TupleListOpsGenK.invariantList(toList)) - - def isEmpty: Boolean = toList.isEmpty - - def eq(other: ListMap[K, B]): Boolean = toList.content == other.toList.content - - def head: (K, B) = { - require(!isEmpty) - toList.head - } - - def size: Int = { - require(toList.size < Integer.MAX_VALUE) - TupleListOpsGenK.intSize(toList) - } - - @pure - def nKeys: Int = { - require(toList.size < Integer.MAX_VALUE) - TupleListOpsGenK.intSizeKeys(TupleListOpsGenK.getKeysList(toList)) - } - - def tail: ListMap[K, B] = { - require(!isEmpty) - ListMap(toList.tail) - } - - def contains(key: K): Boolean = { - val res = TupleListOpsGenK.containsKey(toList, key) - if (res) { - TupleListOpsGenK.lemmaContainsKeyImpliesGetValueByKeyDefined(toList, key) - } - res - - }.ensuring(res => !res || this.get(key).isDefined) - - @inline - def get(key: K): Option[B] = { - TupleListOpsGenK.getValueByKey(toList, key) - } - - @inline - def keysOf(value: B): List[K] = { - TupleListOpsGenK.getKeysOf(toList, value) - } - - def keys(): List[K] = { - TupleListOpsGenK.getKeysList(toList) - } - - def apply(key: K): B = { - require(contains(key)) - get(key).get - } - - def +(keyValue: (K, B)): ListMap[K, B] = { - val newList = - TupleListOpsGenK.insertNoDuplicatedKeys(toList, keyValue._1, keyValue._2) - - TupleListOpsGenK.lemmaContainsTupThenGetReturnValue( - newList, - keyValue._1, - keyValue._2 - ) - ListMap(newList) - - }.ensuring(res => - res.contains(keyValue._1) && res.get(keyValue._1) == Some[B]( - keyValue._2 - ) && res.toList.contains(keyValue) - ) - - def ++(keyValues: List[(K, B)]): ListMap[K, B] = { - decreases(keyValues) - keyValues match { - case Nil() => this - case Cons(keyValue, rest) => (this + keyValue) ++ rest - } - } - def -(key: K): ListMap[K, B] = { - ListMap(TupleListOpsGenK.removePresrvNoDuplicatedKeys(toList, key)) - }.ensuring(res => !res.contains(key)) - - def --(keys: List[K]): ListMap[K, B] = { - decreases(keys) - keys match { - case Nil() => this - case Cons(key, rest) => (this - key) -- rest - } - } - - inline def forall(p: ((K, B)) => Boolean): Boolean = { - toList.forall(p) - } -} - -object TupleListOpsGenK { - - // @inline - inline def invariantList[K, B](l: List[(K, B)]): Boolean = { - noDuplicatedKeys(l) - } - - def getKeysList[K, B](l: List[(K, B)]): List[K] = { - require(invariantList(l)) - decreases(l) - l match { - case Cons(head, tl) => - if (containsKey(tl, head._1)) { - check(false) - } - if (getKeysList(tl).contains(head._1)) { - ListSpecs.forallContained(getKeysList(tl), k => containsKey(tl, k), head._1) - check(false) - } - assert(noDuplicate(getKeysList(tl)) && getKeysList(tl).forall(k => containsKey(tl, k))) - assert(noDuplicate(Cons(head._1, getKeysList(tl)))) - assert(getKeysList(tl).forall(k => containsKey(tl, k))) - lemmaForallContainsAddHeadPreserves(tl, getKeysList(tl), head) - assert(getKeysList(tl).forall(k => containsKey(Cons(head, tl), k))) - assert(Cons(head._1, getKeysList(tl)).forall(k => containsKey(Cons(head, tl), k))) - Cons(head._1, getKeysList(tl)) - case Nil() => Nil[K]() - } - }.ensuring(res => noDuplicate(res) && res.length == l.length && res.forall(k => containsKey(l, k))) - - @pure - def intSizeKeys[K](l: List[K]): Int = { - require(l.length < Integer.MAX_VALUE) - decreases(l) - - l match { - case Cons(head, tl) => 1 + intSizeKeys(tl) - case Nil() => 0 - } - } - - def intSize[K, B](l: List[(K, B)]): Int = { - decreases(l) - l match { - case Cons(head, tl) => { - val s1 = intSize(tl) - if (s1 < Integer.MAX_VALUE) { - 1 + s1 - } else { - 0 - } - } - - case Nil() => 0 - } - }.ensuring(res => res >= 0) - - def getKeysOf[K, B](l: List[(K, B)], value: B): List[K] = { - require(invariantList(l)) - decreases(l) - - l match { - case Cons(head, tl) if (head._2 == value) => { - if (!getKeysOf(tl, value).isEmpty) { - lemmaForallGetValueByKeySameWithADiffHead( - tl, - getKeysOf(tl, value), - value, - head - ) - - } - Cons(head._1, getKeysOf(tl, value)) - } - case Cons(head, tl) if (head._2 != value) => { - val r = getKeysOf(tl, value) - if (!getKeysOf(tl, value).isEmpty) { - lemmaForallGetValueByKeySameWithADiffHead( - tl, - getKeysOf(tl, value), - value, - head - ) - } - getKeysOf(tl, value) - } - case Nil() => Nil[K]() - } - - }.ensuring(res => res.forall(getValueByKey(l, _) == Some[B](value))) - - def filterByValue[K, B](l: List[(K, B)], value: B): List[(K, B)] = { - require(invariantList(l)) - decreases(l) - - l match { - case Cons(head, tl) if (head._2 == value) => - val res = head :: filterByValue(tl, value) - if (containsKey(filterByValue(tl, value), head._1)) { - assert(ListSpecs.subseq(filterByValue(tl, value), tl)) - lemmaContainsKeyImpliesGetValueByKeyDefined(filterByValue(tl, value), head._1) - val va = getValueByKey(filterByValue(tl, value), head._1).get - lemmaGetValueByKeyImpliesContainsTuple(filterByValue(tl, value), head._1, va) - ListSpecs.subseqContains(filterByValue(tl, value), tl, (head._1, va)) - assert(tl.contains((head._1, va))) - lemmaContainsTupleThenContainsKey(tl, head._1, va) - assert(containsKey(tl, head._1)) - check(false) - } - assert(!containsKey(filterByValue(tl, value), head._1)) - res - case Cons(head, tl) if (head._2 != value) => - val res = filterByValue(tl, value) - res - case Nil() => Nil[(K, B)]() - } - }.ensuring(res => res.forall(_._2 == value) && ListSpecs.subseq(res, l) && invariantList(res)) - - def getValueByKey[K, B](l: List[(K, B)], key: K): Option[B] = { - require(invariantList(l)) - decreases(l) - - l match { - case Cons(head, tl) if (head._1 == key) => Some[B](head._2) - case Cons(head, tl) if (head._1 != key) => getValueByKey(tl, key) - case Nil() => None[B]() - } - - } - - def insertNoDuplicatedKeys[K, B]( - l: List[(K, B)], - newKey: K, - newValue: B - ): List[(K, B)] = { - require(invariantList(l)) - decreases(l) - - l match { - case Cons(head, tl) if (head._1 == newKey) => - lemmaSubseqRefl(getKeysList(l)) - (newKey, newValue) :: tl - case Cons(head, tl) if (!containsKey(l, newKey)) => (newKey, newValue) :: l - case Cons(head, tl) => - assert(containsKey(tl, newKey)) - val res = head :: insertNoDuplicatedKeys(tl, newKey, newValue) - if (containsKey(insertNoDuplicatedKeys(tl, newKey, newValue), head._1)) { - assert(head._1 != newKey) - assert(getKeysList(tl).content ++ Set(newKey) == getKeysList(insertNoDuplicatedKeys(tl, newKey, newValue)).content) - lemmaInListThenGetKeysListContains(insertNoDuplicatedKeys(tl, newKey, newValue), head._1) - assert(getKeysList(insertNoDuplicatedKeys(tl, newKey, newValue)).contains(head._1)) - assert(getKeysList(tl).contains(head._1)) - lemmaInGetKeysListThenContainsKey(tl, head._1) - assert(containsKey(tl, head._1)) - check(false) - } - assert(invariantList(res)) - assert(containsKey(res, newKey)) - assert(res.contains((newKey, newValue))) - res - case Nil() => (newKey, newValue) :: Nil() - } - }.ensuring(res => - invariantList(res) && containsKey(res, newKey) && - res.contains((newKey, newValue)) && - getKeysList(res).content == getKeysList(l).content ++ Set(newKey) - ) - - def removePresrvNoDuplicatedKeys[K, B]( - l: List[(K, B)], - key: K - ): List[(K, B)] = { - require(invariantList(l)) - decreases(l) - l match { - case Cons(head, tl) if (head._1 == key) => - if(containsKey(l, key)){ - val h = (key, getValueByKey(l, key).get) - assert(l.head == (key, getValueByKey(l, key).get)) - if(tl.contains(h)){ - lemmaContainsTupleThenContainsKey(tl, h._1, h._2) - check(false) - } - assert(!tl.contains(head)) - check(tl.content == l.content - (key, getValueByKey(l, key).get)) - } else { - check(tl.content == l.content) - } - tl - case Cons(head, tl) if (head._1 != key) => - val res = head :: removePresrvNoDuplicatedKeys(tl, key) - if(getKeysList(tl).contains(head._1)){ - lemmaInGetKeysListThenContainsKey(tl, head._1) - check(false) - } - if(containsKey(removePresrvNoDuplicatedKeys(tl, key), head._1)){ - lemmaInListThenGetKeysListContains(removePresrvNoDuplicatedKeys(tl, key), head._1) - check(false) - } - res - case Nil() => Nil[(K, B)]() - } - }.ensuring(res => - invariantList(res) && - !containsKey(res, key) && - getKeysList(res).content == getKeysList(l).content -- Set(key) && - (if(containsKey(l, key)) { - lemmaContainsKeyImpliesGetValueByKeyDefined(l, key) - res.content == l.content - (key, getValueByKey(l, key).get) - } else { - res.content == l.content - }) - ) - - def noDuplicatedKeys[K, B](l: List[(K, B)]): Boolean = { - decreases(l) - l match { - case Nil() => true - case Cons(_, Nil()) => true - case Cons(h1, t) if (containsKey(t, h1._1)) => false - case Cons(_, t) => noDuplicatedKeys(t) - } - } - - def containsKey[K, B](l: List[(K, B)], key: K): Boolean = { - decreases(l) - l match { - case Cons(head, _) if (head._1 == key) => true - case Cons(_, tl) => containsKey(tl, key) - case Nil() => false - - } - } - - // ----------- LEMMAS ----------------------------------------------------- - - @opaque - @inlineOnce - def lemmaForallSubset[K, B]( - l1: List[(K, B)], - l2: List[(K, B)], - p: ((K, B)) => Boolean - ): Unit = { - require(invariantList(l1)) - require(invariantList(l2)) - require(l2.forall(p) && l1.content.subsetOf(l2.content)) - decreases(l1) - - l1 match { - case Cons(head, tl) => - ListSpecs.subsetContains(l1, l2) - ListSpecs.forallContained(l1, l2.contains , head) - ListSpecs.forallContained(l2, p, head) - assert(l2.contains(head)) - lemmaForallSubset(tl, l2, p) - assert(tl.forall(p)) - assert(p(head)) - case Nil() => () - } - }.ensuring(_ => l1.forall(p)) - - - @opaque - @inlineOnce - def lemmaInsertNoDuplicatedKeysPreservesForall[K, B]( - l: List[(K, B)], - key: K, - value: B, - p: ((K, B)) => Boolean - ): Unit = { - require(invariantList(l)) - require(l.forall(p)) - require(p((key, value))) - decreases(l) - - l match { - case Cons(head, tl) if (head._1 != key) => - lemmaInsertNoDuplicatedKeysPreservesForall(tl, key, value, p) - case _ => () - } - - }.ensuring(_ => insertNoDuplicatedKeys(l, key, value).forall(p)) - - - @opaque - @inlineOnce - def lemmaContainsTwoDifferentTuplesSameKeyImpossible[K, B]( - l: List[(K, B)], - key: K, - v1: B, - v2: B - ): Unit = { - require(l.contains((key, v1)) && l.contains((key, v2))) - require(invariantList(l)) - decreases(l) - - l match { - case Cons(head, tl) if (head._1 != key) => - lemmaContainsTwoDifferentTuplesSameKeyImpossible(tl, key, v1, v2) - case Cons(head, tl) if (head._1 == key) => - if (head._2 == v1) { - if(v1 != v2){ - lemmaContainsTupleThenContainsKey(tl, key, v2) - check(false) - } - } else { - if(head._2 != v2){ - lemmaContainsTupleThenContainsKey(tl, key, v2) - lemmaContainsTupleThenContainsKey(tl, key, v1) - check(false) - } - assert(head._2 == v2) - if (v1 != v2) { - lemmaContainsTupleThenContainsKey(tl, key, v1) - check(false) - } - } - case _ => () - } - }.ensuring(_ => v1 == v2) - - @opaque - @inlineOnce - def lemmaInListThenGetKeysListContains[K, B](l: List[(K, B)], key: K): Unit = { - require(invariantList(l)) - require(containsKey(l, key)) - decreases(l) - - l match { - case Cons(head, tl) if (head._1 != key) => - lemmaInListThenGetKeysListContains(tl, key) - case _ => () - } - }.ensuring(_ => getKeysList(l).contains(key)) - - @inlineOnce - @opaque - def lemmaInGetKeysListThenContainsKey[K, B](l: List[(K, B)], key: K): Unit = { - require(invariantList(l)) - require(getKeysList(l).contains(key)) - decreases(l) - - l match { - case Cons(head, tl) if (head._1 != key) => - lemmaInGetKeysListThenContainsKey(tl, key) - case _ => () - } - }.ensuring(_ => containsKey(l, key)) - - @inlineOnce - @opaque - def lemmaSubseqRefl[B](l: List[B]): Unit = { - decreases(l.size) - l match { - case Nil() => () - case Cons(hd, tl) => lemmaSubseqRefl(tl) - } - }.ensuring (_ => ListSpecs.subseq(l, l)) - - @opaque - @inlineOnce - def lemmaForallContainsAddHeadPreserves[K, B](l: List[(K, B)], keys: List[K], other: (K, B)): Unit = { - require(invariantList(l)) - require(keys.forall(k => containsKey(l, k))) - require(!containsKey(l, other._1)) - decreases(keys) - - keys match { - case Cons(head, tl) => { - lemmaAddHeadStillContainsKey(l, other._1, other._2, head) - lemmaForallContainsAddHeadPreserves(l, tl, other) - } - case _ => () - } - - }.ensuring (_ => keys.forall(k => containsKey(Cons(other, l), k))) - - @opaque - @inlineOnce - def lemmaGetValueByKeyImpliesContainsTuple[K, B](l: List[(K, B)], key: K, v: B): Unit = { - require(invariantList(l)) - require(getValueByKey(l, key) == Some[B](v)) - decreases(l) - l match { - case Cons(head, tl) if (head._1 != key) => - lemmaGetValueByKeyImpliesContainsTuple(tl, key, v) - case _ => () - } - }.ensuring(_ => l.contains((key, v))) - - @opaque - @inlineOnce - def lemmaInsertAndremovePresrvNoDuplicatedKeysCommutative[K, B]( - l: List[(K, B)], - key1: K, - v1: B, - key2: K - ): Unit = { - require(key1 != key2) - require(invariantList(l)) - decreases(l) - - l match { - case Cons(head, tl) => { - lemmaInsertAndremovePresrvNoDuplicatedKeysCommutative(tl, key1, v1, key2) - } - case _ => () - } - - }.ensuring(_ => - insertNoDuplicatedKeys( - removePresrvNoDuplicatedKeys(l, key2), - key1, - v1 - ) == removePresrvNoDuplicatedKeys( - insertNoDuplicatedKeys(l, key1, v1), - key2 - ) - ) - - @opaque - @inlineOnce - def lemmainsertNoDuplicatedKeysThenRemoveIsSame[K, B]( - l: List[(K, B)], - key1: K, - v1: B - ): Unit = { - require(invariantList(l)) - require(!containsKey(l, key1)) - decreases(l) - - l match { - case Cons(head, tl) => { - lemmaTailStillNotContainsKey(l, key1) - lemmainsertNoDuplicatedKeysThenRemoveIsSame(tl, key1, v1) - } - case _ => () - } - - }.ensuring(_ => removePresrvNoDuplicatedKeys(insertNoDuplicatedKeys(l, key1, v1), key1) == l) - - @opaque - @inlineOnce - def lemmaRemoveTheninsertNoDuplicatedKeysIsSameAsInsert[K, B]( - l: List[(K, B)], - key1: K, - v1: B - ): Unit = { - require(invariantList(l)) - decreases(l) - - l match { - case Cons(head, tl) if (head._1 == key1) => () - case Cons(head, tl) => { - lemmaRemoveTheninsertNoDuplicatedKeysIsSameAsInsert(tl, key1, v1) - } - case _ => () - } - - }.ensuring(_ => insertNoDuplicatedKeys(removePresrvNoDuplicatedKeys(l, key1), key1, v1).content == insertNoDuplicatedKeys(l, key1, v1).content) - - @opaque - @inlineOnce - def lemmainsertNoDuplicatedKeysCommutative[K, B]( - l: List[(K, B)], - key1: K, - v1: B, - key2: K, - v2: B - ): Unit = { - require(key1 != key2) - require(invariantList(l)) - decreases(l) - - l match { - case Cons(head, tl) if (head._1 == key1) => () - case Cons(head, tl) => { - lemmainsertNoDuplicatedKeysCommutative(tl, key1, v1, key2, v2) - } - case _ => () - } - - }.ensuring(_ => - insertNoDuplicatedKeys( - insertNoDuplicatedKeys(l, key1, v1), - key2, - v2 - ).content == insertNoDuplicatedKeys( - insertNoDuplicatedKeys(l, key2, v2), - key1, - v1 - ).content - ) - - @opaque - @inlineOnce - def lemmaremovePresrvNoDuplicatedKeysCommutative[K, B]( - l: List[(K, B)], - key1: K, - key2: K - ): Unit = { - require(invariantList(l)) - decreases(l) - - l match { - case Cons(head, tl) => { - lemmaremovePresrvNoDuplicatedKeysCommutative(tl, key1, key2) - } - case _ => () - } - - }.ensuring(_ => - removePresrvNoDuplicatedKeys( - removePresrvNoDuplicatedKeys(l, key1), - key2 - ) == removePresrvNoDuplicatedKeys( - removePresrvNoDuplicatedKeys(l, key2), - key1 - ) - ) - - @opaque - @inlineOnce - def lemmaremovePresrvNoDuplicatedKeysNotPresentPreserves[K, B]( - l: List[(K, B)], - key: K - ): Unit = { - require(invariantList(l)) - require(!containsKey(l, key)) - decreases(l) - - l match { - case Cons(head, tl) => { - lemmaTailStillNotContainsKey(l, key) - assert(!containsKey(tl, key)) - lemmaremovePresrvNoDuplicatedKeysNotPresentPreserves(tl, key) - } - case _ => () - } - - }.ensuring(_ => removePresrvNoDuplicatedKeys(l, key) == l) - - @opaque - @inlineOnce - def lemmainsertNoDuplicatedKeysErasesIfSameKey[K, B]( - l: List[(K, B)], - key1: K, - v1: B, - v2: B - ): Unit = { - require(invariantList(l)) - decreases(l) - - l match { - case Cons(head, tl) => { - lemmainsertNoDuplicatedKeysErasesIfSameKey(tl, key1, v1, v2) - } - case _ => () - } - - }.ensuring(_ => - insertNoDuplicatedKeys( - insertNoDuplicatedKeys(l, key1, v1), - key1, - v2 - ) == insertNoDuplicatedKeys( - l, - key1, - v2 - ) - ) - - @opaque - @inlineOnce - def lemmaAddNewKeyIncrementSize[K, B]( - l: List[(K, B)], - key: K, - value: B - ): Unit = { - require(invariantList(l)) - require(!containsKey(l, key)) - decreases(l) - - val inserted = insertNoDuplicatedKeys(l, key, value) - l match { - case Cons(head, tl) if (head._1 != key) => { - lemmaAddNewKeyIncrementSize(tl, key, value) - - } - case Cons(head, tl) if (head._1 == key) => check(false) - case _ => - } - - }.ensuring(_ => insertNoDuplicatedKeys(l, key, value).length == l.length + 1) - - @opaque - @inlineOnce - def lemmaAddExistingKeyPreservesSize[K, B]( - l: List[(K, B)], - key: K, - value: B - ): Unit = { - decreases(l) - require(invariantList(l)) - require(containsKey(l, key)) - - val inserted = insertNoDuplicatedKeys(l, key, value) - l match { - case Cons(head, tl) if (head._1 != key) => { - lemmaAddExistingKeyPreservesSize(tl, key, value) - } - case Cons(head, tl) if (head._1 == key) => { - assert(inserted == Cons((key, value), tl)) - } - case _ => - } - - }.ensuring(_ => insertNoDuplicatedKeys(l, key, value).length == l.length) - - @opaque - @inlineOnce - def lemmaGetValueByKeyIsDefinedImpliesContainsKey[K, B]( - l: List[(K, B)], - key: K - ): Unit = { - require(invariantList(l) && getValueByKey(l, key).isDefined) - decreases(l) - l match { - case Cons(head, tl) if (head._1 != key) => - lemmaGetValueByKeyIsDefinedImpliesContainsKey(tl, key) - lemmaAddHeadStillContainsKey(tl, head._1, head._2, key) - case _ => () - } - }.ensuring(_ => containsKey(l, key)) - - @opaque - @inlineOnce - def lemmaContainsKeyImpliesGetValueByKeyDefined[K, B]( - l: List[(K, B)], - key: K - ): Unit = { - require(invariantList(l)) - require(containsKey(l, key)) - decreases(l) - l match { - case Cons(head, tl) if (head._1 != key) => - lemmaContainsKeyImpliesGetValueByKeyDefined(tl, key) - case _ => () - } - }.ensuring(_ => getValueByKey(l, key).isDefined) - - @opaque - @inlineOnce - def lemmaForallGetValueByKeySameWithADiffHead[K, B]( - l: List[(K, B)], - keys: List[K], - value: B, - newHead: (K, B) - ): Unit = { - require(invariantList(l)) - require(!l.isEmpty) - require(keys.forall(getValueByKey(l, _) == Some[B](value))) - require(!containsKey(l, newHead._1)) - decreases(keys) - - keys match { - case Cons(head, tl) => { - lemmaGetValueByKeyIsDefinedImpliesContainsKey(l, head) - lemmaAddHeadStillContainsKey(l, newHead._1, newHead._2, head) - lemmaContainsKeyImpliesGetValueByKeyDefined(Cons(newHead, l), head) - lemmaForallGetValueByKeySameWithADiffHead(l, tl, value, newHead) - } - case _ => () - } - - }.ensuring(_ => keys.forall(k => getValueByKey(Cons(newHead, l), k) == Some[B](value))) - - @opaque - @inlineOnce - def lemmaAddHeadStillContainsKey[K, B]( - l: List[(K, B)], - key: K, - value: B, - test: K - ): Unit = { - require(invariantList(l)) - require(containsKey(l, test)) - require(!containsKey(l, key)) - decreases(l) - - l match { - case Cons(head, tl) if (head._1 != test) => - lemmaAddHeadStillContainsKey(tl, key, value, test) - case _ => () - } - - }.ensuring(_ => containsKey(Cons((key, value), l), test)) - - @opaque - @inlineOnce - def lemmaTailStillNotContainsKey[K, B]( - l: List[(K, B)], - test: K - ): Unit = { - require(invariantList(l)) - require(!containsKey(l, test)) - require(!l.isEmpty) - decreases(l) - - l match { - case Cons(head, Nil()) => () - case Cons(head, tl) if (head._1 != test) => - if (containsKey(tl, test)) { - lemmaAddHeadStillContainsKey(tl, head._1, head._2, test) - check(false) - } - lemmaTailStillNotContainsKey(tl, test) - case _ => () - } - - }.ensuring(_ => !containsKey(l.tail, test)) - - @opaque - @inlineOnce - def lemmainsertNoDuplicatedKeysDoesNotModifyOtherKeyValues[K, B]( - l: List[(K, B)], - newKey: K, - newValue: B, - otherKey: K - ): Unit = { - require(invariantList(l) && newKey != otherKey) - decreases(l) - - l match { - case Cons(head, tl) if (head._1 != otherKey) => - lemmainsertNoDuplicatedKeysDoesNotModifyOtherKeyValues( - tl, - newKey, - newValue, - otherKey - ) - case _ => () - } - - }.ensuring(_ => - containsKey( - insertNoDuplicatedKeys(l, newKey, newValue), - otherKey - ) == containsKey(l, otherKey) && - getValueByKey( - insertNoDuplicatedKeys(l, newKey, newValue), - otherKey - ) == getValueByKey( - l, - otherKey - ) - ) - - @opaque - @inlineOnce - def lemmainsertNoDuplicatedKeysDoesNotModifyOtherKeysNotContained[K, B]( - l: List[(K, B)], - newKey: K, - newValue: B, - otherKey: K - ): Unit = { - require(invariantList(l)) - require(!containsKey(l, otherKey)) - require(otherKey != newKey) - decreases(l) - - l match { - case Cons(head, tl) => - lemmaTailStillNotContainsKey(l, otherKey) - assert(!containsKey(tl, otherKey)) - lemmainsertNoDuplicatedKeysDoesNotModifyOtherKeysNotContained( - tl, - newKey, - newValue, - otherKey - ) - case _ => () - } - }.ensuring(_ => !containsKey(insertNoDuplicatedKeys(l, newKey, newValue), otherKey)) - - @opaque - @inlineOnce - def lemmainsertNoDuplicatedKeysDoesNotModifyOtherKeysContained[K, B]( - l: List[(K, B)], - newKey: K, - newValue: B, - otherKey: K - ): Unit = { - require(invariantList(l) && containsKey(l, otherKey) && otherKey != newKey) - decreases(l) - - l match { - case Cons(head, tl) if (head._1 != otherKey) => - lemmainsertNoDuplicatedKeysDoesNotModifyOtherKeysContained( - tl, - newKey, - newValue, - otherKey - ) - case _ => () - } - }.ensuring(_ => containsKey(insertNoDuplicatedKeys(l, newKey, newValue), otherKey)) - - @opaque - @inlineOnce - def lemmainsertNoDuplicatedKeysNotContainedContent[K, B]( - l: List[(K, B)], - newKey: K, - newValue: B - ): Unit = { - require(invariantList(l)) - require(!containsKey(l, newKey)) - decreases(l) - - l match { - case Cons(head, tl) => { - lemmaTailStillNotContainsKey(l, newKey) - lemmainsertNoDuplicatedKeysNotContainedContent(tl, newKey, newValue) - } - case _ => () - } - - }.ensuring (_ => - l.content ++ Set((newKey, newValue)) == insertNoDuplicatedKeys( - l, - newKey, - newValue - ).content - ) - - @opaque - @inlineOnce - def lemmaNotContainsKeyThenNotContainsTuple[K, B]( - l: List[(K, B)], - key: K, - value: B - ): Unit = { - require(invariantList(l)) - require(!containsKey(l, key)) - decreases(l) - l match { - case Cons(head, tl) => - lemmaTailStillNotContainsKey(l, key) - lemmaNotContainsKeyThenNotContainsTuple(tl, key, value) - case _ => () - } - - }.ensuring(_ => !l.contains((key, value))) - - @opaque - @inlineOnce - def lemmaContainsTupleThenContainsKey[K, B]( - l: List[(K, B)], - key: K, - value: B - ): Unit = { - require(invariantList(l)) - require(l.contains((key, value))) - decreases(l) - - l match { - case Cons(head, tl) if (head != (key, value)) => - lemmaContainsTupleThenContainsKey(tl, key, value) - case _ => () - } - }.ensuring(_ => containsKey(l, key)) - - @opaque - @inlineOnce - def lemmaContainsTupThenGetReturnValue[K, B]( - l: List[(K, B)], - key: K, - value: B - ): Unit = { - require(invariantList(l) && containsKey(l, key) && l.contains((key, value))) - decreases(l) - - l match { - case head :: Nil() => () - case Cons(head, tl) if (head._1 == key) => - lemmaNotContainsKeyThenNotContainsTuple(tl, key, value) - case Cons(head, tl) => lemmaContainsTupThenGetReturnValue(tl, key, value) - case Nil() => () - } - }.ensuring(_ => getValueByKey(l, key) == Some[B](value)) -} - -object ListMap { - // def apply[K, B](l: List[(K, B)])(using ord: Ordering[K]): ListMap[K, B] = ListMap(l, ord, ()) - def empty[K, B]: ListMap[K, B] = ListMap[K, B](List.empty[(K, B)]) -} - -object ListMapLemmas { - import ListSpecs._ - - @opaque - @inlineOnce - def removeNotPresentStillSame[K, B](lm: ListMap[K, B], a: K): Unit = { - require(!lm.contains(a)) - TupleListOpsGenK.lemmaremovePresrvNoDuplicatedKeysNotPresentPreserves(lm.toList, a) - }.ensuring(_ => lm - a == lm) - - @opaque - @inlineOnce - def addSameAsAddTwiceSameKeyDiffValues[K, B]( - lm: ListMap[K, B], - a: K, - b1: B, - b2: B - ): Unit = { - TupleListOpsGenK.lemmainsertNoDuplicatedKeysErasesIfSameKey(lm.toList, a, b1, b2) - }.ensuring(_ => lm + (a, b2) == (lm + (a, b1) + (a, b2))) - - @opaque - @inlineOnce - def addRemoveCommutativeForDiffKeys[K, B]( - lm: ListMap[K, B], - a1: K, - b1: B, - a2: K - ): Unit = { - require(a1 != a2) - TupleListOpsGenK.lemmaInsertAndremovePresrvNoDuplicatedKeysCommutative( - lm.toList, - a1, - b1, - a2 - ) - }.ensuring(_ => lm + (a1, b1) - a2 == lm - a2 + (a1, b1)) - - @opaque - @inlineOnce - def addThenRemoveForNewKeyIsSame[K, B]( - lm: ListMap[K, B], - a1: K, - b1: B - ): Unit = { - require(!lm.contains(a1)) - TupleListOpsGenK.lemmainsertNoDuplicatedKeysThenRemoveIsSame(lm.toList, a1, b1) - }.ensuring(_ => lm + (a1, b1) - a1 == lm) - - @opaque - @inlineOnce - def removeThenAddForSameKeyIsSameAsAdd[K, B]( - lm: ListMap[K, B], - a1: K, - b1: B - ): Unit = { - TupleListOpsGenK.lemmaRemoveTheninsertNoDuplicatedKeysIsSameAsInsert(lm.toList, a1, b1) - }.ensuring(_ => (lm - a1 + (a1, b1)).eq(lm + (a1, b1))) - - @opaque - @inlineOnce - def removeCommutative[K, B](lm: ListMap[K, B], a1: K, a2: K): Unit = { - TupleListOpsGenK.lemmaremovePresrvNoDuplicatedKeysCommutative(lm.toList, a1, a2) - }.ensuring(_ => lm - a1 - a2 == lm - a2 - a1) - - @opaque - @inlineOnce - def addCommutativeForDiffKeys[K, B]( - lm: ListMap[K, B], - a1: K, - b1: B, - a2: K, - b2: B - ): Unit = { - require(a1 != a2) - TupleListOpsGenK.lemmainsertNoDuplicatedKeysCommutative(lm.toList, a1, b1, a2, b2) - }.ensuring(_ => (lm + (a1, b1) + (a2, b2)).eq(lm + (a2, b2) + (a1, b1))) - - @opaque - @inlineOnce - def addValidProp[K, B]( - lm: ListMap[K, B], - p: ((K, B)) => Boolean, - a: K, - b: B - ): Unit = { - require(lm.forall(p) && p(a, b)) - decreases(lm.toList.size) - - if (!lm.isEmpty) - addValidProp(lm.tail, p, a, b) - - }.ensuring { _ => - val nlm = lm + (a, b) - nlm.forall(p) - } - - @opaque - @inlineOnce - def removeValidProp[K, B]( - lm: ListMap[K, B], - p: ((K, B)) => Boolean, - a: K - ): Unit = { - require(lm.forall(p)) - decreases(lm.toList.size) - if (!lm.isEmpty) - removeValidProp(lm.tail, p, a) - - }.ensuring { _ => - val nlm = lm - a - nlm.forall(p) - } - - @opaque - @inlineOnce - def insertAllValidProp[K, B]( - lm: ListMap[K, B], - kvs: List[(K, B)], - p: ((K, B)) => Boolean - ): Unit = { - require(lm.forall(p) && kvs.forall(p)) - decreases(kvs) - - if (!kvs.isEmpty) { - addValidProp(lm, p, kvs.head._1, kvs.head._2) - insertAllValidProp(lm + kvs.head, kvs.tail, p) - } - - }.ensuring { _ => - val nlm = lm ++ kvs - nlm.forall(p) - } - - @opaque - @inlineOnce - def removeAllValidProp[K, B]( - lm: ListMap[K, B], - l: List[K], - p: ((K, B)) => Boolean - ): Unit = { - require(lm.forall(p)) - decreases(l) - - if (!l.isEmpty) { - removeValidProp(lm, p, l.head) - removeAllValidProp(lm - l.head, l.tail, p) - } - - }.ensuring { _ => - val nlm = lm -- l - nlm.forall(p) - } - - @opaque - @inlineOnce - def addApplyDifferent[K, B]( - lm: ListMap[K, B], - a: K, - b: B, - a0: K - ): Unit = { - require(lm.contains(a0) && a0 != a) - assert(TupleListOpsGenK.containsKey(lm.toList, a0)) - TupleListOpsGenK.lemmainsertNoDuplicatedKeysDoesNotModifyOtherKeyValues( - lm.toList, - a, - b, - a0 - ) - TupleListOpsGenK.lemmaContainsKeyImpliesGetValueByKeyDefined(lm.toList, a0) - - }.ensuring(_ => (lm + (a -> b)).apply(a0) == lm(a0)) - - @opaque - @inlineOnce - def addStillContains[K, B]( - lm: ListMap[K, B], - a: K, - b: B, - a0: K - ): Unit = { - require(lm.contains(a0)) - - if (a != a0) - TupleListOpsGenK.lemmainsertNoDuplicatedKeysDoesNotModifyOtherKeysContained( - lm.toList, - a, - b, - a0 - ) - - }.ensuring(_ => (lm + (a, b)).contains(a0)) - - @opaque - @inlineOnce - def addStillNotContains[K, B]( - lm: ListMap[K, B], - a: K, - b: B, - a0: K - ): Unit = { - require(!lm.contains(a0) && a != a0) - - TupleListOpsGenK.lemmainsertNoDuplicatedKeysDoesNotModifyOtherKeysNotContained( - lm.toList, - a, - b, - a0 - ) - - }.ensuring(_ => !(lm + (a, b)).contains(a0)) - - @opaque - @inlineOnce - def applyForall[K, B]( - lm: ListMap[K, B], - p: ((K, B)) => Boolean, - k: K - ): Unit = { - require(lm.forall(p) && lm.contains(k)) - decreases(lm.toList.size) - - if (!lm.isEmpty && lm.toList.head._1 != k) - applyForall(lm.tail, p, k) - - }.ensuring(_ => p(k, lm(k))) - - @opaque - @inlineOnce - def getForall[K, B]( - lm: ListMap[K, B], - p: ((K, B)) => Boolean, - k: K - ): Unit = { - require(lm.forall(p)) - decreases(lm.toList.size) - - if (!lm.isEmpty && lm.toList.head._1 != k) - getForall(lm.tail, p, k) - - }.ensuring(_ => lm.get(k).forall(v => p(k, v))) - - @opaque - @inlineOnce - def uniqueImage[K, B](lm: ListMap[K, B], a: K, b: B): Unit = { - require(lm.toList.contains((a, b))) - - TupleListOpsGenK.lemmaContainsTupleThenContainsKey(lm.toList, a, b) - TupleListOpsGenK.lemmaContainsTupThenGetReturnValue(lm.toList, a, b) - - }.ensuring(_ => lm.get(a) == Some[B](b)) - - @opaque - @inlineOnce - def lemmaContainsAllItsOwnKeys[K, B](lm: ListMap[K, B]): Unit = { - decreases(lm.toList.size) - lm.toList match - case Cons(h, t) => { - lemmaContainsAllItsOwnKeys(ListMap(t)) - assert(t.forall(p => ListMap(t).contains(p._1))) - assert(lm == ListMap(t) + h) - lemmaInsertPairStillContainsAll(ListMap(t), t, h._1, h._2) - lemmaInsertPairStillContainsAllEq(ListMap(t), lm, t, h._1, h._2) - assert(t.forall(p => (ListMap(t) + (h._1, h._2)).contains(p._1))) - assert(t.forall(p => lm.contains(p._1))) - assert(Cons(h, t).forall(p => lm.contains(p._1))) - } - case Nil() => - - }.ensuring (_ => lm.toList.forall(p => lm.contains(p._1))) - - @opaque - @inlineOnce - def lemmaInsertPairStillContainsAll[K, B](lm: ListMap[K, B], l: List[(K, B)], k: K, v: B): Unit = { - require(l.forall(p => lm.contains(p._1))) - decreases(l) - l match { - case Cons(h, t) => - if (h._1 != k) { - TupleListOpsGenK.lemmainsertNoDuplicatedKeysDoesNotModifyOtherKeyValues(lm.toList, k, v, h._1) - } - lemmaInsertPairStillContainsAll(lm, t, k, v) - case Nil() => () - } - }.ensuring (_ => l.forall(p => (lm + (k, v)).contains(p._1))) - - @opaque - @inlineOnce - def lemmaInsertPairStillContainsAllEq[K, B](lm: ListMap[K, B], lm2: ListMap[K, B], l: List[(K, B)], k: K, v: B): Unit = { - require(lm + (k, v) == lm2) - require(l.forall(p => lm.contains(p._1))) - decreases(l) - l match { - case Cons(h, t) => - if (h._1 != k) { - TupleListOpsGenK.lemmainsertNoDuplicatedKeysDoesNotModifyOtherKeyValues(lm.toList, k, v, h._1) - } - lemmaInsertPairStillContainsAllEq(lm, lm2, t, k, v) - case Nil() => () - } - }.ensuring (_ => l.forall(p => (lm + (k, v)).contains(p._1)) && l.forall(p => (lm2).contains(p._1))) - - @opaque - @inlineOnce - def addForallContainsKeyThenBeforeAdding[K, B]( - lm: ListMap[K, B], - other: ListMap[K, B], - a: K, - b: B - ): Unit = { - require((lm + (a, b)).toList.forall(p => other.contains(p._1))) - decreases(lm.toList.size) - - lm.toList match { - case Cons(head, tl) if (head._1 == a) => () - case Cons(head, tl) => - addForallContainsKeyThenBeforeAdding(lm.tail, other, a, b) - case Nil() => () - } - - }.ensuring { _ => - lm.toList.forall(p => other.contains(p._1)) - } - - @opaque - @inlineOnce - def lemmaGetValueImpliesTupleContained[K, B](lm: ListMap[K, B], a: K, b: B): Unit = { - require(lm.contains(a)) - require(lm.get(a) == Some[B](b)) - - TupleListOpsGenK.lemmaGetValueByKeyImpliesContainsTuple(lm.toList, a, b) - }.ensuring (_ => lm.toList.contains((a, b))) - - @opaque - def keysOfSound[K, B](@induct lm: ListMap[K, B], value: B): Unit = { - // trivial by postcondition of getKeysOf - assert(TupleListOpsGenK.getKeysOf(lm.toList, value).forall(k => lm.get(k) == Some[B](value))) - }.ensuring(_ => lm.keysOf(value).forall((key: K) => lm.get(key) == Some[B](value))) - - @opaque - @inlineOnce - def addNotContainedContent[K, B]( - lm: ListMap[K, B], - key: K, - value: B - ): Unit = { - require(!lm.contains(key)) - TupleListOpsGenK.lemmainsertNoDuplicatedKeysNotContainedContent( - lm.toList, - key, - value - ) - }.ensuring (_ => - lm.toList.content ++ Set( - (key, value) - ) == (lm + (key, value)).toList.content - ) - - // Equivalence LEMMAS ---------------------------------------------------------------------------------------------------------------- - @opaque - @inlineOnce - def lemmaAddToEqMapsPreservesEq[K, B]( - lm1: ListMap[K, B], - lm2: ListMap[K, B], - key: K, - value: B - ): Unit = { - require(lm1.eq(lm2)) - if(lm1.contains(key)) { - lemmaAddToEqMapsPreservesEqIfContainsKey(lm1, lm2, key, value) - check((lm1 + (key, value)).eq(lm2 + (key, value))) - } else { - lemmaAddToEqMapsPreservesEqIfDoesNotContainKey(lm1, lm2, key, value) - check((lm1 + (key, value)).eq(lm2 + (key, value))) - } - - }.ensuring(_ => (lm1 + (key, value)).eq(lm2 + (key, value))) - - @opaque - @inlineOnce - def lemmaAddToEqMapsPreservesEqIfDoesNotContainKey[K, B]( - lm1: ListMap[K, B], - lm2: ListMap[K, B], - key: K, - value: B - ): Unit = { - require(lm1.eq(lm2)) - require(!lm1.contains(key)) - decreases(lm1.toList.size) - - assert(!lm1.contains(key)) - if(lm2.contains(key)){ - TupleListOpsGenK.lemmaContainsKeyImpliesGetValueByKeyDefined(lm2.toList, key) - TupleListOpsGenK.lemmaGetValueByKeyImpliesContainsTuple(lm2.toList, key, lm2.apply(key)) - assert(lm2.toList.contains((key, lm2.apply(key)))) - assert(lm1.toList.contains((key, lm2.apply(key)))) - TupleListOpsGenK.lemmaContainsTupleThenContainsKey(lm1.toList, key, lm2.apply(key)) - check(false) - } - assert(!lm2.contains(key)) - - - - check((lm1 + (key, value)).eq(lm2 + (key, value))) - - }.ensuring(_ => (lm1 + (key, value)).eq(lm2 + (key, value))) - - @opaque - @inlineOnce - def lemmaAddToEqMapsPreservesEqIfContainsKey[K, B]( - lm1: ListMap[K, B], - lm2: ListMap[K, B], - key: K, - value: B - ): Unit = { - require(lm1.eq(lm2)) - require(lm1.contains(key)) - decreases(lm1.toList.size) - - lemmaEquivalentThenSameContains(lm1, lm2, key) - lemmaEquivalentGetSameValue(lm1, lm2, key) - - val v = lm1.apply(key) - val v2 = lm2.apply(key) - TupleListOpsGenK.lemmaGetValueByKeyImpliesContainsTuple(lm2.toList, key, v2) - assert(lm1.toList.contains((key, v))) - assert(lm2.toList.contains((key, v))) - assert(lm1.apply(key) == lm2.apply(key)) - assert(lm1.apply(key) == v) - - val lm1WithoutKey = lm1 - key - val lm2WithoutKey = lm2 - key - lemmaRemovePreservesEq(lm1, lm2, key) - check(lm1WithoutKey.eq(lm2WithoutKey)) - check(lm1WithoutKey.contains(key) == false) - check(lm2WithoutKey.contains(key) == false) - - val lm1After = lm1WithoutKey + (key, value) - val lm2After = lm2WithoutKey + (key, value) - lemmaAddToEqMapsPreservesEqIfDoesNotContainKey(lm1WithoutKey, lm2WithoutKey, key, value) - - check(lm1After.eq(lm2After)) - - removeThenAddForSameKeyIsSameAsAdd(lm1, key, value) - removeThenAddForSameKeyIsSameAsAdd(lm2, key, value) - - check(lm1After.eq(lm1 + (key, value))) - check(lm2After.eq(lm2 + (key, value))) - - check((lm1 + (key, value)).eq(lm2 + (key, value))) - - }.ensuring(_ => (lm1 + (key, value)).eq(lm2 + (key, value))) - - @opaque - @inlineOnce - def lemmaEquivalentThenSameContains[K, B]( - lm1: ListMap[K, B], - lm2: ListMap[K, B], - key: K - ): Unit = { - require(lm1.eq(lm2)) - if(lm1.contains(key)){ - val v = lm1.apply(key) - TupleListOpsGenK.lemmaGetValueByKeyImpliesContainsTuple(lm1.toList, key, v) - assert(lm1.toList.contains((key, v))) - assert(lm2.toList.contains((key, v))) - TupleListOpsGenK.lemmaContainsTupleThenContainsKey(lm2.toList, key, v) - - assert(lm1.contains(key) == true) - assert(lm2.contains(key) == true) - - } else { - if(lm2.contains(key)){ - TupleListOpsGenK.lemmaContainsKeyImpliesGetValueByKeyDefined(lm2.toList, key) - TupleListOpsGenK.lemmaGetValueByKeyImpliesContainsTuple(lm2.toList, key, lm2.apply(key)) - assert(lm2.toList.contains((key, lm2.apply(key)))) - assert(lm1.toList.contains((key, lm2.apply(key)))) - TupleListOpsGenK.lemmaContainsTupleThenContainsKey(lm1.toList, key, lm2.apply(key)) - check(false) - } - assert(lm1.contains(key) == false) - assert(lm2.contains(key) == false) - } - }.ensuring(_ => lm1.contains(key) == lm2.contains(key)) - - @opaque - @inlineOnce - def lemmaEquivalentGetSameValue[K, B]( - lm1: ListMap[K, B], - lm2: ListMap[K, B], - key: K - ): Unit = { - require(lm1.eq(lm2)) - lemmaEquivalentThenSameContains(lm1, lm2, key) - if(lm1.contains(key)){ - val v = lm1.apply(key) - TupleListOpsGenK.lemmaGetValueByKeyImpliesContainsTuple(lm1.toList, key, v) - assert(lm1.toList.contains((key, v))) - assert(lm2.toList.contains((key, v))) - TupleListOpsGenK.lemmaContainsTupleThenContainsKey(lm2.toList, key, v) - assert(lm2.contains(key)) - val v2 = lm2.apply(key) - TupleListOpsGenK.lemmaGetValueByKeyImpliesContainsTuple(lm2.toList, key, v2) - if(v2 != v){ - assert(lm2.toList.contains((key, v2))) - assert(lm2.toList.contains((key, v))) - TupleListOpsGenK.lemmaContainsTwoDifferentTuplesSameKeyImpossible(lm2.toList, key, v, v2) - check(false) - } - assert(lm1.get(key).get == v) - assert(lm2.get(key).get == v) - } - else{ - if(lm1.get(key).isDefined){ - TupleListOpsGenK.lemmaGetValueByKeyImpliesContainsTuple(lm1.toList, key, lm1.get(key).get) - TupleListOpsGenK.lemmaContainsTupleThenContainsKey(lm1.toList, key, lm1.get(key).get) - check(false) - } - if(lm2.get(key).isDefined){ - TupleListOpsGenK.lemmaGetValueByKeyImpliesContainsTuple(lm2.toList, key, lm2.get(key).get) - TupleListOpsGenK.lemmaContainsTupleThenContainsKey(lm2.toList, key, lm2.get(key).get) - check(false) - } - assert(lm1.get(key).isEmpty) - assert(lm2.get(key).isEmpty) - } - - }.ensuring(_ => lm1.get(key) == lm2.get(key)) - - @opaque - @inlineOnce - def lemmaRemovePreservesEq[K, B]( - lm1: ListMap[K, B], - lm2: ListMap[K, B], - key: K - ): Unit = { - require(lm1.eq(lm2)) - lemmaEquivalentThenSameContains(lm1, lm2, key) - if(lm1.contains(key)) { - val v = lm1.apply(key) - val v2 = lm2.apply(key) - lemmaEquivalentGetSameValue(lm1, lm2, key) - check((lm1 - key).toList.content == lm1.toList.content -- Set((key, v))) - check((lm2 - key).toList.content == lm2.toList.content -- Set((key, v2))) - } else { - check((lm1 - key).toList.content == lm1.toList.content) - check((lm2 - key).toList.content == lm2.toList.content) - } - - }.ensuring(_ => (lm1 - key).eq(lm2 - key)) -} - diff --git a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/chassot/ListMap.scala b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/chassot/ListMap.scala new file mode 120000 index 00000000..5b929f60 --- /dev/null +++ b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/chassot/ListMap.scala @@ -0,0 +1 @@ +../../../../../../../../../data-structures/maps/mutablemaps/src/main/scala/ch/epfl/chassot/ListMap.scala \ No newline at end of file diff --git a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/chassot/MutableHashMap.scala b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/chassot/MutableHashMap.scala deleted file mode 100644 index 43afacde..00000000 --- a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/chassot/MutableHashMap.scala +++ /dev/null @@ -1,1620 +0,0 @@ -/** Author: Samuel Chassot - */ -package ch.epfl.chassot - -import stainless.annotation._ -import stainless.collection.{ListMap => ListMapStainless, ListMapLemmas => ListMapLemmasStainless, _} -import stainless.equations._ -import stainless.lang.{ghost => ghostExpr, _} -import stainless.proof.check -import scala.annotation.tailrec -import stainless.lang.Cell -import MutableLongMap._ -import LongMapFixedSize.validMask - -import stainless.lang.StaticChecks.* // Comment out when using the OptimisedEnsuring object below -// import OptimisedChecks.* // Import to remove `ensuring` and `require` from the code for the benchmarks - -trait Hashable[K] { - @pure - def hash(k: K): Long -} - -object MutableHashMap { - - /** Helper method to create a new empty HashMap - * - * @param defaultValue - * @return - */ - def getEmptyHashMap[K, V](defaultValue: K => V, hashF: Hashable[K]): HashMap[K, V] = { - val initialSize = 16 - HashMap(Cell(MutableLongMap.getEmptyLongMap[List[(K, V)]]((l: Long) => Nil[(K, V)](), initialSize)), hashF, 0, defaultValue) - } ensuring (res => res.valid && res.size == 0) - - - @mutable - final case class HashMap[K, V]( - val underlying: Cell[LongMap[List[(K, V)]]], - val hashF: Hashable[K], - var _size: Int, - val defaultValue: K => V - ) { - - @pure - def imbalanced(): Boolean = underlying.v.imbalanced() - - @pure - def size: Int = _size - - @pure - def isEmpty: Boolean = { - require(valid) - underlying.v.isEmpty - } ensuring (_ => valid) - - @pure - def contains(key: K): Boolean = { - require(valid) - val hash = hashF.hash(key) - - ghostExpr({ - if (underlying.v.contains(hash)) { - if (!underlying.v.map.toList.contains((hash, underlying.v.apply(hash)))) { - TupleListOps.lemmaGetValueByKeyIsDefinedImpliesContainsKey(underlying.v.map.toList, hash) - TupleListOps.lemmaContainsKeyImpliesGetValueByKeyDefined(underlying.v.map.toList, hash) - TupleListOps.lemmaGetValueByKeyImpliesContainsTuple(underlying.v.map.toList, hash, underlying.v.apply(hash)) - check(false) - } - ListSpecs.forallContained(underlying.v.map.toList, (k, v) => noDuplicateKeys(v), (hash, underlying.v.apply(hash))) - } - }) - ghostExpr({ - if (extractMap(underlying.v.map.toList).contains(key)) { - lemmaInGenericMapThenInLongMap(underlying.v.map, key, hashF) - } else { - if (((underlying.v.map.contains(hashF.hash(key)) && getPair(underlying.v.map.apply(hashF.hash(key)), key).isDefined))) { - lemmaInLongMapThenInGenericMap(underlying.v.map, key, hashF) - check(false) - } - } - }) - underlying.v.contains(hash) && getPair(underlying.v.apply(hash), key).isDefined - } ensuring (res => valid && (res == map.contains(key))) - - @pure - def apply(key: K): V = { - require(valid) - if (!contains(key)) { - defaultValue(key) - } else { - val hash = hashF.hash(key) - ghostExpr({ - if (!underlying.v.map.toList.contains((hash, underlying.v.apply(hash)))) { - TupleListOps.lemmaContainsKeyImpliesGetValueByKeyDefined(underlying.v.map.toList, hash) - TupleListOps.lemmaGetValueByKeyImpliesContainsTuple(underlying.v.map.toList, hash, underlying.v.apply(hash)) - check(false) - } - ListSpecs.forallContained(underlying.v.map.toList, (k, v) => noDuplicateKeys(v), (hash, underlying.v.apply(hash))) - }) - - ghostExpr(lemmaGetPairGetSameValueAsMap(underlying.v.map, key, getPair(underlying.v.apply(hash), key).get._2, hashF)) - assert(getPair(underlying.v.apply(hash), key).get._2 == map.get(key).get) - getPair(underlying.v.apply(hash), key).get._2 - } - } ensuring (res => - valid - && (if (contains(key)) res == map.get(key).get - else res == defaultValue(key)) - ) - - def update(key: K, v: V): Boolean = { - require(valid) - @ghost val oldMap = map - @ghost val oldLongListMap = underlying.v.map - - val contained = contains(key) - val res = if (contained) { - val hash = hashF.hash(key) - val currentBucket = underlying.v.apply(hash) - // currentBucket contains the key and it is defined - val newBucket = Cons((key, v), removePairForKey(currentBucket, key)) - val res = underlying.v.update(hash, newBucket) - - ghostExpr({ - if (res) { - lemmaInLongMapAllKeySameHashThenForTuple(oldLongListMap.toList, hash, currentBucket, hashF) - lemmaRemovePairForKeyPreservesHash(currentBucket, key, hash, hashF) - lemmaRemovePairForKeyPreservesNoDuplicateKeys(currentBucket, key) - check(noDuplicateKeys(removePairForKey(currentBucket, key))) - check(!containsKey(removePairForKey(currentBucket, key), key)) - check(noDuplicateKeys(newBucket)) - lemmaChangeOneBucketByAValidOnePreservesForallNoDuplicatesAndHash(oldLongListMap, hash, newBucket, hashF) - lemmaChangeOneBucketToUpdateAnExistingKeyUpdateThisKeyInGenMap(oldLongListMap, hash, newBucket, key, v, hashF) - ListMapLemmas.lemmaEquivalentThenSameContains(map, oldMap + (key, v), key) - check(underlying.v.map.toList.forall((k, v) => noDuplicateKeys(v))) - check(allKeysSameHashInMap(underlying.v.map, hashF)) - check(map.eq(oldMap + (key, v))) - } else { - check(valid) - check(map == oldMap) - } - }) - if (res && !contained) then _size += 1 - res - - } else { - val hash = hashF.hash(key) - val currentBucket = if underlying.v.contains(hash) then underlying.v.apply(hash) else Nil[(K, V)]() - // Either currentBucket is empty, or it does not contain the key - val newBucket = Cons((key, v), currentBucket) - val res = underlying.v.update(hash, newBucket) - - ghostExpr({ - if (res) { - if (!currentBucket.isEmpty) { - lemmaInLongMapAllKeySameHashThenForTuple(oldLongListMap.toList, hash, currentBucket, hashF) - lemmaRemovePairForKeyPreservesHash(currentBucket, key, hash, hashF) - } - lemmaChangeOneBucketByAValidOnePreservesForallNoDuplicatesAndHash(oldLongListMap, hash, newBucket, hashF) - lemmaChangeOneBucketToUpdateANewKeyUpdateThisKeyInGenMap(oldLongListMap, hash, newBucket, key, v, hashF) - check(underlying.v.map.toList.forall((k, v) => noDuplicateKeys(v))) - check(allKeysSameHashInMap(underlying.v.map, hashF)) - check(map.eq(oldMap + (key, v))) - check(res) - ListMapLemmas.lemmaEquivalentThenSameContains(map, oldMap + (key, v), key) - check((oldMap + (key, v)).contains(key)) - check(map.contains(key)) - } else { - check(valid) - check(map == oldMap) - } - }) - - if (res && !contained) then _size += 1 - res - } - res - - } ensuring (res => valid && (if (res) map.contains(key) && (map.eq(old(this).map + (key, v))) else map.eq(old(this).map))) - - def remove(key: K): Boolean = { - require(valid) - val contained = contains(key) - if (!contained) { - ghostExpr({ - lemmaRemoveNotContainedDoesNotChange(underlying.v.map, key, hashF) - }) - true - } else { - val hash = hashF.hash(key) - val currentBucket = underlying.v.apply(hash) - - ghostExpr(ListSpecs.forallContained(underlying.v.map.toList, (k, v) => noDuplicateKeys(v), (hash, underlying.v.apply(hash)))) - - @ghost val oldMap = map - @ghost val oldLongListMap = underlying.v.map - - val newBucket = removePairForKey(currentBucket, key) - val res = underlying.v.update(hash, newBucket) - if (res && contained) then _size -= 1 - - ghostExpr({ - lemmaRemovePairForKeyPreservesNoDuplicateKeys(currentBucket, key) - if (res) { - lemmaInLongMapAllKeySameHashThenForTuple(oldLongListMap.toList, hash, currentBucket, hashF) - lemmaRemovePairForKeyPreservesHash(currentBucket, key, hash, hashF) - lemmaChangeOneBucketByAValidOnePreservesForallNoDuplicatesAndHash(oldLongListMap, hash, newBucket, hashF) - check(underlying.v.map.toList.forall((k, v) => noDuplicateKeys(v))) - check(allKeysSameHashInMap(underlying.v.map, hashF)) - check(valid) - check(oldMap.contains(key)) - lemmaChangeOneBucketToRemoveAKeyRemoveThisKeyInGenMap(oldLongListMap, hash, newBucket, key, hashF) - check(map.eq(oldMap - key)) - } else { - check(valid) - check(map == oldMap) - } - }) - res - } - - } ensuring (res => valid && (if (res) map.eq(old(this).map - key) else map.eq(old(this).map))) - - @ghost - def valid: Boolean = underlying.v.valid && - underlying.v.map.toList.forall((k, v) => noDuplicateKeys(v)) && - allKeysSameHashInMap(underlying.v.map, hashF) - - @pure - @ghost - def map: ListMap[K, V] = { - require(valid) - extractMap(underlying.v.map.toList) - }.ensuring(res => TupleListOpsGenK.invariantList(res.toList)) - - } - @ghost - def extractMap[K, V](l: List[(Long, List[(K, V)])]): ListMap[K, V] = { - require(l.forall((k, v) => noDuplicateKeys(v))) - decreases(l) - l match { - case Cons((k, v), tl) => addToMapMapFromBucket(v, extractMap(tl)) - case Nil() => ListMap.empty[K, V] - } - } ensuring (res => true) - - @ghost - def addToMapMapFromBucket[K, V](l: List[(K, V)], acc: ListMap[K, V]): ListMap[K, V] = { - require(noDuplicateKeys(l)) - decreases(l) - l match { - case Nil() => { - ListMapLemmas.lemmaContainsAllItsOwnKeys(acc) - check(acc.toList.forall(p => acc.contains(p._1))) - acc - } - case Cons((k, v), tl) => { - val newAcc = acc + (k, v) - val res = addToMapMapFromBucket(tl, acc + (k, v)) - ListMapLemmas.lemmaContainsAllItsOwnKeys(acc) - check(newAcc.toList.forall(p => res.contains(p._1))) - check(tl.forall(p => res.contains(p._1))) - check(l == Cons((k, v), tl)) - ListSpecs.forallContained(newAcc.toList, (k, v) => res.contains(k), (k, v)) - check(newAcc.contains(k)) - check(res.contains(k)) - check(l.forall(p => res.contains(p._1))) - check(newAcc.toList.forall(p => res.contains(p._1))) - check(newAcc == acc + (k, v)) - ListMapLemmas.addForallContainsKeyThenBeforeAdding(acc, res, k, v) - - check(acc.toList.forall(p => res.contains(p._1))) - check(l.forall(p => res.contains(p._1)) && acc.toList.forall(p => res.contains(p._1))) - res - } - } - } ensuring (res => l.forall(p => res.contains(p._1)) && acc.toList.forall(p => res.contains(p._1))) - - @ghost - def noDuplicateKeys[K, V](l: List[(K, V)]): Boolean = { - l match { - case Cons(hd, tl) => !containsKey(tl, hd._1) && noDuplicateKeys(tl) - case Nil() => true - } - } - - @pure - @ghost - def allKeysSameHashInMap[K, V](lm: ListLongMap[List[(K, V)]], hashF: Hashable[K]): Boolean = { - lm.toList.forall((k, v) => allKeysSameHash(v, k, hashF)) - } - - @ghost - def allKeysSameHash[K, V](l: List[(K, V)], h: Long, hashF: Hashable[K]): Boolean = { - l.forall(p => hashF.hash(p._1) == h) - } - @ghost - def containsKey[K, V](l: List[(K, V)], key: K): Boolean = { - l match - case Cons(hd, tl) if hd._1 == key => true - case Cons(_, tl) => containsKey(tl, key) - case Nil() => false - } - @ghost - def containsKeyBiggerList[K, V](l: List[(Long, List[(K, V)])], key: K): Boolean = { - l match - case Cons(h, t) if containsKey(h._2, key) => true - case Cons(h, t) => containsKeyBiggerList(t, key) - case Nil() => false - } - - def getPair[K, V](l: List[(K, V)], key: K): Option[(K, V)] = { - require(noDuplicateKeys(l)) - l match - case Cons(hd, tl) if hd._1 == key => Some(hd) - case Cons(_, tl) => getPair(tl, key) - case Nil() => None() - } ensuring (res => res.isEmpty && !containsKey(l, key) || res.isDefined && res.get._1 == key && l.contains(res.get)) - - def removePairForKey[K, V](l: List[(K, V)], key: K): List[(K, V)] = { - require(noDuplicateKeys(l)) - l match - case Cons(hd, tl) if hd._1 == key => tl - case Cons(hd, tl) => Cons(hd, removePairForKey(tl, key)) - case Nil() => Nil() - } ensuring (res => !containsKey(res, key)) - - @ghost - def getValue[K, V](l: List[(Long, List[(K, V)])], k: K): V = { - require(containsKeyBiggerList(l, k)) - require(l.forall((k, v) => noDuplicateKeys(v))) - l match { - case Cons(hd, tl) if containsKey(hd._2, k) => getPair(hd._2, k).get._2 - case Cons(hd, tl) => getValue(tl, k) - } - } - - // ----------------- Lemmas ------------------------------------------------------------------------ - - /** - * This lemma proves that a property `p` that holds for all pairs of the map, holds for a key and its value. - * - * Useful to build caches using this map. - * - * @param hm - * @param k - * @param p - */ - @opaque - @inlineOnce - @ghost - def lemmaForallPairsThenForLookup[K, V](hm: HashMap[K, V], k: K, p: ((K, V)) => Boolean): Unit = { - require(hm.valid) - require(hm.map.forall(p)) - require(hm.contains(k)) - - TupleListOpsGenK.lemmaGetValueByKeyImpliesContainsTuple(hm.map.toList, k, hm.apply(k)) - assert(hm.map.toList.contains((k, hm.apply(k)))) - assert(hm.map.toList.forall(p)) - ListSpecs.forallContained(hm.map.toList, p, (k, hm.apply(k))) - }.ensuring(_ => p((k, hm.apply(k)))) - - - /** - * This lemma proves that inserting a new pair preserves the property `p` that holds for all pairs of the map. - * - * Useful to build caches using this map. - * - * @param hm - * @param k - * @param v - * @param p - */ - @opaque - @inlineOnce - @ghost - def lemmaUpdatePreservesForallPairs[K, V](hm: HashMap[K, V], k: K, v: V, p: ((K, V)) => Boolean): Unit = { - require(hm.valid) - require(hm.map.forall(p)) - require(p((k, v))) - - val oldSnap = snapshot(hm) - val snap = snapshot(hm) - val oldMap = hm.map - val oldSize = hm.size - - val mapAfter = oldMap + (k, v) - - val success = snap.update(k, v) - - if(success) { - assert(snap.map.eq(mapAfter)) - TupleListOpsGenK.lemmaInsertNoDuplicatedKeysPreservesForall(oldMap.toList, k, v, p) - assert(mapAfter.forall(p)) - TupleListOpsGenK.lemmaForallSubset(snap.map.toList, mapAfter.toList, p) - } else { - assert(snap.map.eq(oldMap)) - } - - () - } ensuring (_ => { - val oldMap = snapshot(hm) - val afterUpdate = snapshot(hm) - afterUpdate.update(k, v) - afterUpdate.map.forall(p) - }) - - /** - * This lemma proves that removing a pair preserves the property `p` that holds for all pairs of the map. - * - * Useful to build caches using this map. - * - * @param hm - * @param k - * @param p - */ - @opaque - @inlineOnce - @ghost - def lemmaRemovePreservesForallPairs[K, V](hm: HashMap[K, V], k: K, p: ((K, V)) => Boolean): Unit = { - require(hm.valid) - require(hm.map.forall(p)) - - val oldSnap = snapshot(hm) - val snap = snapshot(hm) - val oldMap = hm.map - val oldSize = hm.size - - val mapAfter = oldMap - k - - val success = snap.remove(k) - - assert(snap.valid) - assert(TupleListOpsGenK.invariantList(snap.map.toList)) - - if(success) { - assert(snap.map.eq(mapAfter)) - assert(oldMap.forall(p)) - TupleListOpsGenK.lemmaForallSubset(mapAfter.toList, oldMap.toList, p) - TupleListOpsGenK.lemmaForallSubset(snap.map.toList, mapAfter.toList, p) - } else { - assert(snap.map.eq(oldMap)) - TupleListOpsGenK.lemmaForallSubset(mapAfter.toList, oldMap.toList, p) - } - - () - } ensuring (_ => { - val oldMap = snapshot(hm) - val afterUpdate = snapshot(hm) - afterUpdate.remove(k) - afterUpdate.map.forall(p) - }) - - @opaque - @inlineOnce - @ghost - def lemmaInGenericMapThenInLongMap[K, V](lm: ListLongMap[List[(K, V)]], key: K, hashF: Hashable[K]): Unit = { - require(lm.toList.forall((k, v) => noDuplicateKeys(v))) - require(allKeysSameHashInMap(lm, hashF)) - require(extractMap(lm.toList).contains(key)) - - val map = extractMap(lm.toList) - lemmaInGenMapThenLongMapContainsHash(lm, key, hashF) - assert(lm.contains(hashF.hash(key))) - ghostExpr({ - val hash = hashF.hash(key) - if (!lm.toList.contains((hash, lm.apply(hash)))) { - TupleListOps.lemmaGetValueByKeyIsDefinedImpliesContainsKey(lm.toList, hash) - TupleListOps.lemmaContainsKeyImpliesGetValueByKeyDefined(lm.toList, hash) - TupleListOps.lemmaGetValueByKeyImpliesContainsTuple(lm.toList, hash, lm.apply(hash)) - check(false) - } - ListSpecs.forallContained(lm.toList, (k, v) => noDuplicateKeys(v), (hash, lm.apply(hash))) - - }) - ListSpecs.forallContained(lm.toList, (k, v) => noDuplicateKeys(v), (hashF.hash(key), lm.apply(hashF.hash(key)))) - - lemmaInGenMapThenGetPairDefined(lm, key, hashF) - assert(getPair(lm.apply(hashF.hash(key)), key).isDefined) - - } ensuring (_ => (lm.contains(hashF.hash(key)) && getPair(lm.apply(hashF.hash(key)), key).isDefined)) - - @opaque - @inlineOnce - @ghost - def lemmaInLongMapThenInGenericMap[K, V](lm: ListLongMap[List[(K, V)]], key: K, hashF: Hashable[K]): Unit = { - require(lm.toList.forall((k, v) => noDuplicateKeys(v))) - require(allKeysSameHashInMap(lm, hashF)) - require(lm.contains(hashF.hash(key))) - require({ - ListLongMapLemmas.lemmaGetValueImpliesTupleContained(lm, hashF.hash(key), lm.apply(hashF.hash(key))) - ListSpecs.forallContained(lm.toList, (k, v) => noDuplicateKeys(v), (hashF.hash(key), lm.apply(hashF.hash(key)))) - getPair(lm.apply(hashF.hash(key)), key).isDefined - }) - - lemmaInLongMapThenContainsKeyBiggerList(lm, key, hashF) - lemmaListContainsThenExtractedMapContains(lm, key, hashF) - - } ensuring (_ => extractMap(lm.toList).contains(key)) - - @opaque - @inlineOnce - @ghost - def lemmaGetPairGetSameValueAsMap[K, V](lm: ListLongMap[List[(K, V)]], key: K, v: V, hashF: Hashable[K]): Unit = { - require(lm.toList.forall((k, v) => noDuplicateKeys(v))) - require(allKeysSameHashInMap(lm, hashF)) - require(extractMap(lm.toList).contains(key)) - require({ - lemmaInGenMapThenLongMapContainsHash(lm, key, hashF) - ListLongMapLemmas.lemmaGetValueImpliesTupleContained(lm, hashF.hash(key), lm.apply(hashF.hash(key))) - ListSpecs.forallContained(lm.toList, (k, v) => noDuplicateKeys(v), (hashF.hash(key), lm.apply(hashF.hash(key)))) - lemmaInGenMapThenGetPairDefined(lm, key, hashF) - getPair(lm.apply(hashF.hash(key)), key).get._2 == v - }) - - lemmaInLongMapThenContainsKeyBiggerList(lm, key, hashF) - check(containsKeyBiggerList(lm.toList, key)) - - lemmaGetValueEquivToGetPair(lm, key, v, hashF) - lemmaExtractMapPreservesMapping(lm, key, v, hashF) - - } ensuring (_ => { - lemmaInGenMapThenLongMapContainsHash(lm, key, hashF) - ListLongMapLemmas.lemmaGetValueImpliesTupleContained(lm, hashF.hash(key), lm.apply(hashF.hash(key))) - ListSpecs.forallContained(lm.toList, (k, v) => noDuplicateKeys(v), (hashF.hash(key), lm.apply(hashF.hash(key)))) - lemmaInGenMapThenGetPairDefined(lm, key, hashF) - getPair(lm.apply(hashF.hash(key)), key).get._2 == extractMap(lm.toList).get(key).get - }) - - @opaque - @inlineOnce - @ghost - def lemmaChangeOneBucketToRemoveAKeyRemoveThisKeyInGenMap[K, V](lm: ListLongMap[List[(K, V)]], hash: Long, newBucket: List[(K, V)], key: K, hashF: Hashable[K]): Unit = { - require(lm.toList.forall((k, v) => noDuplicateKeys(v))) - require(allKeysSameHashInMap(lm, hashF)) - require(extractMap(lm.toList).contains(key)) - require(hashF.hash(key) == hash) - require(allKeysSameHash(newBucket, hash, hashF)) - require({ - lemmaInGenMapThenLongMapContainsHash(lm, key, hashF) - TupleListOps.lemmaGetValueByKeyImpliesContainsTuple(lm.toList, hash, lm.apply(hash)) - ListSpecs.forallContained(lm.toList, (k, v) => noDuplicateKeys(v), (hash, lm.apply(hash))) - newBucket == removePairForKey(lm.apply(hash), key) - }) - require(noDuplicateKeys(newBucket)) - decreases(lm.toList.size) - - lm.toList match { - case Nil() => check(false) - case Cons(hd, tl) if hd._1 == hash => { - if (!containsKey(hd._2, key)) { - lemmaNotInItsHashBucketThenNotInMap(lm, key, hashF) - check(false) - } - assert(containsKey(hd._2, key)) - assert(containsKeyBiggerList(List((hd._1, hd._2)), key)) - lemmaListContainsThenExtractedMapContains(ListLongMap(List((hd._1, hd._2))), key, hashF) - assert(extractMap(List((hd._1, hd._2))).contains(key)) - lemmaChangeOneBucketToRemoveAKeyRemoveThisKeyInGenMapOneHash(hash, hd._2, newBucket, key, hashF) - assert(tl == (lm + (hash, newBucket)).toList.tail) - assert(extractMap(List((hash, newBucket))).eq(extractMap(List((hash, hd._2))) - key)) - assert(lm + (hash, newBucket) == lm.tail + (hash, newBucket)) - assert(extractMap((lm.tail).toList) == extractMap(tl)) - - if (extractMap((lm.tail).toList).contains(key)) { - lemmaInGenMapThenLongMapContainsHash(lm.tail, key, hashF) - check(false) - } - assert(!extractMap((lm.tail).toList).contains(key)) - val oldBucket = hd._2 - lemmaChangeOneBucketToRemoveAKeyRemoveThisKeyInGenMapOneHashIsHead(lm, hash, oldBucket, newBucket, key, hashF) - - assert((lm + (hash, newBucket)).toList == Cons((hash, newBucket), tl)) - assert(extractMap((lm + (hash, newBucket)).toList).eq(addToMapMapFromBucket(newBucket, extractMap(tl)))) - assert(extractMap((lm + (hash, oldBucket)).toList).eq(addToMapMapFromBucket(oldBucket, extractMap(tl)))) - assert(extractMap((lm + (hash, oldBucket)).toList).eq(extractMap(lm.toList))) - assert(extractMap((lm + (hash, newBucket)).toList).eq(extractMap(lm.toList) - key)) - } - case Cons(hd, tl) => { - val oldBucket = lm.apply(hash) - assert(lm.tail.contains(hash)) - assert(lm.tail.apply(hash) == oldBucket) - assert(tl.contains((hash, oldBucket))) - if (!containsKey(oldBucket, key)) { - lemmaNotInItsHashBucketThenNotInMap(lm, key, hashF) - check(false) - } - assert(containsKey(oldBucket, key)) - lemmaInLongMapThenContainsKeyBiggerList(lm.tail, key, hashF) - assert(containsKeyBiggerList(tl, key)) - lemmaListContainsThenExtractedMapContains(lm.tail, key, hashF) - assert(extractMap(tl).contains(key)) - lemmaChangeOneBucketToRemoveAKeyRemoveThisKeyInGenMap(lm.tail, hash, newBucket, key, hashF) - assert(extractMap((lm.tail + (hash, newBucket)).toList).eq(extractMap(lm.tail.toList) - key)) - - assert(extractMap((lm.tail + lm.head).toList).eq(extractMap(lm.toList))) - assert(extractMap(lm.toList).eq(extractMap((lm.tail + lm.head).toList))) - assert(lm.head._1 < hash) - assert(lm.tail + (hash, newBucket) + lm.head == lm + (hash, newBucket)) - assert((lm.tail + (hash, newBucket) + lm.head).head == lm.head) - - assert((extractMap((lm.tail + (hash, newBucket) + lm.head).toList).eq(addToMapMapFromBucket(lm.head._2, extractMap((lm.tail + (hash, newBucket)).toList))))) - lemmaAddToMapFromBucketPreservesEquivalence(extractMap((lm.tail + (hash, newBucket)).toList), extractMap(lm.tail.toList) - key, lm.head._2) - assert((extractMap((lm.tail + (hash, newBucket) + lm.head).toList).eq(addToMapMapFromBucket(lm.head._2, (extractMap(lm.tail.toList) - key))))) - - ListSpecs.forallContained(lm.toList, (k, v) => noDuplicateKeys(v), (hd._1, hd._2)) - assert(noDuplicateKeys(lm.head._2)) - - if (containsKey(lm.head._2, key)) { - check(hash != lm.head._1) - val pair = getPair(lm.head._2, key).get - val value = pair._2 - ListSpecs.forallContained(lm.toList, (k, v) => allKeysSameHash(v, k, hashF), (hd._1, hd._2)) - ListSpecs.forallContained(hd._2, p => hashF.hash(p._1) == hd._1, (key, value)) - check(false) - } - assert(!containsKey(lm.head._2, key)) - lemmaAddToMapFromBucketMinusKeyIsCommutative(extractMap(lm.tail.toList), key, lm.head._2) - assert(addToMapMapFromBucket(lm.head._2, (extractMap(lm.tail.toList) - key)).eq(addToMapMapFromBucket(lm.head._2, (extractMap(lm.tail.toList))) - key)) - assert(addToMapMapFromBucket(lm.head._2, (extractMap(lm.tail.toList) - key)).eq(extractMap(lm.toList) - key)) - - assert(extractMap((lm.tail + (hash, newBucket) + lm.head).toList).eq(extractMap(lm.toList) - key)) - - assert(extractMap((lm.tail + (hash, newBucket) + lm.head).toList).eq(extractMap((lm.tail + lm.head).toList) - key)) - assert(extractMap((lm + (hash, newBucket)).toList).eq(extractMap(lm.toList) - key)) - } - } - - } ensuring (_ => { - extractMap((lm + (hash, newBucket)).toList).eq(extractMap(lm.toList) - key) - }) - - @opaque - @inlineOnce - @ghost - def lemmaChangeOneBucketToUpdateANewKeyUpdateThisKeyInGenMap[K, V]( - lm: ListLongMap[List[(K, V)]], - hash: Long, - newBucket: List[(K, V)], - key: K, - newValue: V, - hashF: Hashable[K], - - ): Unit = { - require(lm.toList.forall((k, v) => noDuplicateKeys(v))) - require(allKeysSameHashInMap(lm, hashF)) - require(hashF.hash(key) == hash) - require(allKeysSameHash(newBucket, hash, hashF)) - require(!extractMap(lm.toList).contains(key)) - require(lm.contains(hash) && newBucket == Cons((key, newValue), lm.apply(hash)) || !lm.contains(hash) && newBucket == Cons((key, newValue), Nil())) - require(noDuplicateKeys(newBucket)) - - decreases(lm.toList.size) - - assert(lm.toList.forall((k, v) => noDuplicateKeys(v))) - ListLongMapLemmas.addValidProp(lm, (k, v) => noDuplicateKeys(v), hash, newBucket) - assert((lm + (hash, newBucket)).toList.forall((k, v) => noDuplicateKeys(v))) - lm.toList match { - case Cons(hd, tl) if hd._1 == hash => - assert(lm.contains(hash)) - assert((lm + (hash, newBucket)).toList.head == (hash, newBucket)) - assert((lm + (hash, newBucket)).toList.tail == tl) - assert(extractMap((lm + (hash, newBucket)).toList) == addToMapMapFromBucket(newBucket, extractMap(tl))) - - assert(newBucket == Cons((key, newValue), lm.apply(hash))) - val newAcc = extractMap(tl) + (key, newValue) - assert(addToMapMapFromBucket(newBucket, extractMap(tl)) == addToMapMapFromBucket(lm.apply(hash), extractMap(tl) + (key, newValue))) - lemmaAddToMapFromBucketPlusKeyValueIsCommutative(extractMap(tl), key, newValue, lm.apply(hash)) - assert(addToMapMapFromBucket(newBucket, extractMap(tl)).eq(addToMapMapFromBucket(lm.apply(hash), extractMap(tl)) + (key, newValue))) - assert(extractMap((lm + (hash, newBucket)).toList).eq(addToMapMapFromBucket(lm.apply(hash), extractMap(tl)) + (key, newValue))) - assert(extractMap((lm + (hash, newBucket)).toList).eq(extractMap(lm.toList) + (key, newValue))) - case Cons(hd, tl) if hd._1 != hash => - assert(!extractMap(lm.toList).contains(key)) - if (extractMap(lm.toList.tail).contains(key)) { - lemmaExtractTailMapContainsThenExtractMapDoes(lm, key, hashF) - check(false) - } - lemmaChangeOneBucketToUpdateANewKeyUpdateThisKeyInGenMap(lm.tail, hash, newBucket, key, newValue, hashF) - assert(extractMap((lm.tail + (hash, newBucket)).toList).eq(extractMap(lm.tail.toList) + (key, newValue))) - assert(lm.head != (hash, newBucket)) - assert(extractMap(lm.toList).eq(addToMapMapFromBucket(hd._2, extractMap(lm.tail.toList)))) - - lemmaNotSameHashThenCannotContainKey(lm, key, hd._1, hashF) - assert(!containsKey(hd._2, key)) - lemmaAddToMapFromBucketPlusKeyValueIsCommutative(extractMap(lm.tail.toList), key, newValue, hd._2) - assert(addToMapMapFromBucket(hd._2, (extractMap(lm.tail.toList) + (key, newValue))).eq(addToMapMapFromBucket(hd._2, extractMap(lm.tail.toList)) + (key, newValue))) - assert(addToMapMapFromBucket(hd._2, (extractMap(lm.tail.toList) + (key, newValue))).eq(addToMapMapFromBucket(hd._2, extractMap(lm.tail.toList)) + (key, newValue))) - - assert(extractMap(lm.toList).eq(addToMapMapFromBucket(hd._2, extractMap(lm.tail.toList)))) - ListMapLemmas.lemmaAddToEqMapsPreservesEq(extractMap(lm.toList), addToMapMapFromBucket(hd._2, extractMap(lm.tail.toList)), key, newValue) - assert((extractMap(lm.toList) + (key, newValue)).eq(addToMapMapFromBucket(hd._2, extractMap(lm.tail.toList)) + (key, newValue))) - - lemmaAddToMapFromBucketPreservesEquivalence(extractMap(lm.tail.toList) + (key, newValue), extractMap((lm.tail + (hash, newBucket)).toList), hd._2) - - // ----------------------------- This is to understand the proof -------------------------------- - // if(lm.contains(hash)){ - // assert(lm.head._1 != hash) - // assert(lm.tail.contains(hash)) - - // assert(lm.tail + (hash, newBucket) == (lm + (hash, newBucket)).tail) - // assert(extractMap((lm.tail + (hash, newBucket)).toList) == extractMap((lm + (hash, newBucket)).toList.tail)) - // assert(extractMap((lm.tail + hd).toList).eq(extractMap(lm.toList))) - - // assert(extractMap((lm + (hash, newBucket)).toList).eq(addToMapMapFromBucket(hd._2, extractMap((lm + (hash, newBucket)).tail.toList)))) - // assert(extractMap((lm + (hash, newBucket)).toList).eq(addToMapMapFromBucket(hd._2, extractMap((lm.tail + (hash, newBucket)).toList)))) - - // assert( extractMap((lm.tail + (hash, newBucket)).toList).eq(extractMap(lm.tail.toList) + (key, newValue))) - - // lemmaAddToMapFromBucketPreservesEquivalence(extractMap(lm.tail.toList) + (key, newValue), extractMap((lm.tail + (hash, newBucket)).toList), hd._2) - // assert(extractMap((lm + (hash, newBucket)).toList).eq(addToMapMapFromBucket(hd._2, extractMap(lm.tail.toList) + (key, newValue)))) - - // assert(extractMap((lm + (hash, newBucket)).toList).eq(addToMapMapFromBucket(hd._2, extractMap(lm.tail.toList)) + (key, newValue))) - // assert(extractMap((lm + (hash, newBucket)).toList).eq(extractMap(lm.toList) + (key, newValue))) - // } else { - // assert(lm + (hash, newBucket) == lm + hd + (hash, newBucket)) - // if(hash < hd._1){ - // assert(lm + (hash, newBucket) == ListLongMap(Cons((hash, newBucket), lm.toList))) - // assert(extractMap((lm + (hash, newBucket)).toList).eq(extractMap(lm.toList) + (key, newValue))) - // } else { - // assert(lm + (hash, newBucket) == ListLongMap(Cons(hd, (lm.tail + (hash, newBucket)).toList))) - // lemmaAddToMapFromBucketPreservesEquivalence(extractMap(lm.tail.toList) + (key, newValue), extractMap((lm.tail + (hash, newBucket)).toList), hd._2) - // assert(extractMap((lm + (hash, newBucket)).toList).eq(extractMap(lm.toList) + (key, newValue))) - // } - - - // } - - case Nil() => () - } - - } ensuring (_ => { - extractMap((lm + (hash, newBucket)).toList).eq(extractMap(lm.toList) + (key, newValue)) - }) - - @opaque - @inlineOnce - @ghost - def lemmaChangeOneBucketToUpdateAnExistingKeyUpdateThisKeyInGenMap[K, V]( - lm: ListLongMap[List[(K, V)]], - hash: Long, - newBucket: List[(K, V)], - key: K, - newValue: V, - hashF: Hashable[K], - - ): Unit = { - require(lm.toList.forall((k, v) => noDuplicateKeys(v))) - require(allKeysSameHashInMap(lm, hashF)) - require(hashF.hash(key) == hash) - require(allKeysSameHash(newBucket, hash, hashF)) - require(extractMap(lm.toList).contains(key)) - require(lm.contains(hash)) - require({ - TupleListOps.lemmaGetValueByKeyImpliesContainsTuple(lm.toList, hash, lm.apply(hash)) - ListSpecs.forallContained(lm.toList, (k, v) => noDuplicateKeys(v), (hash, lm.apply(hash))) - newBucket == Cons((key, newValue), removePairForKey(lm.apply(hash), key)) - }) - require(noDuplicateKeys(newBucket)) - - decreases(lm.toList.size) - - check(lm.toList.forall((k, v) => noDuplicateKeys(v))) - ListLongMapLemmas.addValidProp(lm, (k, v) => noDuplicateKeys(v), hash, newBucket) - check((lm + (hash, newBucket)).toList.forall((k, v) => noDuplicateKeys(v))) - - lm.toList match { - case Cons(hd, tl) if hd._1 == hash => - check(lm + (hash, newBucket) == ListLongMap(Cons((hash, newBucket), tl))) - check(extractMap((lm + (hash, newBucket)).toList) == addToMapMapFromBucket(newBucket, extractMap(tl))) - check(extractMap((lm + (hash, newBucket)).toList) == addToMapMapFromBucket(Cons((key, newValue), removePairForKey(lm.apply(hash), key)), extractMap(tl))) - check(addToMapMapFromBucket(Cons((key, newValue), removePairForKey(lm.apply(hash), key)), extractMap(tl)) == addToMapMapFromBucket(removePairForKey(lm.apply(hash), key), extractMap(tl) + (key, newValue))) - lemmaAddToMapFromBucketPlusKeyValueIsCommutative(extractMap(tl), key, newValue, removePairForKey(lm.apply(hash), key)) - check(addToMapMapFromBucket(Cons((key, newValue), removePairForKey(lm.apply(hash), key)), extractMap(tl)).eq(addToMapMapFromBucket(removePairForKey(lm.apply(hash), key), extractMap(tl)) + (key, newValue))) - - val intermediateLm = lm + (hash, newBucket.tail) - check(newBucket.tail == removePairForKey(lm.apply(hash), key)) - check( addToMapMapFromBucket(removePairForKey(lm.apply(hash), key), extractMap(tl)) == extractMap(intermediateLm.toList)) - lemmaChangeOneBucketToRemoveAKeyRemoveThisKeyInGenMap(lm, hash, newBucket.tail, key, hashF) - check(extractMap(intermediateLm.toList).eq(extractMap(lm.toList) - key)) - - ListMapLemmas.lemmaEquivalentThenSameContains(extractMap(intermediateLm.toList), extractMap(lm.toList) - key, key) - check(intermediateLm.apply(hash) == newBucket.tail) - assert(!extractMap(intermediateLm.toList).contains(key)) - lemmaChangeOneBucketToUpdateANewKeyUpdateThisKeyInGenMap(intermediateLm, hash, newBucket, key, newValue, hashF) - check(extractMap((intermediateLm + (hash, newBucket)).toList).eq(extractMap(intermediateLm.toList) + (key, newValue))) - check(extractMap(intermediateLm.toList).eq(extractMap(lm.toList) - key)) - - ListMapLemmas.removeThenAddForSameKeyIsSameAsAdd(extractMap(lm.toList), key, newValue) - - check(extractMap((lm + (hash, newBucket)).toList).eq(extractMap(lm.toList) + (key, newValue))) - case Cons(hd, tl) if hd._1 != hash => - lemmaNotSameHashThenCannotContainKey(lm, key, hd._1, hashF) - - check(!containsKey(hd._2, key)) - - lemmaAddToMapContainsAndNotInListThenInAcc(hd._2, key, newValue, extractMap(tl)) - - check(extractMap(lm.tail.toList).contains(key)) - lemmaChangeOneBucketToUpdateAnExistingKeyUpdateThisKeyInGenMap(lm.tail, hash, newBucket, key, newValue, hashF) - check(extractMap((lm.tail + (hash, newBucket)).toList).eq(extractMap(lm.tail.toList) + (key, newValue))) - check(lm.head != (hash, newBucket)) - check(extractMap(lm.toList) == addToMapMapFromBucket(hd._2, extractMap(lm.tail.toList))) - - lemmaAddToMapFromBucketPlusKeyValueIsCommutative(extractMap(lm.tail.toList), key, newValue, hd._2) - - assert(addToMapMapFromBucket(hd._2, (extractMap(lm.tail.toList) + (key, newValue))).eq(addToMapMapFromBucket(hd._2, extractMap(lm.tail.toList)) + (key, newValue))) - - assert(addToMapMapFromBucket(hd._2, extractMap(lm.tail.toList)).eq(extractMap(lm.toList))) - - ListMapLemmas.lemmaAddToEqMapsPreservesEq(extractMap(lm.toList), addToMapMapFromBucket(hd._2, extractMap(lm.tail.toList)), key, newValue) - - lemmaAddToMapFromBucketPreservesEquivalence(extractMap(lm.tail.toList) + (key, newValue), extractMap((lm.tail + (hash, newBucket)).toList), hd._2) - - - check(extractMap((lm + (hash, newBucket)).toList).eq(extractMap(lm.toList) + (key, newValue))) - case Nil() => () - } - - } ensuring (_ => { - extractMap((lm + (hash, newBucket)).toList).eq(extractMap(lm.toList) + (key, newValue)) - }) - - // ----------------------------------------------------- - - @opaque - @inlineOnce - @ghost - def lemmaAddToMapContainsAndNotInListThenInAcc[K, V](l: List[(K, V)], key: K, value: V, acc: ListMap[K, V]): Unit = { - require(noDuplicateKeys(l)) - require(!containsKey(l, key)) - require(addToMapMapFromBucket(l, acc).contains(key)) - - decreases(l.size) - l match { - case Cons((k, v), t) => - val newAcc = acc + (k, v) - assert(k != key) - lemmaAddToMapContainsAndNotInListThenInAcc(t, key, value, newAcc) - check(newAcc.contains(k)) - if(!acc.contains(key)){ - ListMapLemmas.addStillNotContains(acc, k, v, key) - check(false) - } - - case Nil() => () - } - - } ensuring (_ => acc.contains(key)) - - @opaque - @inlineOnce - @ghost - def lemmaExtractTailMapContainsThenExtractMapDoes[K, V](lm: ListLongMap[List[(K, V)]], key: K, hashF: Hashable[K]): Unit = { - require(lm.toList.forall((k, v) => noDuplicateKeys(v))) - require(allKeysSameHashInMap(lm, hashF)) - require(!lm.toList.isEmpty) - require(extractMap(lm.tail.toList).contains(key)) - decreases(lm.toList.size) - val hash = hashF.hash(key) - - lm.toList match { - case Cons((k, v), tl) => - check(extractMap(lm.toList) == addToMapMapFromBucket(v, extractMap(lm.tail.toList))) - lemmaAddToMapMaintainsContains(lm.tail, key, v, hashF) - - case Nil() => () - } - - } ensuring (_ => extractMap(lm.toList).contains(key)) - - @opaque - @inlineOnce - @ghost - def lemmaAddToMapMaintainsContains[K, V](lm: ListLongMap[List[(K, V)]], key: K, l: List[(K, V)], hashF: Hashable[K]): Unit = { - require(lm.toList.forall((k, v) => noDuplicateKeys(v))) - require(allKeysSameHashInMap(lm, hashF)) - require(noDuplicateKeys(l)) - require(extractMap(lm.toList).contains(key)) - - decreases(l.size) - val hash = hashF.hash(key) - - l match { - case Cons(hd, tl) => - lemmaAddToMapMaintainsContains(lm, key, tl, hashF) - check(addToMapMapFromBucket(tl, extractMap(lm.toList)).contains(key)) - - lemmaAddToMapFromBucketPlusKeyValueIsCommutative(extractMap(lm.toList), hd._1, hd._2, tl) - val lm1 = addToMapMapFromBucket(tl, extractMap(lm.toList) + hd) - val lm2 = addToMapMapFromBucket(tl, extractMap(lm.toList)) + hd - check(lm1.eq(lm2)) - ListMapLemmas.addStillContains(addToMapMapFromBucket(tl, extractMap(lm.toList)), hd._1, hd._2, key) - assert(addToMapMapFromBucket(tl, extractMap(lm.toList)).contains(key)) - assert((addToMapMapFromBucket(tl, extractMap(lm.toList)) + hd).contains(key)) - ListMapLemmas.lemmaEquivalentThenSameContains(lm1, lm2, key) - check(addToMapMapFromBucket(tl, extractMap(lm.toList) + hd).contains(key)) - - case Nil() => () - } - - } ensuring (_ => addToMapMapFromBucket(l, extractMap(lm.toList)).contains(key)) - - @opaque - @inlineOnce - @ghost - def lemmaAddToMapFromBucketMinusKeyIsCommutative[K, V](lhm: ListMap[K, V], key: K, l: List[(K, V)]): Unit = { - require(!containsKey(l, key)) - require(noDuplicateKeys(l)) - decreases(l) - - l match { - case Cons((k, v), t) => - val newAcc = lhm + (k, v) - ListMapLemmas.addRemoveCommutativeForDiffKeys(lhm, k, v, key) - lemmaAddToMapFromBucketMinusKeyIsCommutative(newAcc, key, t) - case Nil() => - check(addToMapMapFromBucket(l, (lhm - key)) == addToMapMapFromBucket(l, lhm) - key) - } - - } ensuring (_ => addToMapMapFromBucket(l, (lhm - key)) == addToMapMapFromBucket(l, lhm) - key) - - @opaque - @inlineOnce - @ghost - def lemmaAddToMapFromBucketPlusKeyValueIsCommutative[K, V](lhm: ListMap[K, V], key: K, value: V, l: List[(K, V)]): Unit = { - require(!containsKey(l, key)) - require(noDuplicateKeys(l)) - decreases(l) - - l match { - case Cons((k, v), t) => - val newAcc = lhm + (k, v) - ListMapLemmas.addCommutativeForDiffKeys(lhm, k, v, key, value) - lemmaAddToMapFromBucketPlusKeyValueIsCommutative(newAcc, key, value, t) - check(addToMapMapFromBucket(t, (newAcc + (key, value))).eq(addToMapMapFromBucket(t, newAcc) + (key, value))) - //addToMapMapFromBucket(tl, acc + (k, v)) - - check(addToMapMapFromBucket(l, (lhm + (key, value))) == addToMapMapFromBucket(t, (lhm + (key, value)) + (k, v))) - check(((lhm + (k, v)) + (key, value)).eq((lhm + (key, value)) + (k, v))) - val lhm1 = (lhm + (key, value)) + (k, v) - val lhm2 = (lhm + (k, v)) + (key, value) - lemmaAddToMapFromBucketPreservesEquivalence(lhm1, lhm2, t) - check(addToMapMapFromBucket(t, lhm1).eq(addToMapMapFromBucket(t, lhm2))) - case Nil() => - check(addToMapMapFromBucket(l, (lhm + (key, value))) == addToMapMapFromBucket(l, lhm) + (key, value)) - } - - } ensuring (_ => addToMapMapFromBucket(l, (lhm + (key, value))).eq(addToMapMapFromBucket(l, lhm) + (key, value))) - - @opaque - @inlineOnce - def lemmaAddToMapFromBucketPreservesEquivalence[K, V](lhm1: ListMap[K, V], lhm2: ListMap[K, V], l: List[(K, V)]): Unit = { - require(noDuplicateKeys(l)) - require(lhm1.eq(lhm2)) - decreases(l) - - l match { - case Cons((k, v), t) => - val newAcc1 = lhm1 + (k, v) - val newAcc2 = lhm2 + (k, v) - ListMapLemmas.lemmaAddToEqMapsPreservesEq(lhm1, lhm2, k, v) - check(newAcc1.eq(newAcc2)) - lemmaAddToMapFromBucketPreservesEquivalence(newAcc1, newAcc2, t) - case Nil() => - check(lhm1.eq(lhm2)) - } - } ensuring (_ => addToMapMapFromBucket(l, lhm1).eq(addToMapMapFromBucket(l, lhm2))) - - @opaque - @inlineOnce - @ghost - def lemmaChangeOneBucketToRemoveAKeyRemoveThisKeyInGenMapOneHashIsHead[K, V]( - lm: ListLongMap[List[(K, V)]], - hash: Long, - oldBucket: List[(K, V)], - newBucket: List[(K, V)], - key: K, - hashF: Hashable[K], - - ): Unit = { - require(lm.toList.forall((k, v) => noDuplicateKeys(v))) - require(noDuplicateKeys(oldBucket)) - require(noDuplicateKeys(newBucket)) - require(removePairForKey(oldBucket, key) == newBucket) - require(allKeysSameHash(oldBucket, hash, hashF)) - require(extractMap(lm.toList).contains(key)) - require(hashF.hash(key) == hash) - require(allKeysSameHash(newBucket, hash, hashF)) - require(allKeysSameHashInMap(lm, hashF)) - require(noDuplicateKeys(newBucket)) - require(lm.toList.head == (hash, oldBucket)) - decreases(oldBucket.size) - - val l = lm.toList - l match - case Cons(h, t) => - check(t == lm.tail.toList) - check(extractMap(l) == addToMapMapFromBucket(h._2, extractMap(t))) - // check(extractMap(t) == ListMap.empty[K, V]) - oldBucket match { - case Cons(hd, tl) if hd._1 == key => - assert(oldBucket.tail == newBucket) - assert(extractMap(Cons((hash, oldBucket.tail), t)) == extractMap(Cons((hash, newBucket), t))) - assert(addToMapMapFromBucket(oldBucket.tail, extractMap(t)) == addToMapMapFromBucket(newBucket, extractMap(t))) - - lemmaAddToMapFromBucketTlPlusHeadIsSameAsList(oldBucket.head, oldBucket.tail, extractMap(t)) - assert((addToMapMapFromBucket(oldBucket.tail, extractMap(t)) + hd).eq(addToMapMapFromBucket(oldBucket, extractMap(t)))) - - assert(!containsKey(oldBucket.tail, key)) - val m = addToMapMapFromBucket(oldBucket.tail, extractMap(t)) - assert(m == extractMap(Cons((hash, oldBucket.tail), t))) - lemmaNotInItsHashBucketThenNotInMap(ListLongMap(Cons((hash, oldBucket.tail), t)), key, hashF) - assert(!m.contains(key)) - ListMapLemmas.addThenRemoveForNewKeyIsSame(m, key, hd._2) - assert((m + hd) - key == m) - assert((m + hd).eq(extractMap(Cons((hash, oldBucket), t)))) - ListMapLemmas.lemmaRemovePreservesEq(m + hd, extractMap(Cons((hash, oldBucket), t)), key) - assert((extractMap(Cons((hash, oldBucket), t)) - key).eq(extractMap(Cons((hash, oldBucket.tail), t)))) - assert(extractMap(Cons((hash, newBucket), t)).eq(extractMap(Cons((hash, oldBucket), t)) - key)) - case Cons(hd, tl) if hd._1 != key => - lemmaInGenMapThenGetPairDefined(lm, key, hashF) - lemmaGetPairDefinedThenContainsKey(oldBucket, key, hashF) - assert(containsKey(oldBucket, key)) - assert(containsKey(tl, key)) - assert(removePairForKey(oldBucket, key) == newBucket) - assert(removePairForKey(oldBucket.tail, key) == newBucket.tail) - assert(removePairForKey(tl, key) == newBucket.tail) - - lemmaListContainsThenExtractedMapContains(ListLongMap(Cons((hash, tl), t)), key, hashF) - assert(extractMap(Cons((hash, tl), t)).contains(key)) - - lemmaAddToMapFromBucketTlPlusHeadIsSameAsList(oldBucket.head, oldBucket.tail, extractMap(t)) - lemmaAddToMapFromBucketTlPlusHeadIsSameAsList(newBucket.head, newBucket.tail, extractMap(t)) - assert(extractMap(Cons((hash, oldBucket), t)).eq(extractMap(Cons((hash, oldBucket.tail), t)) + hd)) - assert(hd == newBucket.head) - assert(extractMap(Cons((hash, oldBucket), t)).eq(extractMap(Cons((hash, oldBucket.tail), t)) + newBucket.head)) - - assert(extractMap(Cons((hash, tl), t)).contains(key)) - lemmaChangeOneBucketToRemoveAKeyRemoveThisKeyInGenMapOneHashIsHead(ListLongMap(Cons((hash, tl), t)), hash, tl, newBucket.tail, key, hashF) - - assert(extractMap(Cons((hash, newBucket.tail), t)).eq(extractMap(Cons((hash, tl), t)) - key)) - - ListMapLemmas.addRemoveCommutativeForDiffKeys(extractMap(Cons((hash, tl), t)), hd._1, hd._2, key) - //lm + (a1, b1) - a2 == lm - a2 + (a1, b1) - assert(extractMap(Cons((hash, tl), t)) + hd - key == extractMap(Cons((hash, tl), t)) - key + hd) - assert((extractMap(Cons((hash, tl), t)) + hd - key).eq(extractMap(Cons((hash, tl), t)) - key + hd)) - - assert((extractMap(Cons((hash, tl), t)) + hd).eq(extractMap(Cons((hash, oldBucket), t)))) - - ListMapLemmas.lemmaAddToEqMapsPreservesEq(extractMap(Cons((hash, tl), t)) - key, extractMap(Cons((hash, newBucket.tail), t)), hd._1, hd._2) - assert(((extractMap(Cons((hash, tl), t)) - key) + hd).eq(extractMap(Cons((hash, newBucket.tail), t)) + hd)) - - assert(((extractMap(Cons((hash, tl), t)) + hd)).eq(extractMap(Cons((hash, oldBucket), t)))) - ListMapLemmas.lemmaRemovePreservesEq((extractMap(Cons((hash, tl), t)) + hd), extractMap(Cons((hash, oldBucket), t)), key) - assert(((extractMap(Cons((hash, tl), t)) + hd) - key).eq(extractMap(Cons((hash, oldBucket), t)) - key)) - assert(((extractMap(Cons((hash, tl), t)) - key) + hd).eq(extractMap(Cons((hash, oldBucket), t)) - key)) - - assert((extractMap(Cons((hash, newBucket.tail), t)) + hd).eq(extractMap(Cons((hash, newBucket), t)))) - - assert(extractMap(Cons((hash, newBucket), t)).eq(extractMap(Cons((hash, oldBucket), t)) - key)) - case Nil() => - lemmaNotInItsHashBucketThenNotInMap(lm, key, hashF) - check(false) - } - - case Nil() => check(false) - - } ensuring (_ => { - extractMap(Cons((hash, newBucket), lm.toList.tail)).eq(extractMap(Cons((hash, oldBucket), lm.toList.tail)) - key) - }) - - @opaque - @inlineOnce - @ghost - def lemmaChangeOneBucketToRemoveAKeyRemoveThisKeyInGenMapOneHash[K, V](hash: Long, oldBucket: List[(K, V)], newBucket: List[(K, V)], key: K, hashF: Hashable[K]): Unit = { - require(noDuplicateKeys(oldBucket)) - require(noDuplicateKeys(newBucket)) - require(removePairForKey(oldBucket, key) == newBucket) - require(allKeysSameHash(oldBucket, hash, hashF)) - require(extractMap(List((hash, oldBucket))).contains(key)) - require(hashF.hash(key) == hash) - require(allKeysSameHash(newBucket, hash, hashF)) - require(noDuplicateKeys(newBucket)) - decreases(oldBucket.size) - - val l = List((hash, oldBucket)) - l match - case Cons(h, t) => - check(t.isEmpty) - check(extractMap(l) == addToMapMapFromBucket(h._2, extractMap(t))) - check(extractMap(t) == ListMap.empty[K, V]) - oldBucket match { - case Cons(hd, tl) if hd._1 == key => - assert(oldBucket.tail == newBucket) - assert(extractMap(List((hash, oldBucket.tail))) == extractMap(List((hash, newBucket)))) - assert(addToMapMapFromBucket(oldBucket.tail, ListMap.empty[K, V]) == addToMapMapFromBucket(newBucket, ListMap.empty[K, V])) - - lemmaAddToMapFromBucketTlPlusHeadIsSameAsList(oldBucket.head, oldBucket.tail, ListMap.empty[K, V]) - assert((addToMapMapFromBucket(oldBucket.tail, ListMap.empty[K, V]) + hd).eq(addToMapMapFromBucket(oldBucket, ListMap.empty[K, V]))) - - assert(!containsKey(oldBucket.tail, key)) - val m = addToMapMapFromBucket(oldBucket.tail, ListMap.empty[K, V]) - assert(m == extractMap(List((hash, oldBucket.tail)))) - lemmaNotInItsHashBucketThenNotInMap(ListLongMap(List((hash, oldBucket.tail))), key, hashF) - assert(!m.contains(key)) - ListMapLemmas.addThenRemoveForNewKeyIsSame(m, key, hd._2) - assert((m + hd) - key == m) - assert(extractMap(List((hash, oldBucket))) == addToMapMapFromBucket(oldBucket, ListMap.empty[K, V])) - assert((m + hd).eq(extractMap(List((hash, oldBucket))))) - ListMapLemmas.lemmaRemovePreservesEq(m + hd, extractMap(List((hash, oldBucket))), key) - assert(extractMap(List((hash, newBucket))) == m) - assert((extractMap(List((hash, oldBucket))) - key).eq(extractMap(List((hash, newBucket))))) - - case Cons(hd, tl) if hd._1 != key => - lemmaInGenMapThenGetPairDefined(ListLongMap(List((hash, oldBucket))), key, hashF) - lemmaGetPairDefinedThenContainsKey(oldBucket, key, hashF) - assert(containsKey(oldBucket, key)) - assert(containsKey(tl, key)) - assert(removePairForKey(oldBucket, key) == newBucket) - assert(removePairForKey(oldBucket.tail, key) == newBucket.tail) - assert(removePairForKey(tl, key) == newBucket.tail) - - lemmaListContainsThenExtractedMapContains(ListLongMap(List((hash, tl))), key, hashF) - assert(extractMap(List((hash, tl))).contains(key)) - - lemmaAddToMapFromBucketTlPlusHeadIsSameAsList(oldBucket.head, oldBucket.tail, ListMap.empty[K, V]) - lemmaAddToMapFromBucketTlPlusHeadIsSameAsList(newBucket.head, newBucket.tail, ListMap.empty[K, V]) - assert(extractMap(List((hash, oldBucket))).eq(extractMap(List((hash, oldBucket.tail))) + hd)) - assert(extractMap(List((hash, newBucket))).eq(extractMap(List((hash, newBucket.tail))) + newBucket.head)) - - lemmaChangeOneBucketToRemoveAKeyRemoveThisKeyInGenMapOneHash(hash, tl, newBucket.tail, key, hashF) - assert(extractMap(List((hash, newBucket.tail))).eq(extractMap(List((hash, tl))) - key)) - ListMapLemmas.addRemoveCommutativeForDiffKeys(extractMap(List((hash, tl))), hd._1, hd._2, key) - //lm + (a1, b1) - a2 == lm - a2 + (a1, b1) - assert(extractMap(List((hash, tl))) + hd - key == extractMap(List((hash, tl))) - key + hd) - ListMapLemmas.lemmaRemovePreservesEq(extractMap(List((hash, tl))) + hd, extractMap(List((hash, oldBucket))), key) - assert((extractMap(List((hash, oldBucket))) - key).eq(extractMap(List((hash, tl))) - key + hd)) - assert(newBucket.head == hd) - assert(extractMap(List((hash, newBucket))).eq(extractMap(List((hash, newBucket.tail))) + hd)) - ListMapLemmas.lemmaAddToEqMapsPreservesEq(extractMap(List((hash, tl))) - key, extractMap(List((hash, newBucket.tail))), hd._1, hd._2) - assert(extractMap(List((hash, newBucket))).eq(extractMap(List((hash, tl))) - key + hd)) - assert(extractMap(List((hash, oldBucket))).eq(extractMap(List((hash, tl))) + hd)) - assert(extractMap(List((hash, newBucket))).eq(extractMap(List((hash, oldBucket))) - key)) - case Nil() => check(false) - } - - case Nil() => check(false) - - } ensuring (_ => { - extractMap(List((hash, newBucket))).eq(extractMap(List((hash, oldBucket))) - key) - }) - - @opaque - @inlineOnce - @ghost - def lemmaInLongMapAllKeySameHashThenForTuple[K, V](lml: List[(Long, List[(K, V)])], hash: Long, bucket: List[(K, V)], hashF: Hashable[K]): Unit = { - require(lml.forall((k, v) => allKeysSameHash(v, k, hashF))) - require(lml.contains((hash, bucket))) - decreases(lml.size) - - ListSpecs.forallContained(lml, (k, v) => allKeysSameHash(v, k, hashF), (hash, bucket)) - - } ensuring (_ => allKeysSameHash(bucket, hash, hashF)) - - @opaque - @inlineOnce - @ghost - def lemmaRemovePairForKeyPreservesHash[K, V](@induct l: List[(K, V)], key: K, hash: Long, hashF: Hashable[K]): Unit = { - require(noDuplicateKeys(l)) - require(allKeysSameHash(l, hash, hashF)) - - } ensuring (_ => allKeysSameHash(removePairForKey(l, key), hash, hashF)) - - @opaque - @inlineOnce - @ghost - def lemmaChangeOneBucketByAValidOnePreservesForallNoDuplicatesAndHash[K, V](lm: ListLongMap[List[(K, V)]], hash: Long, newBucket: List[(K, V)], hashF: Hashable[K]): Unit = { - require(lm.toList.forall((k, v) => noDuplicateKeys(v))) - require(allKeysSameHashInMap(lm, hashF)) - require(noDuplicateKeys(newBucket)) - require(allKeysSameHash(newBucket, hash, hashF)) - decreases(lm.toList.size) - lm.toList match - case Cons(h, t) => { - lemmaChangeOneBucketByAValidOnePreservesForallNoDuplicatesAndHash(ListLongMap(t), hash, newBucket, hashF) - } - case Nil() => () - - } ensuring (_ => { - val newMap = old(lm) + (hash, newBucket) - newMap.toList.forall((k, v) => noDuplicateKeys(v)) && allKeysSameHashInMap(newMap, hashF) - }) - - @opaque - @inlineOnce - @ghost - def lemmaRemoveNotContainedDoesNotChange[K, V](lm: ListLongMap[List[(K, V)]], key: K, hashF: Hashable[K]): Unit = { - require(lm.toList.forall((k, v) => noDuplicateKeys(v))) - require(allKeysSameHashInMap(lm, hashF)) - require(!extractMap(lm.toList).contains(key)) - - ListMapLemmas.removeNotPresentStillSame(extractMap(lm.toList), key) - - } ensuring (_ => extractMap(lm.toList) == extractMap(lm.toList) - key) - - @opaque - @inlineOnce - @ghost - def lemmaRemovePairForKeyPreservesNoDuplicateKeys[K, V](l: List[(K, V)], key: K): Unit = { - require(noDuplicateKeys(l)) - decreases(l) - - l match { - case Cons(hd, tl) if hd._1 == key => () - case Cons(hd, tl) => { - lemmaRemovePairForKeyPreservesNoDuplicateKeys(tl, key) - lemmaRemovePairForKeyPreservesNotContainsKey(tl, key, hd._1) - lemmaNotContainsKeyThenCannotContainPair(removePairForKey(tl, key), hd._1, hd._2) - } - case Nil() => () - } - - } ensuring (_ => noDuplicateKeys(removePairForKey(l, key))) - - @opaque - @inlineOnce - @ghost - def lemmaRemovePairForKeyPreservesNotContainsKey[K, V](@induct l: List[(K, V)], key: K, otherK: K): Unit = { - require(noDuplicateKeys(l)) - require(otherK != key) - require(!containsKey(l, otherK)) - - } ensuring (_ => !containsKey(removePairForKey(l, key), otherK)) - - @opaque - @inlineOnce - @ghost - def lemmaInGenMapThenGetPairDefined[K, V](lm: ListLongMap[List[(K, V)]], key: K, hashF: Hashable[K]): Unit = { - require(lm.toList.forall((k, v) => noDuplicateKeys(v))) - require(allKeysSameHashInMap(lm, hashF)) - require(extractMap(lm.toList).contains(key)) - - decreases(lm.toList.size) - lemmaInGenMapThenLongMapContainsHash(lm, key, hashF) - ListLongMapLemmas.lemmaGetValueImpliesTupleContained(lm, hashF.hash(key), lm.apply(hashF.hash(key))) - ListSpecs.forallContained(lm.toList, (k, v) => noDuplicateKeys(v), (hashF.hash(key), lm.apply(hashF.hash(key)))) - - if (getPair(lm.apply(hashF.hash(key)), key).isEmpty) { - val l = lm.apply(hashF.hash(key)) - lemmaNotInItsHashBucketThenNotInMap(lm, key, hashF) - check(false) - } - check(getPair(lm.apply(hashF.hash(key)), key).isDefined) - - } ensuring (_ => { - lemmaInGenMapThenLongMapContainsHash(lm, key, hashF) - ListLongMapLemmas.lemmaGetValueImpliesTupleContained(lm, hashF.hash(key), lm.apply(hashF.hash(key))) - ListSpecs.forallContained(lm.toList, (k, v) => noDuplicateKeys(v), (hashF.hash(key), lm.apply(hashF.hash(key)))) - getPair(lm.apply(hashF.hash(key)), key).isDefined - }) - - @opaque - @inlineOnce - @ghost - def lemmaNotInItsHashBucketThenNotInMap[K, V](lm: ListLongMap[List[(K, V)]], key: K, hashF: Hashable[K]): Unit = { - require(lm.toList.forall((k, v) => noDuplicateKeys(v))) - require(allKeysSameHashInMap(lm, hashF)) - require(lm.contains(hashF.hash(key))) - require(!containsKey(lm.apply(hashF.hash(key)), key)) - - decreases(lm.toList.size) - lm.toList match { - case Cons(hd, tl) if hd._1 == hashF.hash(key) => - lemmaHashNotInLongMapThenNotInGenerated(lm.tail, key, hashF) - lemmaAddToMapFromBucketContainsIIFInAccOrL(hd._2, extractMap(tl), key) - case Cons(hd, tl) => - assert(hd._1 != hashF.hash(key)) - lemmaNotSameHashThenCannotContainKey(lm, key, hd._1, hashF) - lemmaAddToMapFromBucketContainsIIFInAccOrL(hd._2, extractMap(tl), key) - lemmaNotInItsHashBucketThenNotInMap(lm.tail, key, hashF) - case Nil() => () - } - - } ensuring (_ => !extractMap(lm.toList).contains(key)) - - @opaque - @inlineOnce - @ghost - def lemmaInGenMapThenLongMapContainsHash[K, V](lm: ListLongMap[List[(K, V)]], key: K, hashF: Hashable[K]): Unit = { - require(lm.toList.forall((k, v) => noDuplicateKeys(v))) - require(allKeysSameHashInMap(lm, hashF)) - require(extractMap(lm.toList).contains(key)) - decreases(lm.toList.size) - - val hash = hashF.hash(key) - - if (!lm.contains(hashF.hash(key))) { - lemmaHashNotInLongMapThenNotInGenerated(lm, key, hashF) - check(!extractMap(lm.toList).contains(key)) - check(false) - } - - } ensuring (_ => lm.contains(hashF.hash(key))) - - @opaque - @inlineOnce - @ghost - def lemmaHashNotInLongMapThenNotInGenerated[K, V](lm: ListLongMap[List[(K, V)]], key: K, hashF: Hashable[K]): Unit = { - require(lm.toList.forall((k, v) => noDuplicateKeys(v))) - require(allKeysSameHashInMap(lm, hashF)) - require(!lm.contains(hashF.hash(key))) - decreases(lm.toList.size) - - lm.toList match { - case Cons(hd, tl) => - lemmaAddToMapFromBucketContainsIIFInAccOrL(hd._2, extractMap(tl), key) - lemmaNotSameHashThenCannotContainKey(lm, key, hd._1, hashF) - lemmaHashNotInLongMapThenNotInGenerated(lm.tail, key, hashF) - case Nil() => () - } - - } ensuring (_ => !extractMap(lm.toList).contains(key)) - - @opaque - @inlineOnce - @ghost - def lemmaAddToMapFromBucketContainsIIFInAccOrL[K, V](l: List[(K, V)], acc: ListMap[K, V], key: K): Unit = { - require(noDuplicateKeys(l)) - decreases(l) - l match { - case Nil() => () - case Cons((k, v), tl) => - val newAcc = acc + (k, v) - val res = addToMapMapFromBucket(tl, acc + (k, v)) - lemmaAddToMapFromBucketContainsIIFInAccOrL(tl, newAcc, key) - if (acc.contains(key)) { - ListMapLemmas.addStillContains(acc, k, v, key) - } else if (k == key) { - () - } else if (containsKey(tl, key)) { - () - } else { - lemmaAddToMapFromBucketContainsIIFInAccOrL(tl, newAcc, key) - ListMapLemmas.addStillNotContains(acc, k, v, key) - - } - } - } ensuring (_ => (addToMapMapFromBucket(l, acc).contains(key) == (containsKey(l, key) || acc.contains(key)))) - - @opaque - @inlineOnce - @ghost - def lemmaAddToMapFromBucketTlPlusHeadIsSameAsList[K, V](t: (K, V), l: List[(K, V)], acc: ListMap[K, V]): Unit = { - require(noDuplicateKeys(l)) - require(noDuplicateKeys(Cons(t, l))) - decreases(l) - l match { - case Nil() => () - case Cons((k, v), tl) => - val newAcc = acc + (k, v) - val res = addToMapMapFromBucket(tl, acc + (k, v)) - lemmaAddToMapFromBucketTlPlusHeadIsSameAsList(t, tl, newAcc) - - check(addToMapMapFromBucket(tl, newAcc).eq(addToMapMapFromBucket(l, acc))) - check(addToMapMapFromBucket(Cons(t, tl), newAcc).eq(addToMapMapFromBucket(tl, newAcc) + t)) - check(addToMapMapFromBucket(Cons(t, tl), newAcc).eq(addToMapMapFromBucket(tl, newAcc + t))) - check(addToMapMapFromBucket(Cons(t, tl), newAcc).eq(addToMapMapFromBucket(tl, acc + (k, v) + t))) - check(addToMapMapFromBucket(Cons(t, l), acc).eq(addToMapMapFromBucket(l, acc + t))) - check(addToMapMapFromBucket(Cons(t, l), acc).eq(addToMapMapFromBucket(tl, acc + t + (k, v)))) - ListMapLemmas.addCommutativeForDiffKeys(acc, k, v, t._1, t._2) - ListMapLemmas.addCommutativeForDiffKeys(acc, t._1, t._2, k, v) - check((acc + t + (k, v)).eq(acc + (k, v) + t)) - lemmaAddToMapFromBucketPreservesEquivalence(acc + (k, v) + t, acc + t + (k, v), tl) - check(addToMapMapFromBucket(Cons(t, l), acc).eq(addToMapMapFromBucket(tl, acc + (k, v) + t))) - check(addToMapMapFromBucket(Cons(t, l), acc).eq(addToMapMapFromBucket(l, acc) + t)) - } - - } ensuring (_ => addToMapMapFromBucket(Cons(t, l), acc).eq(addToMapMapFromBucket(l, acc) + t)) - - - - @opaque - @inlineOnce - @ghost - def lemmaExtractMapPreservesMapping[K, V](lm: ListLongMap[List[(K, V)]], key: K, value: V, hashF: Hashable[K]): Unit = { - require(lm.toList.forall((k, v) => noDuplicateKeys(v))) - require(allKeysSameHashInMap(lm, hashF)) - require(extractMap(lm.toList).contains(key)) - require({ - lemmaInGenericMapThenInLongMap(lm, key, hashF) - lemmaInLongMapThenContainsKeyBiggerList(lm, key, hashF) - getValue(lm.toList, key) == value - }) - decreases(lm.toList.size) - - lm.toList match { - case Cons(hd, tl) => - if (containsKey(hd._2, key)) { - ListSpecs.forallContained(lm.toList, (k, v) => allKeysSameHash(v, k, hashF), (hd._1, hd._2)) - ListSpecs.forallContained(hd._2, p => hashF.hash(p._1) == hd._1, (key, value)) - lemmaHashNotInLongMapThenNotInGenerated(lm.tail, key, hashF) - lemmaAddToMapFromBucketMaintainsMapping(hd._2, extractMap(tl), key, value) - - } else { - check(!containsKey(hd._2, key)) - if (!lm.tail.contains(hashF.hash(key))) { - lemmaHashNotInLongMapThenNotInGenerated(lm.tail, key, hashF) - } - lemmaInLongMapThenContainsKeyBiggerList(lm, key, hashF) - lemmaListContainsThenExtractedMapContains(lm.tail, key, hashF) - lemmaExtractMapPreservesMapping(lm.tail, key, value, hashF) - lemmaAddToMapFromBucketMaintainsMapping(hd._2, extractMap(tl), key, value) - } - - case Nil() => () - } - - } ensuring (_ => extractMap(lm.toList).apply(key) == value) - - @opaque - @inlineOnce - @ghost - def lemmaAddToMapFromBucketMaintainsMapping[K, V](l: List[(K, V)], acc: ListMap[K, V], key: K, value: V): Unit = { - require(noDuplicateKeys(l)) - require(addToMapMapFromBucket(l, acc).contains(key)) - require(acc.contains(key) && acc.apply(key) == value && !containsKey(l, key) || containsKey(l, key) && l.contains((key, value)) && !acc.contains(key)) - decreases(l) - l match { - case Nil() => () - case Cons((k, v), tl) => - val newAcc = acc + (k, v) - val res = addToMapMapFromBucket(tl, acc + (k, v)) - - if (k == key) { - lemmaNotContainsKeyThenCannotContainPair(tl, key, value) - assert(v == value) - - lemmaAddToMapFromBucketMaintainsMapping(tl, newAcc, key, value) - } else if (acc.contains(key)) { - ListMapLemmas.addStillContains(acc, k, v, key) - ListMapLemmas.addApplyDifferent(acc, k, v, key) - lemmaNotContainsKeyThenCannotContainPair(l, key, value) - - lemmaAddToMapFromBucketMaintainsMapping(tl, newAcc, key, value) - } else { - assert(!acc.contains(key)) - assert(tl.contains((key, value))) - ListMapLemmas.addStillNotContains(acc, k, v, key) - lemmaAddToMapFromBucketMaintainsMapping(tl, newAcc, key, value) - - } - } - } ensuring (_ => addToMapMapFromBucket(l, acc).apply(key) == value) - - @opaque - @inlineOnce - @ghost - def lemmaInLongMapThenContainsKeyBiggerList[K, V](lm: ListLongMap[List[(K, V)]], key: K, hashF: Hashable[K]): Unit = { - require(lm.toList.forall((k, v) => noDuplicateKeys(v))) - require(allKeysSameHashInMap(lm, hashF)) - require(lm.contains(hashF.hash(key))) - require({ - ListLongMapLemmas.lemmaGetValueImpliesTupleContained(lm, hashF.hash(key), lm.apply(hashF.hash(key))) - ListSpecs.forallContained(lm.toList, (k, v) => noDuplicateKeys(v), (hashF.hash(key), lm.apply(hashF.hash(key)))) - getPair(lm.apply(hashF.hash(key)), key).isDefined - }) - decreases(lm.toList.size) - lm.toList match - case Cons(hd, tl) if hd._1 == hashF.hash(key) => lemmaGetPairDefinedThenContainsKey(hd._2, key, hashF) - case Cons(hd, tl) => - assert(hashF.hash(key) != hd._1) - lemmaNotSameHashThenCannotContainKey(lm, key, hd._1, hashF) - assert(!containsKey(hd._2, key)) - lemmaInLongMapThenContainsKeyBiggerList(lm.tail, key, hashF) - check(containsKeyBiggerList(lm.toList, key)) - case Nil() => check(containsKeyBiggerList(lm.toList, key)) - - } ensuring (_ => containsKeyBiggerList(lm.toList, key)) - - @opaque - @inlineOnce - @ghost - def lemmaListContainsThenExtractedMapContains[K, V](lm: ListLongMap[List[(K, V)]], key: K, hashF: Hashable[K]): Unit = { - require(lm.toList.forall((k, v) => noDuplicateKeys(v))) - require(allKeysSameHashInMap(lm, hashF)) - require(containsKeyBiggerList(lm.toList, key)) - decreases(lm.toList.size) - - lm.toList match - case Cons(hd, tl) if containsKey(hd._2, key) => { - val v = getValue(lm.toList, key) - lemmaInPairListHeadThenGetValueInTuple(lm, key, v, hashF) - check(hd._2.contains((key, v))) - check(extractMap(lm.toList) == addToMapMapFromBucket(hd._2, extractMap(tl))) - ListSpecs.forallContained(hd._2, p => addToMapMapFromBucket(hd._2, extractMap(tl)).contains(p._1), (key, v)) - } - case Cons(hd, tl) => { - assert(extractMap(lm.toList) == addToMapMapFromBucket(hd._2, extractMap(tl))) - lemmaInBiggerListButNotHeadThenTail(lm, key, hashF) - assert(containsKeyBiggerList(tl, key)) - lemmaListContainsThenExtractedMapContains(lm.tail, key, hashF) - assert(extractMap(tl).contains(key)) - val v = extractMap(tl).get(key).get - val m = extractMap(tl) - ListMapLemmas.lemmaGetValueImpliesTupleContained(extractMap(tl), key, v) - // lemmaGetValueInExtractMapToList(tl, key, v, hashF) - check(extractMap(tl).toList.contains((key, v))) - ListSpecs.forallContained(extractMap(tl).toList, p => extractMap(lm.toList).contains(p._1), (key, v)) - } - case Nil() => () - - } ensuring (_ => extractMap(lm.toList).contains(key)) - - @opaque - @inlineOnce - @ghost - def lemmaInPairListHeadThenGetValueInTuple[K, V](lm: ListLongMap[List[(K, V)]], key: K, v: V, hashF: Hashable[K]): Unit = { - require(lm.toList.forall((k, v) => noDuplicateKeys(v))) - require(allKeysSameHashInMap(lm, hashF)) - require(containsKeyBiggerList(lm.toList, key) && containsKey(lm.toList.head._2, key)) - require(v == getValue(lm.toList, key)) - - } ensuring (_ => lm.toList.head._2.contains((key, v))) - - @opaque - @inlineOnce - @ghost - def lemmaGetValueEquivToGetPair[K, V](lm: ListLongMap[List[(K, V)]], key: K, v: V, hashF: Hashable[K]): Unit = { - require(lm.toList.forall((k, v) => noDuplicateKeys(v))) - require(allKeysSameHashInMap(lm, hashF)) - require(containsKeyBiggerList(lm.toList, key)) - require({ - lemmaListContainsThenExtractedMapContains(lm, key, hashF) - lemmaInGenMapThenLongMapContainsHash(lm, key, hashF) - ListLongMapLemmas.lemmaGetValueImpliesTupleContained(lm, hashF.hash(key), lm.apply(hashF.hash(key))) - ListSpecs.forallContained(lm.toList, (k, v) => noDuplicateKeys(v), (hashF.hash(key), lm.apply(hashF.hash(key)))) - lemmaInGenMapThenGetPairDefined(lm, key, hashF) - getPair(lm.apply(hashF.hash(key)), key).get._2 == v - }) - decreases(lm.toList.size) - - lm.toList match { - case Cons(hd, tl) if hd._1 == hashF.hash(key) => - ListSpecs.forallContained(lm.toList, (k, v) => allKeysSameHash(v, k, hashF), (hd._1, hd._2)) - if (!containsKey(hd._2, key)) { - lemmaNotInItsHashBucketThenNotInMap(lm, key, hashF) - lemmaListContainsThenExtractedMapContains(lm, key, hashF) - check(false) - } - case Cons(hd, tl) => - lemmaNotSameHashThenCannotContainKey(lm, key, hd._1, hashF) - assert(!containsKey(hd._2, key)) - lemmaInBiggerListButNotHeadThenTail(lm, key, hashF) - lemmaGetValueEquivToGetPair(lm.tail, key, v, hashF) - case Nil() => () - } - - } ensuring (_ => getValue(lm.toList, key) == v) - @opaque - @inlineOnce - @ghost - def lemmaInBiggerListButNotHeadThenTail[K, V](lm: ListLongMap[List[(K, V)]], key: K, hashF: Hashable[K]): Unit = { - require(lm.toList.forall((k, v) => noDuplicateKeys(v))) - require(allKeysSameHashInMap(lm, hashF)) - require(containsKeyBiggerList(lm.toList, key)) - require(!containsKey(lm.toList.head._2, key)) - decreases(lm.toList.size) - - } ensuring (_ => containsKeyBiggerList(lm.toList.tail, key)) - - @opaque - @inlineOnce - @ghost - def lemmaGetPairDefinedThenContainsKey[K, V](l: List[(K, V)], key: K, hashF: Hashable[K]): Unit = { - require(noDuplicateKeys(l)) - require(getPair(l, key).isDefined) - decreases(l) - l match - case Cons(hd, tl) if hd._1 == key => () - case Cons(_, tl) => lemmaGetPairDefinedThenContainsKey(tl, key, hashF) - case Nil() => () - - } ensuring (_ => containsKey(l, key)) - - @opaque - @inlineOnce - @ghost - def lemmaNotSameHashThenCannotContainKey[K, V](lm: ListLongMap[List[(K, V)]], key: K, hash: Long, hashF: Hashable[K]): Unit = { - require(lm.toList.forall((k, v) => noDuplicateKeys(v))) - require(allKeysSameHashInMap(lm, hashF)) - // require(lm.contains(hashF.hash(key))) - require(lm.contains(hash)) - require(hash != hashF.hash(key)) - - val listHash = lm.apply(hash) - - ListLongMapLemmas.lemmaGetValueImpliesTupleContained(lm, hash, listHash) - ListSpecs.forallContained(lm.toList, (k, v) => allKeysSameHash(v, k, hashF), (hash, listHash)) - assert(allKeysSameHash(listHash, hash, hashF)) - if (containsKey(listHash, key)) { - ListSpecs.forallContained(lm.toList, (k, v) => noDuplicateKeys(v), (hash, listHash)) - ListSpecs.forallContained(listHash, (k, v) => hashF.hash(k) == hash, (key, getPair(listHash, key).get._2)) - check(false) - } - } ensuring (_ => !containsKey(lm.apply(hash), key)) - - @opaque - @inlineOnce - @ghost - def lemmaNotContainsKeyThenCannotContainPair[K, V](l: List[(K, V)], key: K, v: V): Unit = { - require(!containsKey(l, key)) - decreases(l) - l match - case Cons(hd, tl) if hd._1 == key => check(false) - case Cons(_, tl) => lemmaNotContainsKeyThenCannotContainPair(tl, key, v) - case Nil() => () - - } ensuring (_ => !l.contains((key, v))) -} - diff --git a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/chassot/MutableHashMap.scala b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/chassot/MutableHashMap.scala new file mode 120000 index 00000000..333efe6a --- /dev/null +++ b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/chassot/MutableHashMap.scala @@ -0,0 +1 @@ +../../../../../../../../../data-structures/maps/mutablemaps/src/main/scala/ch/epfl/chassot/MutableHashMap.scala \ No newline at end of file diff --git a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/chassot/MutableLongMap.scala b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/chassot/MutableLongMap.scala deleted file mode 100644 index a7c83429..00000000 --- a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/chassot/MutableLongMap.scala +++ /dev/null @@ -1,8407 +0,0 @@ -/** Author: Samuel Chassot - */ -package ch.epfl.chassot - -import stainless.annotation._ -import stainless.collection._ -import stainless.equations._ -import stainless.lang.{ghost => ghostExpr, *} -import stainless.proof.check -import scala.annotation.tailrec -import stainless.lang.Cell - -import stainless.lang.StaticChecks.* // Comment out when using the OptimisedEnsuring object below -// import OptimisedChecks.* // Import to remove `ensuring` and `require` from the code for the benchmarks - -object MutableLongMap { - import LongMapFixedSize.validMask - - /** Helper method to create a new empty LongMap - * - * @param defaultEntry - * @return - */ - def getEmptyLongMap[V](defaultEntry: Long => V): LongMap[V] = { - val m = 15 - assert(validMask(m)) - LongMap(Cell(LongMapFixedSize.getNewLongMapFixedSize(m, defaultEntry))) - } ensuring (res => res.valid && res.size == 0) - - /** Helper method to create a new empty LongMap with a given initial array size WARNING: UNSOUND!!! The given size must be a power of 2 <= 2^30 - * - * @param defaultEntry - * @return - */ - def getEmptyLongMap[V](defaultEntry: Long => V, initialSize: Int): LongMap[V] = { - require(validMask(initialSize - 1)) - val m = initialSize - 1 - assert(validMask(m)) - LongMap(Cell(LongMapFixedSize.getNewLongMapFixedSize(m, defaultEntry))) - } ensuring (res => res.valid && res.size == 0) - - @mutable - final case class LongMap[V]( - val underlying: Cell[LongMapFixedSize[V]] - ) { - - @pure - def imbalanced(): Boolean = (2 * (underlying.v._size + underlying.v._vacant)) > underlying.v.mask || underlying.v._vacant > underlying.v._size - - @pure - def size: Int = underlying.v.size - - @pure - def isEmpty: Boolean = { - require(valid) - underlying.v.isEmpty - } ensuring (_ => valid) - - @pure - def contains(key: Long): Boolean = { - require(valid) - underlying.v.contains(key) - } ensuring (res => valid && (res == map.contains(key))) - - @pure - def apply(key: Long): V = { - require(valid) - underlying.v.apply(key) - } ensuring (res => - valid - && (if (contains(key)) res == map.get(key).get - else res == underlying.v.defaultEntry(key)) - ) - - def update(key: Long, v: V): Boolean = { - require(valid) - val repacked = if (imbalanced()) { - repack() - } else { - true - } - if (repacked) { - underlying.v.update(key, v) - } else { - false - } - } ensuring (res => valid && (if (res) map.contains(key) && (map == old(this).map + (key, v)) else map == old(this).map)) - - def remove(key: Long): Boolean = { - require(valid) - underlying.v.remove(key) - } ensuring (res => valid && (if (res) map == old(this).map - key else map == old(this).map)) - - // require(_size < 268435456) // Smallest size that can trigger a problem with a certain mask - @pure - def computeNewMask(oldMask: Int, _vacant: Int, _size: Int): Int = { - require(validMask(oldMask)) - require(_size >= 0 && _size <= oldMask + 1) - require(_vacant >= 0) - var m = oldMask - if (2 * (_size + _vacant) >= oldMask && !(5 * _vacant > oldMask)) { - m = ((m << 1) + 1) & MAX_MASK - } - while (m > 8 && 8 * _size < m && ((m >> 1) & MAX_MASK) + 1 >= _size) { - decreases(m) - m = m >>> 1 - } - m - } ensuring (res => validMask(res) && _size <= res + 1) - - def repack(): Boolean = { - require(valid) - - val newMask: Int = computeNewMask(underlying.v.mask, underlying.v._vacant, underlying.v._size) - val newMapCell: Cell[LongMapFixedSize[V]] = Cell(LongMapFixedSize.getNewLongMapFixedSize(newMask, underlying.v.defaultEntry)) - val resExtraKeys = if ((underlying.v.extraKeys & 1) != 0 && (underlying.v.extraKeys & 2) != 0) { - // it means there is a mapping for the key 0 and the Long.MIN_VALUE - val u1 = newMapCell.v.update(0L, underlying.v.zeroValue) - val u2 = newMapCell.v.update(Long.MinValue, underlying.v.minValue) - u1 && u2 - } else if ((underlying.v.extraKeys & 1) != 0 && (underlying.v.extraKeys & 2) == 0) { - // it means there is a mapping for the key 0 - newMapCell.v.update(0L, underlying.v.zeroValue) - } else if ((underlying.v.extraKeys & 2) != 0 && (underlying.v.extraKeys & 1) == 0) { - // it means there is a mapping for the key Long.MIN_VALUE - newMapCell.v.update(Long.MinValue, underlying.v.minValue) - } else { - true - } - - if (!resExtraKeys) { - false - } else { - assert(LongMapFixedSize.validMask(underlying.v.mask)) - assert(underlying.v._keys.length == underlying.v.mask + 1) - assert((underlying.v._keys.length - 1) >= 0) - val repackFromRes = repackFrom(newMapCell.v, underlying.v._keys.length - 1) - if (repackFromRes) { - // Swap the current underyling with the new one - swap(underlying, newMapCell) - true - } else { - false - } - } - } ensuring (res => res == false || map == old(this).map) - - // @tailrec - def repackFrom(newMap: LongMapFixedSize[V], from: Int): Boolean = { - require(valid) - require(from >= 0 && from < underlying.v._keys.length) - require(newMap.valid) - require(newMap.mask + 1 >= underlying.v._size) - require( - LongMapFixedSize.getCurrentListMap( - underlying.v._keys, - underlying.v._values, - underlying.v.mask, - underlying.v.extraKeys, - underlying.v.zeroValue, - underlying.v.minValue, - from + 1, - underlying.v.defaultEntry - ) == newMap.map - ) - decreases(from) - val currentKey = underlying.v._keys(from) - // println(f"RepackFrom: from = $from, key = $currentKey") - - val currentValue = underlying.v._values(from).get(underlying.v.defaultEntry(0L)) - - @ghost val newMapListMapBefore = newMap.map - - if (currentKey != 0 && currentKey != Long.MinValue) { - - // There is a key in the array, add it to the new map - val res = newMap.update(currentKey, currentValue) - - ghostExpr(if (newMapListMapBefore.contains(currentKey)) { - LongMapFixedSize.lemmaListMapContainsThenArrayContainsFrom( - underlying.v._keys, - underlying.v._values, - underlying.v.mask, - underlying.v.extraKeys, - underlying.v.zeroValue, - underlying.v.minValue, - currentKey, - from + 1, - underlying.v.defaultEntry - ) - LongMapFixedSize.lemmaNoDuplicateFromThenFromBigger(underlying.v._keys, 0, from) - LongMapFixedSize.lemmaArrayNoDuplicateFromNotContainsKeysInAcc(underlying.v._keys, from + 1, currentKey, List(currentKey)) - check(false) - } else { () }) - - if (res) { - if (from > 0) { - - // val underlyingMapFromPOneNXtra = LongMapFixedSize.getCurrentListMapNoExtraKeys(underlying.v._keys,underlying.v._values,underlying.v.mask,underlying.v.extraKeys,underlying.v.zeroValue,underlying.v.minValue,from + 1,underlying.v.defaultEntry) - ghostExpr( - ListLongMapLemmas.addCommutativeForDiffKeys( - LongMapFixedSize.getCurrentListMapNoExtraKeys( - underlying.v._keys, - underlying.v._values, - underlying.v.mask, - underlying.v.extraKeys, - underlying.v.zeroValue, - underlying.v.minValue, - from + 1, - underlying.v.defaultEntry - ), - currentKey, - currentValue, - 0L, - underlying.v.zeroValue - ) - ) - ghostExpr( - ListLongMapLemmas.addCommutativeForDiffKeys( - LongMapFixedSize.getCurrentListMapNoExtraKeys( - underlying.v._keys, - underlying.v._values, - underlying.v.mask, - underlying.v.extraKeys, - underlying.v.zeroValue, - underlying.v.minValue, - from + 1, - underlying.v.defaultEntry - ) + (0L, underlying.v.zeroValue), - currentKey, - currentValue, - Long.MinValue, - underlying.v.minValue - ) - ) - assert( - LongMapFixedSize.getCurrentListMap( - underlying.v._keys, - underlying.v._values, - underlying.v.mask, - underlying.v.extraKeys, - underlying.v.zeroValue, - underlying.v.minValue, - from, - underlying.v.defaultEntry - ) == newMap.map - ) - repackFrom(newMap, from - 1) - } else { - - ghostExpr( - ListLongMapLemmas.addCommutativeForDiffKeys( - LongMapFixedSize.getCurrentListMapNoExtraKeys( - underlying.v._keys, - underlying.v._values, - underlying.v.mask, - underlying.v.extraKeys, - underlying.v.zeroValue, - underlying.v.minValue, - from + 1, - underlying.v.defaultEntry - ), - currentKey, - currentValue, - 0L, - underlying.v.zeroValue - ) - ) - ghostExpr( - ListLongMapLemmas.addCommutativeForDiffKeys( - LongMapFixedSize.getCurrentListMapNoExtraKeys( - underlying.v._keys, - underlying.v._values, - underlying.v.mask, - underlying.v.extraKeys, - underlying.v.zeroValue, - underlying.v.minValue, - from + 1, - underlying.v.defaultEntry - ) + (0L, underlying.v.zeroValue), - currentKey, - currentValue, - Long.MinValue, - underlying.v.minValue - ) - ) - true - } - } else { - false - } - - } else { - if (from > 0) { - repackFrom(newMap, from - 1) - } else { - true - } - } - - } ensuring (res => if (res) newMap.valid && newMap.map == underlying.v.map else true) - - @ghost - def valid: Boolean = underlying.v.valid - - @pure - @ghost - def map: ListLongMap[V] = { - require(valid) - underlying.v.map - } - - } - - sealed trait ValueCell[V] { - def get(defaultValue: V): V - } - - case class ValueCellFull[V](v: V) extends ValueCell[V] { - def get(defaultValue: V): V = v - } - - case class EmptyCell[V]() extends ValueCell[V] { - def get(defaultValue: V): V = defaultValue - } - - def isFull[V](c: ValueCell[V]): Boolean = { - c match { - case ValueCellFull(_) => true - case EmptyCell() => false - } - } - - def extractCell[V](c: ValueCell[V]): V = { - require(isFull(c)) - c match { - case ValueCellFull(v) => v - } - } - - private final val MAX_MASK: Int = 0x3fffffff - - private final val MAX_ITER = Int.MaxValue - 1 // arbitrary - - /** A Map with keys of type Long and values of type Long mask must be a valid mask, i.e., 2^n - 1. The smallest possible mask is 0 and the biggest is 0x3fffffff _keys and _values must be initialized - * to an array of length mask + 1, containing all 0 values, i.e., Array.fill(mask + 1)(0) extraKeys must be initialized to 0 _size must be initialized to 0 - * - * @param mask - * @param extraKeys - * @param zeroValue - * @param minValue - * @param _size - * @param _keys - * @param _values - */ - @mutable - final case class LongMapFixedSize[V]( - val defaultEntry: Long => V, - val mask: Int, - var extraKeys: Int, - var zeroValue: V, - var minValue: V, - var _size: Int, - val _keys: Array[Long], - val _values: Array[ValueCell[V]], - var _vacant: Int - ) { - import LongMapFixedSize.validKeyInArray - import LongMapFixedSize.arrayCountValidKeys - import LongMapFixedSize.arrayContainsKey - import LongMapFixedSize.arrayScanForKey - import LongMapFixedSize.arrayNoDuplicates - import LongMapFixedSize.seekEntryOrOpen - import LongMapFixedSize.toIndex - import LongMapFixedSize.lemmaArrayContainsFromImpliesContainsFromZero - import LongMapFixedSize.arrayForallSeekEntryOrOpenFound - import LongMapFixedSize.lemmaValidKeyAtIImpliesCountKeysIsOne - import LongMapFixedSize.lemmaAddValidKeyIncreasesNumberOfValidKeysInArray - import LongMapFixedSize.lemmaRemoveValidKeyDecreasesNumberOfValidKeysInArray - import LongMapFixedSize.lemmaArrayForallSeekEntryOrOpenFoundFromSmallerThenFromBigger - import LongMapFixedSize.lemmaArrayNoDuplicateThenKeysContainedNotEqual - import LongMapFixedSize.lemmaNoDuplicateFromThenFromBigger - import LongMapFixedSize.lemmaPutNonValidKeyPreservesNoDuplicate - import LongMapFixedSize.lemmaPutNewValidKeyPreservesNoDuplicate - import LongMapFixedSize.seekEntry - import LongMapFixedSize.inRange - import LongMapFixedSize.validMask - import LongMapFixedSize.lemmaPutLongMinValuePreservesForallSeekEntryOrOpen - import LongMapFixedSize.lemmaSeekEntryOrOpenFindsThenSeekEntryFinds - import LongMapFixedSize.lemmaPutValidKeyPreservesForallSeekEntryOrOpen - import LongMapFixedSize.lemmaArrayNoDuplicateRemoveOneThenNotContain - import LongMapFixedSize.getCurrentListMap - import LongMapFixedSize.lemmaKeyInListMapIsInArray - import LongMapFixedSize.lemmaValidKeyInArrayIsInListMap - import LongMapFixedSize.lemmaKeyInListMapThenSameValueInArray - import LongMapFixedSize.lemmaArrayContainsKeyThenInListMap - import LongMapFixedSize.lemmaSeekEntryGivesInRangeIndex - import LongMapFixedSize.lemmaInListMapThenSeekEntryOrOpenFindsIt - import LongMapFixedSize.lemmaNotInListMapThenSeekEntryOrOpenFindsFreeOrNothing - import LongMapFixedSize.lemmaAddValidKeyToArrayThenAddPairToListMap - import LongMapFixedSize.lemmaChangeZeroKeyThenAddPairToListMap - import LongMapFixedSize.lemmaChangeLongMinValueKeyThenAddPairToListMap - import LongMapFixedSize.lemmaChangeValueExistingKeyToArrayThenAddPairToListMap - import LongMapFixedSize.lemmaRemoveZeroKeyThenRemoveKeyFromListMap - import LongMapFixedSize.lemmaRemoveLongMinValueKeyThenRemoveKeyFromListMap - import LongMapFixedSize.lemmaRemoveValidKeyToArrayThenRemoveKeyFromListMap - - @pure - def size: Int = { - _size + (extraKeys + 1) / 2 - } - - @pure - def imbalanced: Boolean = - 2 * _size > mask - - @pure - def isEmpty: Boolean = { - require(valid) - size == 0 - } ensuring (_ => valid) - - @pure - def contains(key: Long): Boolean = { - require(valid) - if (key == 0) (extraKeys & 1) != 0 - else if (key == Long.MinValue) (extraKeys & 2) != 0 - else { - val seekEntryRes = seekEntry(key)(_keys, mask) - seekEntryRes match { - case Found(index) => { - ghostExpr(lemmaArrayContainsFromImpliesContainsFromZero(_keys, key, index)) - ghostExpr( - lemmaValidKeyInArrayIsInListMap( - _keys, - _values, - mask, - extraKeys, - zeroValue, - minValue, - index, - defaultEntry - ) - ) - true - } - case _ => { - ghostExpr( - if ( - getCurrentListMap( - _keys, - _values, - mask, - extraKeys, - zeroValue, - minValue, - 0, - defaultEntry - ).contains( - key - ) - ) { - lemmaKeyInListMapIsInArray( - _keys, - _values, - mask, - extraKeys, - zeroValue, - minValue, - key, - defaultEntry - ) - val i = arrayScanForKey(_keys, key, 0) - lemmaArrayForallSeekEntryOrOpenFoundFromSmallerThenFromBigger(_keys, mask, 0, i) - lemmaSeekEntryOrOpenFindsThenSeekEntryFinds(key, i, _keys, mask) - check(false) - } - ) - false - } - } - } - } ensuring (res => valid && (res == map.contains(key))) - - /** Retrieves the value associated with a key. If the key does not exist in the map, the `defaultEntry` for that key is returned instead. - * - * @param key - * @return - */ - @pure - def apply(key: Long): V = { - require(valid) - if (key == -key) { - if (key == 0 && (extraKeys & 1) != 0) zeroValue - else if (key == Long.MinValue && (extraKeys & 2) != 0) minValue - else defaultEntry(key) - } else { - val seekEntryRes = seekEntry(key)(_keys, mask) - ghostExpr(lemmaSeekEntryGivesInRangeIndex(_keys, _values, mask, extraKeys, zeroValue, minValue, key)) - seekEntryRes match { - case Found(index) => { - ghostExpr(lemmaArrayContainsFromImpliesContainsFromZero(_keys, key, index)) - ghostExpr( - lemmaValidKeyInArrayIsInListMap( - _keys, - _values, - mask, - extraKeys, - zeroValue, - minValue, - index, - defaultEntry - ) - ) - ghostExpr( - lemmaKeyInListMapThenSameValueInArray( - _keys, - _values, - mask, - extraKeys, - zeroValue, - minValue, - key, - index, - defaultEntry - ) - ) - _values(index).get(defaultEntry(0L)) - } - case _ => defaultEntry(key) - } - } - } ensuring (res => - valid - && (if (contains(key)) res == map.get(key).get - else res == defaultEntry(key)) - ) - - /** Updates the map to include a new key-value pair. Returns a boolean indicating if the update was successful. It is not successful if no free space is found (i.e., the map is full) - * - * This is the fastest way to add an entry to a `LongMap`. - */ - def update(key: Long, v: V): Boolean = { - require(valid) - if (key == -key) { - if (key == 0) { - ghostExpr( - lemmaChangeZeroKeyThenAddPairToListMap( - _keys, - _values, - mask, - extraKeys, - (extraKeys | 1), - zeroValue, - v, - minValue, - defaultEntry - ) - ) - zeroValue = v - extraKeys |= 1 - true - } else { - val extraKeysBefore = extraKeys - ghostExpr( - lemmaChangeLongMinValueKeyThenAddPairToListMap( - _keys, - _values, - mask, - extraKeys, - (extraKeys | 2), - zeroValue, - minValue, - v, - defaultEntry - ) - ) - minValue = v - extraKeys |= 2 - true - } - - } else { - val seekEntryRes = seekEntryOrOpen(key)(_keys, mask) - seekEntryRes match { - case Undefined() => { - // the key is not in the array, it was not able to find an empty space, the map is maybe full - ghostExpr( - if ( - getCurrentListMap( - _keys, - _values, - mask, - extraKeys, - zeroValue, - minValue, - 0, - defaultEntry - ).contains( - key - ) - ) { - lemmaInListMapThenSeekEntryOrOpenFindsIt( - _keys, - _values, - mask, - extraKeys, - zeroValue, - minValue, - key, - defaultEntry - ) - check(false) - } else { - lemmaNotInListMapThenSeekEntryOrOpenFindsFreeOrNothing( - _keys, - _values, - mask, - extraKeys, - zeroValue, - minValue, - key, - defaultEntry - ) - } - ) - false - } - case MissingVacant(index) => updateHelperNewKey(key, v, index) - case MissingZero(index) => updateHelperNewKey(key, v, index) - case Found(index) => { - ghostExpr( - if ( - getCurrentListMap( - _keys, - _values, - mask, - extraKeys, - zeroValue, - minValue, - 0, - defaultEntry - ).contains( - key - ) - ) { - lemmaInListMapThenSeekEntryOrOpenFindsIt( - _keys, - _values, - mask, - extraKeys, - zeroValue, - minValue, - key, - defaultEntry - ) - } else { - lemmaNotInListMapThenSeekEntryOrOpenFindsFreeOrNothing( - _keys, - _values, - mask, - extraKeys, - zeroValue, - minValue, - key, - defaultEntry - ) - check(false) - } - ) - - ghostExpr( - lemmaChangeValueExistingKeyToArrayThenAddPairToListMap( - _keys, - _values, - mask, - extraKeys, - zeroValue, - minValue, - index, - key, - v, - defaultEntry - ) - ) - - _values(index) = ValueCellFull(v) - - ghostExpr( - lemmaValidKeyInArrayIsInListMap( - _keys, - _values, - mask, - extraKeys, - zeroValue, - minValue, - index, - defaultEntry - ) - ) - true - } - } - } - } ensuring (res => valid && (if (res) map.contains(key) && (map == old(this).map + (key, v)) else map == old(this).map)) - - /** Removes the given key from the map. Returns true if the key was present and is removed, false if the key was not present - * - * @param key - * @return - */ - def remove(key: Long): Boolean = { - require(valid) - if (key == -key) { - if (key == 0L) { - ghostExpr( - lemmaRemoveZeroKeyThenRemoveKeyFromListMap( - _keys, - _values, - mask, - extraKeys, - extraKeys & 0x2, - zeroValue, - defaultEntry(0L), - minValue, - defaultEntry - ) - ) - extraKeys &= 0x2 - zeroValue = defaultEntry(0L) - - true - } else { - ghostExpr( - lemmaRemoveLongMinValueKeyThenRemoveKeyFromListMap( - _keys, - _values, - mask, - extraKeys, - extraKeys & 0x1, - zeroValue, - minValue, - defaultEntry(Long.MinValue), - defaultEntry - ) - ) - extraKeys &= 0x1 - minValue = defaultEntry(Long.MinValue) - - true - } - } else { - val seekEntryRes = seekEntry(key)(_keys, mask) - seekEntryRes match { - case Found(index) => { - // _vacant += 1 - ghostExpr(lemmaRemoveValidKeyDecreasesNumberOfValidKeysInArray(_keys, index, Long.MinValue)) - ghostExpr(lemmaPutNonValidKeyPreservesNoDuplicate(_keys, Long.MinValue, index, 0, List())) - ghostExpr(lemmaPutLongMinValuePreservesForallSeekEntryOrOpen(_keys, index)(mask)) - ghostExpr(lemmaArrayNoDuplicateRemoveOneThenNotContain(_keys, index, key)) - ghostExpr( - lemmaRemoveValidKeyToArrayThenRemoveKeyFromListMap( - _keys, - _values, - mask, - extraKeys, - zeroValue, - minValue, - index, - key, - defaultEntry - ) - ) - - _size -= 1 - _keys(index) = Long.MinValue - _values(index) = ValueCellFull(defaultEntry(0L)) - val tempVac = _vacant + 1 - if (tempVac > 0) { - _vacant = tempVac - } - - ghostExpr( - if ( - getCurrentListMap( - _keys, - _values, - mask, - extraKeys, - zeroValue, - minValue, - 0, - defaultEntry - ).contains( - key - ) - ) { - lemmaKeyInListMapIsInArray( - _keys, - _values, - mask, - extraKeys, - zeroValue, - minValue, - key, - defaultEntry - ) - check(false) - } else { () } - ) - - true - } - case _ => false - } - } - } ensuring (res => valid && (if (res) map == old(this).map - key else map == old(this).map)) - - /** Go through an helper function because this piece of code has to be called in 2 cases of the pattern matching - * - * @return - */ - private def updateHelperNewKey(key: Long, v: V, index: Int): Boolean = { - require(valid) - require(key != 0) - require(key != Long.MinValue) - require( - seekEntryOrOpen(key)(_keys, mask) == MissingZero( - index - ) || seekEntryOrOpen(key)( - _keys, - mask - ) == MissingVacant(index) - ) - - ghostExpr( - if ( - getCurrentListMap(_keys, _values, mask, extraKeys, zeroValue, minValue, 0, defaultEntry) - .contains(key) - ) { - ghostExpr( - lemmaInListMapThenSeekEntryOrOpenFindsIt( - _keys, - _values, - mask, - extraKeys, - zeroValue, - minValue, - key, - defaultEntry - ) - ) - check(false) - } else { - ghostExpr( - lemmaNotInListMapThenSeekEntryOrOpenFindsFreeOrNothing( - _keys, - _values, - mask, - extraKeys, - zeroValue, - minValue, - key, - defaultEntry - ) - ) - } - ) - assert(inRange(index, mask)) - ghostExpr(if (arrayContainsKey(_keys, key, 0)) { - - lemmaArrayContainsKeyThenInListMap( - _keys, - _values, - mask, - extraKeys, - zeroValue, - minValue, - key, - 0, - defaultEntry - ) - - check(false) - } else {}) - - ghostExpr(lemmaPutNewValidKeyPreservesNoDuplicate(_keys, key, index, 0, List())) - ghostExpr(lemmaAddValidKeyIncreasesNumberOfValidKeysInArray(_keys, index, key)) - ghostExpr(lemmaPutValidKeyPreservesForallSeekEntryOrOpen(key, _keys, index)(mask)) - - ghostExpr( - lemmaAddValidKeyToArrayThenAddPairToListMap( - _keys, - _values, - mask, - extraKeys, - zeroValue, - minValue, - index, - key, - v, - defaultEntry - ) - ) - - _keys(index) = key - _size += 1 - - ghostExpr(lemmaArrayContainsFromImpliesContainsFromZero(_keys, key, index)) - ghostExpr(lemmaValidKeyAtIImpliesCountKeysIsOne(_keys, index)) - - _values(index) = ValueCellFull(v) - - ghostExpr( - lemmaValidKeyInArrayIsInListMap( - _keys, - _values, - mask, - extraKeys, - zeroValue, - minValue, - index, - defaultEntry - ) - ) - true - - } ensuring (res => res && valid && map.contains(key) && (map == old(this).map + (key, v))) - - @ghost - def valid: Boolean = { - // class invariant - simpleValid && - arrayCountValidKeys(_keys, 0, _keys.length) == _size && - arrayForallSeekEntryOrOpenFound(0)(_keys, mask) && - arrayNoDuplicates(_keys, 0) - } - - @ghost - def simpleValid: Boolean = { - validMask(mask) && - _values.length == mask + 1 && - _keys.length == _values.length && - _size >= 0 && - _size <= mask + 1 && - size >= _size && - size == _size + (extraKeys + 1) / 2 && - extraKeys >= 0 && - extraKeys <= 3 && - _vacant >= 0 - } - - @pure - @ghost - def map: ListLongMap[V] = { - require(valid) - getCurrentListMap(_keys, _values, mask, extraKeys, zeroValue, minValue, 0, defaultEntry) - } - - } - - abstract sealed class SeekEntryResult - case class Found(index: Int) extends SeekEntryResult - case class MissingZero(index: Int) extends SeekEntryResult - case class MissingVacant(index: Int) extends SeekEntryResult - case class Intermediate(undefined: Boolean, index: Int, x: Int) extends SeekEntryResult - case class Undefined() extends SeekEntryResult - - object LongMapFixedSize { - - def getNewLongMapFixedSize[V](mask: Int, defaultEntry: Long => V): LongMapFixedSize[V] = { - require(validMask(mask)) - - val res = LongMapFixedSize[V]( - defaultEntry = defaultEntry, - mask = mask, - extraKeys = 0, - zeroValue = defaultEntry(0L), - minValue = defaultEntry(0L), - _size = 0, - _keys = Array.fill(mask + 1)(0L), - _values = Array.fill(mask + 1)(EmptyCell[V]()), - _vacant = 0 - ) - ghostExpr(LongMapFixedSize.lemmaArrayCountValidKeysOfFilled0ArrayIs0(res._keys, 0, mask + 1)) - - ghostExpr(LongMapFixedSize.lemmaArrayForallSeekEntryOrOpenFoundAlwaysTrueFor0Array(res._keys, mask, 0)) - - ghostExpr(LongMapFixedSize.lemmaArrayNoDuplicatesInAll0Array(res._keys, 0, mask + 1)) - ghostExpr(if (res.map != ListLongMap.empty[V]) { - assert(!res.map.toList.isEmpty) - val kv = res.map.toList.head - val k = kv._1 - LongMapFixedSize.lemmaKeyInListMapIsInArray(res._keys, res._values, mask, res.extraKeys, res.zeroValue, res.minValue, k, res.defaultEntry) - val index = arrayScanForKey(res._keys, k, 0) - check(false) - - } else { () }) - res - } ensuring (res => res.valid && res.mask == mask && res.map == ListLongMap.empty[V]) - - @pure - def validMask(mask: Int): Boolean = { - (mask == 0x00000007 || - mask == 0x0000000f || - mask == 0x0000001f || - mask == 0x0000003f || - mask == 0x0000007f || - mask == 0x000000ff || - mask == 0x000001ff || - mask == 0x000003ff || - mask == 0x000007ff || - mask == 0x00000fff || - mask == 0x00001fff || - mask == 0x00003fff || - mask == 0x00007fff || - mask == 0x0000ffff || - mask == 0x0001ffff || - mask == 0x0003ffff || - mask == 0x0007ffff || - mask == 0x000fffff || - mask == 0x001fffff || - mask == 0x003fffff || - mask == 0x007fffff || - mask == 0x00ffffff || - mask == 0x01ffffff || - mask == 0x03ffffff || - mask == 0x07ffffff || - mask == 0x0fffffff || - mask == 0x1fffffff || - mask == 0x3fffffff) && mask <= MAX_MASK // MAX is MAX_MASK - - } - - /** Checks if i is a valid index in the Array of values - * - * @param i - * @return - */ - private def inRange(i: Int, mask: Int): Boolean = { - // mask + 1 is the size of the Array - i >= 0 && i < mask + 1 - } - - @pure - @ghost - def arrayForallSeekEntryOrOpenFound(i: Int)(implicit _keys: Array[Long], mask: Int): Boolean = { - require(validMask(mask)) - require(_keys.length == mask + 1) - require(i >= 0) - require(i <= _keys.length) - - decreases(_keys.length - i) - - if (i >= _keys.length) true - else if (validKeyInArray(_keys(i))) { - lemmaArrayContainsFromImpliesContainsFromZero(_keys, _keys(i), i) - LongMapFixedSize.seekEntryOrOpen(_keys(i))(_keys, mask) == Found(i) && - arrayForallSeekEntryOrOpenFound(i + 1) - } else arrayForallSeekEntryOrOpenFound(i + 1) - } - - /** Compute the index in the array for a given key with hashing and magic stuff - * - * @param k - * the key - * @return - */ - @pure - private def toIndex(k: Long, mask: Int): Int = { - require(mask >= 0) - require(mask <= MAX_MASK) - // Part of the MurmurHash3 32 bit finalizer - val h = ((k ^ (k >>> 32)) & 0xffffffffL).toInt - val x = (h ^ (h >>> 16)) * 0x85ebca6b - (x ^ (x >>> 13)) & mask - } ensuring (res => res < mask + 1 && res >= 0) - - /** Given a key, seek for its index into the array returns a corresponding instance of SeekEntryResult with the index if found - * - * @param k - * the key - * @return - * the index of the given key into the array - */ - @pure - def seekEntry(k: Long)(implicit _keys: Array[Long], mask: Int): SeekEntryResult = { - require(validMask(mask)) - require(_keys.length == mask + 1) - require(validKeyInArray(k)) - decreases(1) - - val intermediate = - seekKeyOrZeroOrLongMinValue(0, toIndex(k, mask))(k, _keys, mask) - intermediate match { - case Intermediate(undefined, index, x) if (undefined) => Undefined() - case Intermediate(undefined, index, x) if (!undefined) => { - val q = _keys(index) - if (q == k) Found(index) - else if (q == 0) MissingZero(index) - else { - // e is the index of Long.MinValue i.e. the spot of a key that was removed - // we need to search from there until we see a zero. Maybe the key we're - // searching was added after the removed one and is therefore after in the array. - // If we find a zero before finding the key, we return the index of the Long.MinValue to - // reuse the spot - assert(_keys(index) == Long.MinValue) - assert(index >= 0 && index < mask + 1) - val res = seekKeyOrZeroReturnVacant(x, index, index)(k, _keys, mask) - res match { - case MissingVacant(index) => MissingZero(index) - case _ => res - } - } - } - } - - } ensuring (res => - res match { - case MissingVacant(index) => false // should never happen - case Found(index) => _keys(index) == k - case MissingZero(_) => true - case Undefined() => true - } - ) - - /** Search the index of the given key. If the key is in the array, it finds its index (OK is returned). If the key is not in the array, it finds either: - * - A free space with a 0 value (MissingBit is returned) - * - A freed space with a Long.MinValue value (MissingVacant is returned) - * - Nothing (EntryNotFound is returned as a second value) - * - * @param k - * @return - */ - @pure - def seekEntryOrOpen(k: Long)(implicit _keys: Array[Long], mask: Int): SeekEntryResult = { - require(validMask(mask)) - require(mask >= 0) - require(_keys.length == mask + 1) - - require(validKeyInArray(k)) - - val intermediate = - seekKeyOrZeroOrLongMinValue(0, toIndex(k, mask))(k, _keys, mask) - intermediate match { - case Intermediate(undefined, index, x) if (undefined) => Undefined() - case Intermediate(undefined, index, x) if (!undefined) => { - val q = _keys(index) - if (q == k) Found(index) - else if (q == 0) MissingZero(index) - else { - // index is the index of Long.MinValue i.e. the spot of a key that was removed - // we need to search from there until we see a zero. Maybe the key we're - // searching was added after the removed one and is therefore after in the array. - // If we find a zero before finding the key, we return the index of the Long.MinValue to - // reuse the spot - assert(_keys(index) == Long.MinValue) - assert(index >= 0 && index < mask + 1) - val res = seekKeyOrZeroReturnVacant(x, index, index)(k, _keys, mask) - res - } - } - } - } ensuring (res => - ( - res match { - case Undefined() => true - case Found(index) => _keys(index) == k - case MissingZero(index) => _keys(index) == 0 - case MissingVacant(index) => _keys(index) == Long.MinValue - case _ => false - } - ) - ) - - @opaque - @pure - private def nextIndex(ee: Int, x: Int, mask: Int): Int = { - require(validMask(mask)) - require(mask >= 0) - require(ee >= 0 && ee < mask + 1) - require(x <= MAX_ITER && x >= 0) - - (ee + 2 * (x + 1) * x - 3) & mask - } ensuring (res => res >= 0 && res < mask + 1) - - // @tailrec - @pure - private def seekKeyOrZeroOrLongMinValue(x: Int, ee: Int)(implicit - k: Long, - _keys: Array[Long], - mask: Int - ): SeekEntryResult = { - require(validMask(mask)) - require(mask >= 0) - require(_keys.length == mask + 1) - - require(ee >= 0 && ee < mask + 1) - require(x <= MAX_ITER && x >= 0) - require(validKeyInArray(k)) - - decreases(MAX_ITER - x) - val q = _keys(ee) - if (x >= MAX_ITER) Intermediate(true, ee, x) - else if (q == k || q + q == 0) Intermediate(false, ee, x) - else - seekKeyOrZeroOrLongMinValue(x + 1, nextIndex(ee, x + 1, mask)) - } ensuring (res => - (res match { - case Intermediate(undefined, index, resx) if (undefined) => resx >= MAX_ITER - case Intermediate(undefined, index, resx) if (!undefined) => - resx < MAX_ITER && resx >= 0 && resx >= x && (_keys(index) == k || _keys( - index - ) == 0 || _keys( - index - ) == Long.MinValue) - case _ => false - }) - ) - - // @tailrec - @pure - private def seekKeyOrZeroReturnVacant(x: Int, ee: Int, vacantSpotIndex: Int)(implicit - k: Long, - _keys: Array[Long], - mask: Int - ): SeekEntryResult = { - require(validMask(mask)) - require(mask >= 0) - require(_keys.length == mask + 1) - - require(ee >= 0 && ee < mask + 1) - require(x <= MAX_ITER && x >= 0) - require(vacantSpotIndex >= 0 && vacantSpotIndex < mask + 1) - require(_keys(vacantSpotIndex) == Long.MinValue) - require(validKeyInArray(k)) - - decreases(MAX_ITER + 1 - x) - val q = _keys(ee) - if (x >= MAX_ITER) Undefined() - else if (q == k) Found(ee) - else if (q == 0) MissingVacant(vacantSpotIndex) - else - seekKeyOrZeroReturnVacant( - x + 1, - nextIndex(ee, x + 1, mask), - vacantSpotIndex - ) - - } ensuring (res => - res match { - case Undefined() => true - case Found(index) => _keys(index) == k - case MissingVacant(index) => index == vacantSpotIndex && _keys(index) == Long.MinValue - case _ => false - } - ) - - @pure - @ghost - def getCurrentListMap[V]( - _keys: Array[Long], - _values: Array[ValueCell[V]], - mask: Int, - extraKeys: Int, - zeroValue: V, - minValue: V, - from: Int, - defaultEntry: Long => V - ): ListLongMap[V] = { - require(validMask(mask)) - require(_values.length == mask + 1) - require(_keys.length == _values.length) - require(mask >= 0) - require(extraKeys >= 0) - require(extraKeys <= 3) - require(arrayForallSeekEntryOrOpenFound(0)(_keys, mask)) - require(arrayNoDuplicates(_keys, 0)) - - require(from >= 0 && from <= _keys.length) - - val res = if ((extraKeys & 1) != 0 && (extraKeys & 2) != 0) { - // it means there is a mapping for the key 0 and the Long.MIN_VALUE - (getCurrentListMapNoExtraKeys(_keys, _values, mask, extraKeys, zeroValue, minValue, from, defaultEntry) + (0L, zeroValue)) + (Long.MinValue, minValue) - } else if ((extraKeys & 1) != 0 && (extraKeys & 2) == 0) { - // it means there is a mapping for the key 0 - getCurrentListMapNoExtraKeys(_keys, _values, mask, extraKeys, zeroValue, minValue, from, defaultEntry) + (0L, zeroValue) - } else if ((extraKeys & 2) != 0 && (extraKeys & 1) == 0) { - // it means there is a mapping for the key Long.MIN_VALUE - getCurrentListMapNoExtraKeys(_keys, _values, mask, extraKeys, zeroValue, minValue, from, defaultEntry) + (Long.MinValue, minValue) - } else { - getCurrentListMapNoExtraKeys(_keys, _values, mask, extraKeys, zeroValue, minValue, from, defaultEntry) - } - if (from < _keys.length && validKeyInArray(_keys(from))) { - ListLongMapLemmas.addStillContains(getCurrentListMapNoExtraKeys(_keys, _values, mask, extraKeys, zeroValue, minValue, from, defaultEntry), 0, zeroValue, _keys(from)) - ListLongMapLemmas.addApplyDifferent(getCurrentListMapNoExtraKeys(_keys, _values, mask, extraKeys, zeroValue, minValue, from, defaultEntry), Long.MinValue, minValue, _keys(from)) - ListLongMapLemmas.addApplyDifferent(getCurrentListMapNoExtraKeys(_keys, _values, mask, extraKeys, zeroValue, minValue, from, defaultEntry), 0, zeroValue, _keys(from)) - ListLongMapLemmas.addApplyDifferent(getCurrentListMapNoExtraKeys(_keys, _values, mask, extraKeys, zeroValue, minValue, from, defaultEntry), Long.MinValue, minValue, _keys(from)) - } - - res - - } ensuring (res => - (if (from < _keys.length && validKeyInArray(_keys(from))) - res.contains(_keys(from)) && res(_keys(from)) == _values(from).get(defaultEntry(0L)) - else - // else if (from < _keys.length) res == getCurrentListMap(from + 1) else - true) && - (if ((extraKeys & 1) != 0) res.contains(0) && res(0) == zeroValue else !res.contains(0)) && - (if ((extraKeys & 2) != 0) res.contains(Long.MinValue) && res(Long.MinValue) == minValue - else !res.contains(Long.MinValue)) - ) - - @ghost - @pure - def getCurrentListMapNoExtraKeys[V]( - _keys: Array[Long], - _values: Array[ValueCell[V]], - mask: Int, - extraKeys: Int, - zeroValue: V, - minValue: V, - from: Int, - defaultEntry: Long => V - ): ListLongMap[V] = { - require(validMask(mask)) - require(_values.length == mask + 1) - require(_keys.length == _values.length) - require(mask >= 0) - - require(extraKeys >= 0) - require(extraKeys <= 3) - require(arrayForallSeekEntryOrOpenFound(0)(_keys, mask)) - require(arrayNoDuplicates(_keys, 0)) - - require(from >= 0 && from <= _keys.length) - decreases(_keys.length + 1 - from) - if (from >= _keys.length) { - ListLongMap.empty[V] - } else if (validKeyInArray(_keys(from))) { - ListLongMapLemmas.addStillNotContains( - getCurrentListMapNoExtraKeys(_keys, _values, mask, extraKeys, zeroValue, minValue, from + 1, defaultEntry), - _keys(from), - _values(from).get(defaultEntry(0L)), - 0 - ) - - getCurrentListMapNoExtraKeys(_keys, _values, mask, extraKeys, zeroValue, minValue, from + 1, defaultEntry) + (_keys(from), _values(from).get(defaultEntry(0L))) - } else { - getCurrentListMapNoExtraKeys(_keys, _values, mask, extraKeys, zeroValue, minValue, from + 1, defaultEntry) - } - } ensuring (res => - !res.contains(0) && !res.contains(Long.MinValue) && - (if (from < _keys.length && validKeyInArray(_keys(from))) - res.contains(_keys(from)) && res(_keys(from)) == _values(from).get(defaultEntry(0L)) - else if (from < _keys.length) - res == getCurrentListMapNoExtraKeys(_keys, _values, mask, extraKeys, zeroValue, minValue, from + 1, defaultEntry) - else res.isEmpty) - ) - - // LEMMAS -----------------–-----------------–-----------------–-----------------–-----------------–--------------- - - @opaque - @inlineOnce - @pure - @ghost - def lemmaAddValidKeyToArrayThenAddPairToListMap[V]( - _keys: Array[Long], - _values: Array[ValueCell[V]], - mask: Int, - extraKeys: Int, - zeroValue: V, - minValue: V, - i: Int, - k: Long, - v: V, - defaultEntry: Long => V - ): Unit = { - require(validMask(mask)) - require(_values.length == mask + 1) - require(_keys.length == _values.length) - require(mask >= 0) - require(extraKeys >= 0) - require(extraKeys <= 3) - require(arrayForallSeekEntryOrOpenFound(0)(_keys, mask)) - require(arrayNoDuplicates(_keys, 0)) - - require(i >= 0 && i < _keys.length) - require(validKeyInArray(k)) - require(_keys(i) == Long.MinValue || _keys(i) == 0) - require(!arrayContainsKey(_keys, k, 0)) - require(arrayForallSeekEntryOrOpenFound(0)(_keys.updated(i, k), mask)) - require(arrayNoDuplicates(_keys.updated(i, k), 0)) - - val mapNoExtraKeysBefore = - getCurrentListMapNoExtraKeys( - _keys, - _values, - mask, - extraKeys, - zeroValue, - minValue, - 0, - defaultEntry - ) - val mapNoExtraKeysAfter = getCurrentListMapNoExtraKeys( - _keys.updated(i, k), - _values.updated(i, ValueCellFull(v)), - mask, - extraKeys, - zeroValue, - minValue, - 0, - defaultEntry - ) - - lemmaAddValidKeyToArrayThenMapNoExtrasAddPair( - _keys, - _values, - mask, - extraKeys, - zeroValue, - minValue, - i, - k, - v, - 0, - defaultEntry - ) - - check(mapNoExtraKeysBefore + (k, v) == mapNoExtraKeysAfter) - - val mapAfter = - getCurrentListMap( - _keys.updated(i, k), - _values.updated(i, ValueCellFull(v)), - mask, - extraKeys, - zeroValue, - minValue, - 0, - defaultEntry - ) - val mapBefore = - getCurrentListMap(_keys, _values, mask, extraKeys, zeroValue, minValue, 0, defaultEntry) - if ((extraKeys & 1) != 0 && (extraKeys & 2) != 0) { - // it means there is a mapping for the key 0 and the Long.MIN_VALUE - check(mapAfter == (mapNoExtraKeysAfter + (0L, zeroValue)) + (Long.MinValue, minValue)) - check(mapBefore == (mapNoExtraKeysBefore + (0L, zeroValue)) + (Long.MinValue, minValue)) - check( - mapAfter == ((mapNoExtraKeysBefore + (k, v)) + (0L, zeroValue)) + (Long.MinValue, minValue) - ) - ListLongMapLemmas.addCommutativeForDiffKeys(mapNoExtraKeysBefore, k, v, 0L, zeroValue) - ListLongMapLemmas.addCommutativeForDiffKeys( - mapNoExtraKeysBefore + (0L, zeroValue), - k, - v, - Long.MinValue, - minValue - ) - } else if ((extraKeys & 1) != 0 && (extraKeys & 2) == 0) { - // it means there is a mapping for the key 0 - check(mapAfter == mapNoExtraKeysAfter + (0L, zeroValue)) - check(mapBefore == (mapNoExtraKeysBefore + (0L, zeroValue))) - check(mapAfter == (mapNoExtraKeysBefore + (k, v)) + (0L, zeroValue)) - ListLongMapLemmas.addCommutativeForDiffKeys(mapNoExtraKeysBefore, k, v, 0L, zeroValue) - } else if ((extraKeys & 2) != 0 && (extraKeys & 1) == 0) { - // it means there is a mapping for the key Long.MIN_VALUE - check(mapAfter == mapNoExtraKeysAfter + (Long.MinValue, minValue)) - check(mapAfter == (mapNoExtraKeysBefore + (k, v)) + (Long.MinValue, minValue)) - check(mapBefore == (mapNoExtraKeysBefore + (Long.MinValue, minValue))) - ListLongMapLemmas.addCommutativeForDiffKeys( - mapNoExtraKeysBefore, - k, - v, - Long.MinValue, - minValue - ) - } else { - check(mapAfter == mapNoExtraKeysAfter) - check( - getCurrentListMap( - _keys, - _values, - mask, - extraKeys, - zeroValue, - minValue, - 0, - defaultEntry - ) + (k, v) == getCurrentListMap( - _keys.updated(i, k), - _values.updated(i, ValueCellFull(v)), - mask, - extraKeys, - zeroValue, - minValue, - 0, - defaultEntry - ) - ) - } - - } ensuring (_ => - getCurrentListMap( - _keys, - _values, - mask, - extraKeys, - zeroValue, - minValue, - 0, - defaultEntry - ) + (k, v) == getCurrentListMap( - _keys.updated(i, k), - _values.updated(i, ValueCellFull(v)), - mask, - extraKeys, - zeroValue, - minValue, - 0, - defaultEntry - ) - ) - - @opaque - @inlineOnce - @pure - @ghost - def lemmaAddValidKeyToArrayThenMapNoExtrasAddPair[V]( - _keys: Array[Long], - _values: Array[ValueCell[V]], - mask: Int, - extraKeys: Int, - zeroValue: V, - minValue: V, - i: Int, - k: Long, - v: V, - from: Int, - defaultEntry: Long => V - ): Unit = { - require(from >= 0 && from <= _keys.length) - require(validMask(mask)) - require(_values.length == mask + 1) - require(_keys.length == _values.length) - require(mask >= 0) - require(extraKeys >= 0) - require(extraKeys <= 3) - require(arrayForallSeekEntryOrOpenFound(0)(_keys, mask)) - require(arrayNoDuplicates(_keys, 0)) - - require(i >= 0 && i < _keys.length) - require(validKeyInArray(k)) - require(_keys(i) == Long.MinValue || _keys(i) == 0) - require(!arrayContainsKey(_keys, k, 0)) - require(arrayForallSeekEntryOrOpenFound(0)(_keys.updated(i, k), mask)) - require(arrayNoDuplicates(_keys.updated(i, k), 0)) - - require(i >= 0 && i < _keys.length) - require(validKeyInArray(k)) - require(_keys(i) == Long.MinValue || _keys(i) == 0) - - decreases(_keys.length - from) - - if (from > i) { - if (from + 1 <= _keys.length) { - lemmaAddValidKeyToArrayThenMapNoExtrasAddPair( - _keys, - _values, - mask, - extraKeys, - zeroValue, - minValue, - i, - k, - v, - from + 1, - defaultEntry - ) - } - - } else { - assert(from <= i) - val listmapNoExtrasBefore = - getCurrentListMapNoExtraKeys( - _keys, - _values, - mask, - extraKeys, - zeroValue, - minValue, - from, - defaultEntry - ) - val listmapNoExtrasAfter = getCurrentListMapNoExtraKeys( - _keys.updated(i, k), - _values.updated(i, ValueCellFull(v)), - mask, - extraKeys, - zeroValue, - minValue, - from, - defaultEntry - ) - if (from == i) { - lemmaAddValidKeyToArrayThenMapNoExtrasAddPair( - _keys, - _values, - mask, - extraKeys, - zeroValue, - minValue, - i, - k, - v, - from + 1, - defaultEntry - ) - assert(!validKeyInArray(_keys(from))) - check( - listmapNoExtrasAfter == - getCurrentListMapNoExtraKeys( - _keys.updated(i, k), - _values.updated(i, ValueCellFull(v)), - mask, - extraKeys, - zeroValue, - minValue, - from + 1, - defaultEntry - ) + (_keys.updated(i, k).apply(from), _values - .updated(i, ValueCellFull(v)) - .apply(from) - .get(defaultEntry(0L))) - ) - - ListLongMapLemmas.addSameAsAddTwiceSameKeyDiffValues( - getCurrentListMapNoExtraKeys( - _keys.updated(i, k), - _values.updated(i, ValueCellFull(v)), - mask, - extraKeys, - zeroValue, - minValue, - from + 1, - defaultEntry - ), - k, - _values(from).get(defaultEntry(0L)), - _values.updated(i, ValueCellFull(v)).apply(from).get(defaultEntry(0L)) - ) - - } else { - assert(from < i) - lemmaAddValidKeyToArrayThenMapNoExtrasAddPair( - _keys, - _values, - mask, - extraKeys, - zeroValue, - minValue, - i, - k, - v, - from + 1, - defaultEntry - ) - if (validKeyInArray(_keys(from))) { - check( - getCurrentListMapNoExtraKeys( - _keys.updated(i, k), - _values.updated(i, ValueCellFull(v)), - mask, - extraKeys, - zeroValue, - minValue, - from, - defaultEntry - ) == - ((getCurrentListMapNoExtraKeys( - _keys, - _values, - mask, - extraKeys, - zeroValue, - minValue, - from + 1, - defaultEntry - ) + (k, v) + (_keys(from), _values(from).get(defaultEntry(0L))))) - ) - - if (_keys(from) == k) { - lemmaArrayContainsFromImpliesContainsFromSmaller(_keys, k, from, 0) - check(false) - } - - check(_keys(from) != k) - - ListLongMapLemmas.addCommutativeForDiffKeys( - getCurrentListMapNoExtraKeys( - _keys, - _values, - mask, - extraKeys, - zeroValue, - minValue, - from + 1, - defaultEntry - ), - k, - v, - _keys(from), - _values(from).get(defaultEntry(0L)) - ) - } - } - } - - } ensuring (_ => - if (from <= i) - getCurrentListMapNoExtraKeys( - _keys.updated(i, k), - _values.updated(i, ValueCellFull(v)), - mask, - extraKeys, - zeroValue, - minValue, - from, - defaultEntry - ) == - (getCurrentListMapNoExtraKeys( - _keys, - _values, - mask, - extraKeys, - zeroValue, - minValue, - from, - defaultEntry - ) + (k, v)) - else - getCurrentListMapNoExtraKeys( - _keys.updated(i, k), - _values.updated(i, ValueCellFull(v)), - mask, - extraKeys, - zeroValue, - minValue, - from, - defaultEntry - ) == - getCurrentListMapNoExtraKeys( - _keys, - _values, - mask, - extraKeys, - zeroValue, - minValue, - from, - defaultEntry - ) - ) - - @opaque - @inlineOnce - @pure - @ghost - def lemmaChangeValueExistingKeyToArrayThenAddPairToListMap[V]( - _keys: Array[Long], - _values: Array[ValueCell[V]], - mask: Int, - extraKeys: Int, - zeroValue: V, - minValue: V, - i: Int, - k: Long, - v: V, - defaultEntry: Long => V - ): Unit = { - require(validMask(mask)) - require(_values.length == mask + 1) - require(_keys.length == _values.length) - require(mask >= 0) - require(extraKeys >= 0) - require(extraKeys <= 3) - require(arrayForallSeekEntryOrOpenFound(0)(_keys, mask)) - require(arrayNoDuplicates(_keys, 0)) - - require(i >= 0 && i < _keys.length) - require(validKeyInArray(k)) - require(_keys(i) == k) - - val mapNoExtraKeysBefore = - getCurrentListMapNoExtraKeys( - _keys, - _values, - mask, - extraKeys, - zeroValue, - minValue, - 0, - defaultEntry - ) - val mapNoExtraKeysAfter = - getCurrentListMapNoExtraKeys( - _keys, - _values.updated(i, ValueCellFull(v)), - mask, - extraKeys, - zeroValue, - minValue, - 0, - defaultEntry - ) - - lemmaChangeValueExistingKeyToArrayThenMapNoExtrasAddPair( - _keys, - _values, - mask, - extraKeys, - zeroValue, - minValue, - i, - k, - v, - 0, - defaultEntry - ) - - check(mapNoExtraKeysBefore + (k, v) == mapNoExtraKeysAfter) - - val mapAfter = - getCurrentListMap( - _keys, - _values.updated(i, ValueCellFull(v)), - mask, - extraKeys, - zeroValue, - minValue, - 0, - defaultEntry - ) - val mapBefore = - getCurrentListMap(_keys, _values, mask, extraKeys, zeroValue, minValue, 0, defaultEntry) - if ((extraKeys & 1) != 0 && (extraKeys & 2) != 0) { - // it means there is a mapping for the key 0 and the Long.MIN_VALUE - check(mapAfter == (mapNoExtraKeysAfter + (0L, zeroValue)) + (Long.MinValue, minValue)) - check(mapBefore == (mapNoExtraKeysBefore + (0L, zeroValue)) + (Long.MinValue, minValue)) - check( - mapAfter == ((mapNoExtraKeysBefore + (k, v)) + (0L, zeroValue)) + (Long.MinValue, minValue) - ) - ListLongMapLemmas.addCommutativeForDiffKeys(mapNoExtraKeysBefore, k, v, 0L, zeroValue) - ListLongMapLemmas.addCommutativeForDiffKeys( - mapNoExtraKeysBefore + (0L, zeroValue), - k, - v, - Long.MinValue, - minValue - ) - } else if ((extraKeys & 1) != 0 && (extraKeys & 2) == 0) { - // it means there is a mapping for the key 0 - check(mapAfter == mapNoExtraKeysAfter + (0L, zeroValue)) - check(mapBefore == (mapNoExtraKeysBefore + (0L, zeroValue))) - check(mapAfter == (mapNoExtraKeysBefore + (k, v)) + (0L, zeroValue)) - ListLongMapLemmas.addCommutativeForDiffKeys(mapNoExtraKeysBefore, k, v, 0L, zeroValue) - } else if ((extraKeys & 2) != 0 && (extraKeys & 1) == 0) { - // it means there is a mapping for the key Long.MIN_VALUE - check(mapAfter == mapNoExtraKeysAfter + (Long.MinValue, minValue)) - check(mapAfter == (mapNoExtraKeysBefore + (k, v)) + (Long.MinValue, minValue)) - check(mapBefore == (mapNoExtraKeysBefore + (Long.MinValue, minValue))) - ListLongMapLemmas.addCommutativeForDiffKeys( - mapNoExtraKeysBefore, - k, - v, - Long.MinValue, - minValue - ) - } else { - check(mapAfter == mapNoExtraKeysAfter) - } - - } ensuring (_ => - getCurrentListMap( - _keys, - _values, - mask, - extraKeys, - zeroValue, - minValue, - 0, - defaultEntry - ) + (k, v) == getCurrentListMap( - _keys, - _values.updated(i, ValueCellFull(v)), - mask, - extraKeys, - zeroValue, - minValue, - 0, - defaultEntry - ) - ) - - @opaque - @inlineOnce - @pure - @ghost - def lemmaChangeValueExistingKeyToArrayThenMapNoExtrasAddPair[V]( - _keys: Array[Long], - _values: Array[ValueCell[V]], - mask: Int, - extraKeys: Int, - zeroValue: V, - minValue: V, - i: Int, - k: Long, - v: V, - from: Int, - defaultEntry: Long => V - ): Unit = { - require(from >= 0 && from <= _keys.length) - require(validMask(mask)) - require(_values.length == mask + 1) - require(_keys.length == _values.length) - require(mask >= 0) - require(extraKeys >= 0) - require(extraKeys <= 3) - require(arrayForallSeekEntryOrOpenFound(0)(_keys, mask)) - require(arrayNoDuplicates(_keys, 0)) - - require(i >= 0 && i < _keys.length) - require(validKeyInArray(k)) - require(_keys(i) == k) - - decreases(_keys.length - from) - - if (from > i) { - if (from + 1 <= _keys.length) { - lemmaChangeValueExistingKeyToArrayThenMapNoExtrasAddPair( - _keys, - _values, - mask, - extraKeys, - zeroValue, - minValue, - i, - k, - v, - from + 1, - defaultEntry - ) - } - } else { - assert(from <= i) - val listmapNoExtrasBefore = - getCurrentListMapNoExtraKeys( - _keys, - _values, - mask, - extraKeys, - zeroValue, - minValue, - from, - defaultEntry - ) - val listmapNoExtrasAfter = - getCurrentListMapNoExtraKeys( - _keys, - _values.updated(i, ValueCellFull(v)), - mask, - extraKeys, - zeroValue, - minValue, - from, - defaultEntry - ) - if (from == i) { - lemmaChangeValueExistingKeyToArrayThenMapNoExtrasAddPair( - _keys, - _values, - mask, - extraKeys, - zeroValue, - minValue, - i, - k, - v, - from + 1, - defaultEntry - ) - assert(validKeyInArray(_keys(from))) - check( - listmapNoExtrasAfter == - getCurrentListMapNoExtraKeys( - _keys, - _values.updated(i, ValueCellFull(v)), - mask, - extraKeys, - zeroValue, - minValue, - from + 1, - defaultEntry - ) + (_keys(from), _values - .updated(i, ValueCellFull(v)) - .apply(from) - .get(defaultEntry(0L))) - ) - - ListLongMapLemmas.addSameAsAddTwiceSameKeyDiffValues( - getCurrentListMapNoExtraKeys( - _keys, - _values.updated(i, ValueCellFull(v)), - mask, - extraKeys, - zeroValue, - minValue, - from + 1, - defaultEntry - ), - k, - _values(from).get(defaultEntry(0L)), - _values.updated(i, ValueCellFull(v)).apply(from).get(defaultEntry(0L)) - ) - - } else { - assert(from < i) - lemmaChangeValueExistingKeyToArrayThenMapNoExtrasAddPair( - _keys, - _values, - mask, - extraKeys, - zeroValue, - minValue, - i, - k, - v, - from + 1, - defaultEntry - ) - - if (validKeyInArray(_keys(from))) { - check( - getCurrentListMapNoExtraKeys( - _keys, - _values.updated(i, ValueCellFull(v)), - mask, - extraKeys, - zeroValue, - minValue, - from, - defaultEntry - ) == - ((getCurrentListMapNoExtraKeys( - _keys, - _values, - mask, - extraKeys, - zeroValue, - minValue, - from + 1, - defaultEntry - ) + (k, v) + (_keys(from), _values(from).get(defaultEntry(0L))))) - ) - - if (_keys(from) == k) { - lemmaNoDuplicateFromThenFromBigger(_keys, 0, from) - lemmaArrayContainsFromImpliesContainsFromSmaller(_keys, k, i, from + 1) - lemmaArrayNoDuplicateThenKeysContainedNotEqual(_keys, k, from, Nil()) - check(false) - } - check(_keys(from) != k) - - ListLongMapLemmas.addCommutativeForDiffKeys( - getCurrentListMapNoExtraKeys( - _keys, - _values, - mask, - extraKeys, - zeroValue, - minValue, - from + 1, - defaultEntry - ), - k, - v, - _keys(from), - _values(from).get(defaultEntry(0L)) - ) - } - } - } - - } ensuring (_ => - if (from <= i) - getCurrentListMapNoExtraKeys( - _keys, - _values.updated(i, ValueCellFull(v)), - mask, - extraKeys, - zeroValue, - minValue, - from, - defaultEntry - ) == - (getCurrentListMapNoExtraKeys( - _keys, - _values, - mask, - extraKeys, - zeroValue, - minValue, - from, - defaultEntry - ) + (k, v)) - else - getCurrentListMapNoExtraKeys( - _keys, - _values.updated(i, ValueCellFull(v)), - mask, - extraKeys, - zeroValue, - minValue, - from, - defaultEntry - ) == - getCurrentListMapNoExtraKeys( - _keys, - _values, - mask, - extraKeys, - zeroValue, - minValue, - from, - defaultEntry - ) - ) - - @opaque - @inlineOnce - @pure - @ghost - def lemmaChangeZeroKeyThenAddPairToListMap[V]( - _keys: Array[Long], - _values: Array[ValueCell[V]], - mask: Int, - extraKeysBefore: Int, - extraKeysAfter: Int, - zeroValueBefore: V, - zeroValueAfter: V, - minValue: V, - defaultEntry: Long => V - ): Unit = { - require(validMask(mask)) - require(_values.length == mask + 1) - require(_keys.length == _values.length) - require(mask >= 0) - require(extraKeysBefore >= 0) - require(extraKeysBefore <= 3) - require(extraKeysAfter >= 0) - require(extraKeysAfter <= 3) - require((extraKeysBefore & 2) == (extraKeysAfter & 2)) // Long.MinValue key does not change - require((extraKeysAfter & 1) != 0) // 0 key must be defined after - require(arrayForallSeekEntryOrOpenFound(0)(_keys, mask)) - require(arrayNoDuplicates(_keys, 0)) - - val mapNoExtraKeysBefore = - getCurrentListMapNoExtraKeys( - _keys, - _values, - mask, - extraKeysBefore, - zeroValueBefore, - minValue, - 0, - defaultEntry - ) - val mapNoExtraKeysAfter = - getCurrentListMapNoExtraKeys( - _keys, - _values, - mask, - extraKeysAfter, - zeroValueAfter, - minValue, - 0, - defaultEntry - ) - - lemmaNoChangeToArrayThenSameMapNoExtras( - _keys, - _values, - mask, - extraKeysBefore, - extraKeysAfter, - zeroValueBefore, - zeroValueAfter, - minValue, - minValue, - 0, - defaultEntry - ) - assert(mapNoExtraKeysBefore == mapNoExtraKeysAfter) - - if ((extraKeysBefore & 2) == 0) { - // key Long.MinValue not defined - if ((extraKeysBefore & 1) == 0) { - check( - getCurrentListMap( - _keys, - _values, - mask, - extraKeysBefore, - zeroValueBefore, - minValue, - 0, - defaultEntry - ) + (0, zeroValueAfter) == getCurrentListMap( - _keys, - _values, - mask, - extraKeysAfter, - zeroValueAfter, - minValue, - 0, - defaultEntry - ) - ) - } else { - val mapBefore = - getCurrentListMap( - _keys, - _values, - mask, - extraKeysBefore, - zeroValueBefore, - minValue, - 0, - defaultEntry - ) - val mapAfter = - getCurrentListMap( - _keys, - _values, - mask, - extraKeysAfter, - zeroValueAfter, - minValue, - 0, - defaultEntry - ) - - check(mapBefore == (mapNoExtraKeysBefore + (0L, zeroValueBefore))) - check(mapAfter == (mapNoExtraKeysAfter + (0L, zeroValueAfter))) - - ListLongMapLemmas.addSameAsAddTwiceSameKeyDiffValues( - mapNoExtraKeysBefore, - 0L, - zeroValueBefore, - zeroValueAfter - ) - check(mapAfter == (mapNoExtraKeysAfter + (0L, zeroValueBefore)) + (0L, zeroValueAfter)) - } - - } else { - // key Long.MinValue defined - if ((extraKeysBefore & 1) == 0) { - val mapBefore = - getCurrentListMap( - _keys, - _values, - mask, - extraKeysBefore, - zeroValueBefore, - minValue, - 0, - defaultEntry - ) - val mapAfter = - getCurrentListMap( - _keys, - _values, - mask, - extraKeysAfter, - zeroValueAfter, - minValue, - 0, - defaultEntry - ) - - check(mapBefore == (mapNoExtraKeysBefore + (Long.MinValue, minValue))) - check( - mapAfter == ((mapNoExtraKeysBefore + (0L, zeroValueAfter)) + (Long.MinValue, minValue)) - ) - ListLongMapLemmas.addCommutativeForDiffKeys( - mapNoExtraKeysBefore, - 0L, - zeroValueAfter, - Long.MinValue, - minValue - ) - } else { - val mapBefore = - getCurrentListMap( - _keys, - _values, - mask, - extraKeysBefore, - zeroValueBefore, - minValue, - 0, - defaultEntry - ) - val mapAfter = - getCurrentListMap( - _keys, - _values, - mask, - extraKeysAfter, - zeroValueAfter, - minValue, - 0, - defaultEntry - ) - - check( - mapBefore == (mapNoExtraKeysBefore + (0L, zeroValueBefore)) + (Long.MinValue, minValue) - ) - check( - mapAfter == (mapNoExtraKeysAfter + (0L, zeroValueAfter)) + (Long.MinValue, minValue) - ) - - check( - mapBefore == (mapNoExtraKeysBefore + (Long.MinValue, minValue)) + (0L, zeroValueBefore) - ) - check( - mapAfter == (mapNoExtraKeysAfter + (Long.MinValue, minValue)) + (0L, zeroValueAfter) - ) - ListLongMapLemmas.addSameAsAddTwiceSameKeyDiffValues( - (mapNoExtraKeysBefore + (Long.MinValue, minValue)), - 0L, - zeroValueBefore, - zeroValueAfter - ) - check( - mapAfter == ((mapNoExtraKeysBefore + (Long.MinValue, minValue)) + (0L, zeroValueBefore)) + (0L, zeroValueAfter) - ) - check( - mapAfter == (mapNoExtraKeysBefore + (Long.MinValue, minValue)) + (0L, zeroValueAfter) - ) - check( - mapAfter == (mapNoExtraKeysBefore + (0L, zeroValueAfter)) + (Long.MinValue, minValue) - ) - } - } - - } ensuring (_ => - getCurrentListMap( - _keys, - _values, - mask, - extraKeysBefore, - zeroValueBefore, - minValue, - 0, - defaultEntry - ) + (0, zeroValueAfter) == getCurrentListMap( - _keys, - _values, - mask, - extraKeysAfter, - zeroValueAfter, - minValue, - 0, - defaultEntry - ) - ) - - @opaque - @inlineOnce - @pure - @ghost - def lemmaChangeLongMinValueKeyThenAddPairToListMap[V]( - _keys: Array[Long], - _values: Array[ValueCell[V]], - mask: Int, - extraKeysBefore: Int, - extraKeysAfter: Int, - zeroValue: V, - minValueBefore: V, - minValueAfter: V, - defaultEntry: Long => V - ): Unit = { - require(validMask(mask)) - require(_values.length == mask + 1) - require(_keys.length == _values.length) - require(mask >= 0) - require(extraKeysBefore >= 0) - require(extraKeysBefore <= 3) - require(extraKeysAfter >= 0) - require(extraKeysAfter <= 3) - require((extraKeysBefore & 1) == (extraKeysAfter & 1)) // zero key does not change - require((extraKeysAfter & 2) != 0) // MinValue key must be defined after - require(arrayForallSeekEntryOrOpenFound(0)(_keys, mask)) - require(arrayNoDuplicates(_keys, 0)) - - val mapNoExtraKeysBefore = - getCurrentListMapNoExtraKeys( - _keys, - _values, - mask, - extraKeysBefore, - zeroValue, - minValueBefore, - 0, - defaultEntry - ) - val mapNoExtraKeysAfter = - getCurrentListMapNoExtraKeys( - _keys, - _values, - mask, - extraKeysAfter, - zeroValue, - minValueAfter, - 0, - defaultEntry - ) - - lemmaNoChangeToArrayThenSameMapNoExtras( - _keys, - _values, - mask, - extraKeysBefore, - extraKeysAfter, - zeroValue, - zeroValue, - minValueBefore, - minValueAfter, - 0, - defaultEntry - ) - - assert(mapNoExtraKeysBefore == mapNoExtraKeysAfter) - - if ((extraKeysBefore & 1) == 0) { - // key 0 not defined - if ((extraKeysBefore & 2) == 0) { - check( - getCurrentListMap( - _keys, - _values, - mask, - extraKeysBefore, - zeroValue, - minValueBefore, - 0, - defaultEntry - ) + (Long.MinValue, minValueAfter) == getCurrentListMap( - _keys, - _values, - mask, - extraKeysAfter, - zeroValue, - minValueAfter, - 0, - defaultEntry - ) - ) - } else { - val mapBefore = - getCurrentListMap( - _keys, - _values, - mask, - extraKeysBefore, - zeroValue, - minValueBefore, - 0, - defaultEntry - ) - val mapAfter = - getCurrentListMap( - _keys, - _values, - mask, - extraKeysAfter, - zeroValue, - minValueAfter, - 0, - defaultEntry - ) - - ListLongMapLemmas.addSameAsAddTwiceSameKeyDiffValues( - mapNoExtraKeysBefore, - Long.MinValue, - minValueBefore, - minValueAfter - ) - } - - } else { - // key 0 defined - if ((extraKeysBefore & 2) == 0) { - // trivial - } else { - val mapBefore = - getCurrentListMap( - _keys, - _values, - mask, - extraKeysBefore, - zeroValue, - minValueBefore, - 0, - defaultEntry - ) - val mapAfter = - getCurrentListMap( - _keys, - _values, - mask, - extraKeysAfter, - zeroValue, - minValueAfter, - 0, - defaultEntry - ) - - ListLongMapLemmas.addSameAsAddTwiceSameKeyDiffValues( - mapNoExtraKeysBefore + (0L, zeroValue), - Long.MinValue, - minValueBefore, - minValueAfter - ) - } - } - - } ensuring (_ => - getCurrentListMap( - _keys, - _values, - mask, - extraKeysBefore, - zeroValue, - minValueBefore, - 0, - defaultEntry - ) + (Long.MinValue, minValueAfter) == getCurrentListMap( - _keys, - _values, - mask, - extraKeysAfter, - zeroValue, - minValueAfter, - 0, - defaultEntry - ) - ) - - @opaque - @inlineOnce - @pure - @ghost - def lemmaRemoveValidKeyToArrayThenRemoveKeyFromListMap[V]( - _keys: Array[Long], - _values: Array[ValueCell[V]], - mask: Int, - extraKeys: Int, - zeroValue: V, - minValue: V, - i: Int, - k: Long, - defaultEntry: Long => V - ): Unit = { - require(validMask(mask)) - require(_values.length == mask + 1) - require(_keys.length == _values.length) - require(mask >= 0) - require(extraKeys >= 0) - require(extraKeys <= 3) - require(arrayForallSeekEntryOrOpenFound(0)(_keys, mask)) - require(arrayNoDuplicates(_keys, 0)) - - require(i >= 0 && i < _keys.length) - require(validKeyInArray(k)) - require(_keys(i) == k) - require(arrayForallSeekEntryOrOpenFound(0)(_keys.updated(i, Long.MinValue), mask)) - require(arrayNoDuplicates(_keys.updated(i, Long.MinValue), 0)) - - lemmaArrayContainsFromImpliesContainsFromZero(_keys, k, i) - assert(arrayContainsKey(_keys, k, 0)) - - val mapNoExtraKeysBefore = - getCurrentListMapNoExtraKeys( - _keys, - _values, - mask, - extraKeys, - zeroValue, - minValue, - 0, - defaultEntry - ) - val mapNoExtraKeysAfter = getCurrentListMapNoExtraKeys( - _keys.updated(i, Long.MinValue), - _values.updated(i, ValueCellFull(defaultEntry(0L))), - mask, - extraKeys, - zeroValue, - minValue, - 0, - defaultEntry - ) - - lemmaRemoveValidKeyFromArrayThenMapNoExtrasRemoveKey( - _keys, - _values, - mask, - extraKeys, - zeroValue, - minValue, - i, - k, - 0, - defaultEntry - ) - - check(mapNoExtraKeysBefore - k == mapNoExtraKeysAfter) - - val mapAfter = - getCurrentListMap( - _keys.updated(i, Long.MinValue), - _values.updated(i, ValueCellFull(defaultEntry(0L))), - mask, - extraKeys, - zeroValue, - minValue, - 0, - defaultEntry - ) - val mapBefore = - getCurrentListMap(_keys, _values, mask, extraKeys, zeroValue, minValue, 0, defaultEntry) - if ((extraKeys & 1) != 0 && (extraKeys & 2) != 0) { - // it means there is a mapping for the key 0 and the Long.MIN_VALUE - check(mapAfter == (mapNoExtraKeysAfter + (0L, zeroValue)) + (Long.MinValue, minValue)) - check(mapBefore == (mapNoExtraKeysBefore + (0L, zeroValue)) + (Long.MinValue, minValue)) - check( - mapAfter == ((mapNoExtraKeysBefore - k) + (0L, zeroValue)) + (Long.MinValue, minValue) - ) - - ListLongMapLemmas.addRemoveCommutativeForDiffKeys(mapNoExtraKeysBefore, 0L, zeroValue, k) - ListLongMapLemmas.addRemoveCommutativeForDiffKeys( - mapNoExtraKeysBefore + (0L, zeroValue), - Long.MinValue, - minValue, - k - ) - } else if ((extraKeys & 1) != 0 && (extraKeys & 2) == 0) { - // it means there is a mapping for the key 0 - check(mapAfter == mapNoExtraKeysAfter + (0L, zeroValue)) - check(mapBefore == (mapNoExtraKeysBefore + (0L, zeroValue))) - check(mapAfter == (mapNoExtraKeysBefore - k) + (0L, zeroValue)) - - ListLongMapLemmas.addRemoveCommutativeForDiffKeys(mapNoExtraKeysBefore, 0L, zeroValue, k) - - } else if ((extraKeys & 2) != 0 && (extraKeys & 1) == 0) { - // it means there is a mapping for the key Long.MIN_VALUE - check(mapAfter == mapNoExtraKeysAfter + (Long.MinValue, minValue)) - check(mapAfter == (mapNoExtraKeysBefore - k) + (Long.MinValue, minValue)) - check(mapBefore == (mapNoExtraKeysBefore + (Long.MinValue, minValue))) - - ListLongMapLemmas.addRemoveCommutativeForDiffKeys( - mapNoExtraKeysBefore, - Long.MinValue, - minValue, - k - ) - } else { - check(mapAfter == mapNoExtraKeysAfter) - - } - - } ensuring (_ => - getCurrentListMap( - _keys, - _values, - mask, - extraKeys, - zeroValue, - minValue, - 0, - defaultEntry - ) - k == getCurrentListMap( - _keys.updated(i, Long.MinValue), - _values.updated(i, ValueCellFull(defaultEntry(0L))), - mask, - extraKeys, - zeroValue, - minValue, - 0, - defaultEntry - ) - ) - - @opaque - @inlineOnce - @pure - @ghost - def lemmaRemoveLongMinValueKeyThenRemoveKeyFromListMap[V]( - _keys: Array[Long], - _values: Array[ValueCell[V]], - mask: Int, - extraKeysBefore: Int, - extraKeysAfter: Int, - zeroValue: V, - minValueBefore: V, - minValueAfter: V, - defaultEntry: Long => V - ): Unit = { - require(validMask(mask)) - require(_values.length == mask + 1) - require(_keys.length == _values.length) - require(mask >= 0) - require(extraKeysBefore >= 0) - require(extraKeysBefore <= 3) - require(extraKeysAfter >= 0) - require(extraKeysAfter <= 3) - require((extraKeysBefore & 1) == (extraKeysAfter & 1)) // zero key does not change - require((extraKeysAfter & 2) == 0) // MinValue key must be removed after - require(arrayForallSeekEntryOrOpenFound(0)(_keys, mask)) - require(arrayNoDuplicates(_keys, 0)) - - val mapNoExtraKeysBefore = - getCurrentListMapNoExtraKeys( - _keys, - _values, - mask, - extraKeysBefore, - zeroValue, - minValueBefore, - 0, - defaultEntry - ) - val mapNoExtraKeysAfter = - getCurrentListMapNoExtraKeys( - _keys, - _values, - mask, - extraKeysAfter, - zeroValue, - minValueAfter, - 0, - defaultEntry - ) - - lemmaNoChangeToArrayThenSameMapNoExtras( - _keys, - _values, - mask, - extraKeysBefore, - extraKeysAfter, - zeroValue, - zeroValue, - minValueBefore, - minValueAfter, - 0, - defaultEntry - ) - - assert(mapNoExtraKeysBefore == mapNoExtraKeysAfter) - - if ((extraKeysBefore & 1) == 0) { - // key 0 not defined - if ((extraKeysBefore & 2) == 0) { - ListLongMapLemmas.removeNotPresentStillSame( - getCurrentListMap( - _keys, - _values, - mask, - extraKeysBefore, - zeroValue, - minValueBefore, - 0, - defaultEntry - ), - Long.MinValue - ) - } else { - val mapBefore = - getCurrentListMap( - _keys, - _values, - mask, - extraKeysBefore, - zeroValue, - minValueBefore, - 0, - defaultEntry - ) - val mapAfter = - getCurrentListMap( - _keys, - _values, - mask, - extraKeysAfter, - zeroValue, - minValueAfter, - 0, - defaultEntry - ) - - check(mapBefore == mapNoExtraKeysBefore + (Long.MinValue, minValueBefore)) - check(mapAfter == mapNoExtraKeysAfter) - ListLongMapLemmas.addThenRemoveForNewKeyIsSame( - mapNoExtraKeysBefore, - Long.MinValue, - minValueBefore - ) - check(mapBefore - Long.MinValue == mapAfter) - } - - } else { - // key 0 defined - if ((extraKeysBefore & 2) == 0) { - ListLongMapLemmas.removeNotPresentStillSame( - getCurrentListMap( - _keys, - _values, - mask, - extraKeysBefore, - zeroValue, - minValueBefore, - 0, - defaultEntry - ), - Long.MinValue - ) - } else { - val mapBefore = - getCurrentListMap( - _keys, - _values, - mask, - extraKeysBefore, - zeroValue, - minValueBefore, - 0, - defaultEntry - ) - val mapAfter = - getCurrentListMap( - _keys, - _values, - mask, - extraKeysAfter, - zeroValue, - minValueAfter, - 0, - defaultEntry - ) - - check( - mapBefore == (mapNoExtraKeysBefore + (0L, zeroValue)) + (Long.MinValue, minValueBefore) - ) - check(mapAfter == (mapNoExtraKeysAfter + (0L, zeroValue))) - ListLongMapLemmas.addThenRemoveForNewKeyIsSame( - mapNoExtraKeysBefore + (0L, zeroValue), - Long.MinValue, - minValueBefore - ) - check(mapBefore - Long.MinValue == mapAfter) - } - } - - } ensuring (_ => - getCurrentListMap( - _keys, - _values, - mask, - extraKeysBefore, - zeroValue, - minValueBefore, - 0, - defaultEntry - ) - Long.MinValue == getCurrentListMap( - _keys, - _values, - mask, - extraKeysAfter, - zeroValue, - minValueAfter, - 0, - defaultEntry - ) - ) - - @opaque - @inlineOnce - @pure - @ghost - def lemmaRemoveZeroKeyThenRemoveKeyFromListMap[V]( - _keys: Array[Long], - _values: Array[ValueCell[V]], - mask: Int, - extraKeysBefore: Int, - extraKeysAfter: Int, - zeroValueBefore: V, - zeroValueAfter: V, - minValue: V, - defaultEntry: Long => V - ): Unit = { - require(validMask(mask)) - require(_values.length == mask + 1) - require(_keys.length == _values.length) - require(mask >= 0) - require(extraKeysBefore >= 0) - require(extraKeysBefore <= 3) - require(extraKeysAfter >= 0) - require(extraKeysAfter <= 3) - require((extraKeysBefore & 2) == (extraKeysAfter & 2)) // MinValue key does not change - require((extraKeysAfter & 1) == 0) // 0 key must be removed after - require(arrayForallSeekEntryOrOpenFound(0)(_keys, mask)) - require(arrayNoDuplicates(_keys, 0)) - - val mapNoExtraKeysBefore = - getCurrentListMapNoExtraKeys( - _keys, - _values, - mask, - extraKeysBefore, - zeroValueBefore, - minValue, - 0, - defaultEntry - ) - val mapNoExtraKeysAfter = - getCurrentListMapNoExtraKeys( - _keys, - _values, - mask, - extraKeysAfter, - zeroValueAfter, - minValue, - 0, - defaultEntry - ) - - lemmaNoChangeToArrayThenSameMapNoExtras( - _keys, - _values, - mask, - extraKeysBefore, - extraKeysAfter, - zeroValueBefore, - zeroValueAfter, - minValue, - minValue, - 0, - defaultEntry - ) - - assert(mapNoExtraKeysBefore == mapNoExtraKeysAfter) - - if ((extraKeysBefore & 2) == 0) { - // MinValue not defined - if ((extraKeysBefore & 1) == 0) { - ListLongMapLemmas.removeNotPresentStillSame( - getCurrentListMap( - _keys, - _values, - mask, - extraKeysBefore, - zeroValueBefore, - minValue, - 0, - defaultEntry - ), - 0L - ) - } else { - val mapBefore = - getCurrentListMap( - _keys, - _values, - mask, - extraKeysBefore, - zeroValueBefore, - minValue, - 0, - defaultEntry - ) - val mapAfter = - getCurrentListMap( - _keys, - _values, - mask, - extraKeysAfter, - zeroValueAfter, - minValue, - 0, - defaultEntry - ) - - check(mapBefore == mapNoExtraKeysBefore + (0L, zeroValueBefore)) - check(mapAfter == mapNoExtraKeysAfter) - ListLongMapLemmas.addThenRemoveForNewKeyIsSame( - mapNoExtraKeysBefore, - 0L, - zeroValueBefore - ) - check(mapBefore - 0L == mapAfter) - } - - } else { - // MinValue defined - if ((extraKeysBefore & 1) == 0) { - ListLongMapLemmas.removeNotPresentStillSame( - getCurrentListMap( - _keys, - _values, - mask, - extraKeysBefore, - zeroValueBefore, - minValue, - 0, - defaultEntry - ), - 0L - ) - } else { - val mapBefore = - getCurrentListMap( - _keys, - _values, - mask, - extraKeysBefore, - zeroValueBefore, - minValue, - 0, - defaultEntry - ) - val mapAfter = - getCurrentListMap( - _keys, - _values, - mask, - extraKeysAfter, - zeroValueAfter, - minValue, - 0, - defaultEntry - ) - - check( - mapBefore == (mapNoExtraKeysBefore + (0L, zeroValueBefore)) + (Long.MinValue, minValue) - ) - check(mapAfter == (mapNoExtraKeysAfter + (Long.MinValue, minValue))) - ListLongMapLemmas.addThenRemoveForNewKeyIsSame( - mapNoExtraKeysBefore + (Long.MinValue, minValue), - 0L, - zeroValueBefore - ) - ListLongMapLemmas.addCommutativeForDiffKeys( - mapNoExtraKeysBefore, - 0L, - zeroValueBefore, - Long.MinValue, - minValue - ) - check(mapBefore - 0L == mapAfter) - } - } - - } ensuring (_ => - getCurrentListMap( - _keys, - _values, - mask, - extraKeysBefore, - zeroValueBefore, - minValue, - 0, - defaultEntry - ) - 0L == getCurrentListMap( - _keys, - _values, - mask, - extraKeysAfter, - zeroValueAfter, - minValue, - 0, - defaultEntry - ) - ) - - @opaque - @inlineOnce - @pure - @ghost - @opaque - @inlineOnce - @pure - @ghost - def lemmaRemoveValidKeyFromArrayThenMapNoExtrasRemoveKey[V]( - _keys: Array[Long], - _values: Array[ValueCell[V]], - mask: Int, - extraKeys: Int, - zeroValue: V, - minValue: V, - i: Int, - k: Long, - from: Int, - defaultEntry: Long => V - ): Unit = { - require(from >= 0 && from <= _keys.length) - require(validMask(mask)) - require(_values.length == mask + 1) - require(_keys.length == _values.length) - require(mask >= 0) - require(extraKeys >= 0) - require(extraKeys <= 3) - require(arrayForallSeekEntryOrOpenFound(0)(_keys, mask)) - require(arrayNoDuplicates(_keys, 0)) - - require(i >= 0 && i < _keys.length) - require(validKeyInArray(k)) - require(_keys(i) == k) - require(arrayForallSeekEntryOrOpenFound(0)(_keys.updated(i, Long.MinValue), mask)) - require(arrayNoDuplicates(_keys.updated(i, Long.MinValue), 0)) - - decreases(_keys.length - from) - - lemmaArrayContainsFromImpliesContainsFromZero(_keys, k, i) - - check(arrayContainsKey(_keys, k, 0)) - - if (from > i) { - if (from + 1 <= _keys.length) { - lemmaRemoveValidKeyFromArrayThenMapNoExtrasRemoveKey( - _keys, - _values, - mask, - extraKeys, - zeroValue, - minValue, - i, - k, - from + 1, - defaultEntry - ) - } - - } else { - assert(from <= i) - val listmapNoExtrasBefore = - getCurrentListMapNoExtraKeys( - _keys, - _values, - mask, - extraKeys, - zeroValue, - minValue, - from, - defaultEntry - ) - val listmapNoExtrasAfter = getCurrentListMapNoExtraKeys( - _keys.updated(i, Long.MinValue), - _values.updated(i, ValueCellFull(defaultEntry(0L))), - mask, - extraKeys, - zeroValue, - minValue, - from, - defaultEntry - ) - if (from == i) { - lemmaRemoveValidKeyFromArrayThenMapNoExtrasRemoveKey( - _keys, - _values, - mask, - extraKeys, - zeroValue, - minValue, - i, - k, - from + 1, - defaultEntry - ) - assert(_keys(from) == k) - - if ( - getCurrentListMapNoExtraKeys( - _keys, - _values, - mask, - extraKeys, - zeroValue, - minValue, - from + 1, - defaultEntry - ).contains(k) - ) { - if ((extraKeys & 1) != 0 && (extraKeys & 2) != 0) { - ListLongMapLemmas.addStillContains( - getCurrentListMapNoExtraKeys( - _keys, - _values, - mask, - extraKeys, - zeroValue, - minValue, - from + 1, - defaultEntry - ), - 0L, - zeroValue, - k - ) - ListLongMapLemmas.addStillContains( - getCurrentListMapNoExtraKeys( - _keys, - _values, - mask, - extraKeys, - zeroValue, - minValue, - from + 1, - defaultEntry - ) + (0L, zeroValue), - Long.MinValue, - minValue, - k - ) - } else if ((extraKeys & 1) != 0 && (extraKeys & 2) == 0) { - ListLongMapLemmas.addStillContains( - getCurrentListMapNoExtraKeys( - _keys, - _values, - mask, - extraKeys, - zeroValue, - minValue, - from + 1, - defaultEntry - ), - 0L, - zeroValue, - k - ) - } else if ((extraKeys & 2) != 0 && (extraKeys & 1) == 0) { - ListLongMapLemmas.addStillContains( - getCurrentListMapNoExtraKeys( - _keys, - _values, - mask, - extraKeys, - zeroValue, - minValue, - from + 1, - defaultEntry - ), - Long.MinValue, - minValue, - k - ) - } - lemmaListMapContainsThenArrayContainsFrom( - _keys, - _values, - mask, - extraKeys, - zeroValue, - minValue, - k, - from + 1, - defaultEntry - ) - check(arrayContainsKey(_keys, k, from + 1)) - lemmaNoDuplicateFromThenFromBigger(_keys, 0, from) - lemmaArrayNoDuplicateThenKeysContainedNotEqual(_keys, k, from, Nil()) - check(false) - } - ListLongMapLemmas.addThenRemoveForNewKeyIsSame( - getCurrentListMapNoExtraKeys( - _keys, - _values, - mask, - extraKeys, - zeroValue, - minValue, - from + 1, - defaultEntry - ), - _keys(from), - _values(from).get(defaultEntry(0L)) - ) - check( - getCurrentListMapNoExtraKeys( - _keys, - _values, - mask, - extraKeys, - zeroValue, - minValue, - from, - defaultEntry - ) - k == - getCurrentListMapNoExtraKeys( - _keys, - _values, - mask, - extraKeys, - zeroValue, - minValue, - from + 1, - defaultEntry - ) - ) - check( - getCurrentListMapNoExtraKeys( - _keys, - _values, - mask, - extraKeys, - zeroValue, - minValue, - from, - defaultEntry - ) - k == - getCurrentListMapNoExtraKeys( - _keys.updated(i, Long.MinValue), - _values.updated(i, ValueCellFull(defaultEntry(0L))), - mask, - extraKeys, - zeroValue, - minValue, - from + 1, - defaultEntry - ) - ) - check( - getCurrentListMapNoExtraKeys( - _keys, - _values, - mask, - extraKeys, - zeroValue, - minValue, - from, - defaultEntry - ) - k == - getCurrentListMapNoExtraKeys( - _keys.updated(i, Long.MinValue), - _values.updated(i, ValueCellFull(defaultEntry(0L))), - mask, - extraKeys, - zeroValue, - minValue, - from, - defaultEntry - ) - ) - - } else { - assert(from < i) - lemmaRemoveValidKeyFromArrayThenMapNoExtrasRemoveKey( - _keys, - _values, - mask, - extraKeys, - zeroValue, - minValue, - i, - k, - from + 1, - defaultEntry - ) - if (validKeyInArray(_keys(from))) { - check( - getCurrentListMapNoExtraKeys( - _keys.updated(i, Long.MinValue), - _values.updated(i, ValueCellFull(defaultEntry(0L))), - mask, - extraKeys, - zeroValue, - minValue, - from, - defaultEntry - ) == - ((getCurrentListMapNoExtraKeys( - _keys, - _values, - mask, - extraKeys, - zeroValue, - minValue, - from + 1, - defaultEntry - ) - k + (_keys(from), _values(from).get(defaultEntry(0L))))) - ) - - if (_keys(from) == k) { - check(arrayContainsKey(_keys, k, i)) - lemmaArrayContainsFromImpliesContainsFromSmaller(_keys, k, i, from + 1) - lemmaNoDuplicateFromThenFromBigger(_keys, 0, from) - lemmaArrayNoDuplicateThenKeysContainedNotEqual(_keys, k, from, Nil()) - check(false) - } - - check(_keys(from) != k) - - check( - getCurrentListMapNoExtraKeys( - _keys, - _values, - mask, - extraKeys, - zeroValue, - minValue, - from, - defaultEntry - ) == - getCurrentListMapNoExtraKeys( - _keys, - _values, - mask, - extraKeys, - zeroValue, - minValue, - from + 1, - defaultEntry - ) + (_keys(from), _values(from).get(defaultEntry(0L))) - ) - check( - getCurrentListMapNoExtraKeys( - _keys, - _values, - mask, - extraKeys, - zeroValue, - minValue, - from + 1, - defaultEntry - ) - k == - getCurrentListMapNoExtraKeys( - _keys.updated(i, Long.MinValue), - _values.updated(i, ValueCellFull(defaultEntry(0L))), - mask, - extraKeys, - zeroValue, - minValue, - from + 1, - defaultEntry - ) - ) - - ListLongMapLemmas.addRemoveCommutativeForDiffKeys( - getCurrentListMapNoExtraKeys( - _keys, - _values, - mask, - extraKeys, - zeroValue, - minValue, - from + 1, - defaultEntry - ), - _keys(from), - _values(from).get(defaultEntry(0L)), - k - ) - } - } - } - - } ensuring (_ => - if (from <= i) - getCurrentListMapNoExtraKeys( - _keys.updated(i, Long.MinValue), - _values.updated(i, ValueCellFull(defaultEntry(0L))), - mask, - extraKeys, - zeroValue, - minValue, - from, - defaultEntry - ) == - (getCurrentListMapNoExtraKeys( - _keys, - _values, - mask, - extraKeys, - zeroValue, - minValue, - from, - defaultEntry - ) - k) - else - getCurrentListMapNoExtraKeys( - _keys.updated(i, Long.MinValue), - _values.updated(i, ValueCellFull(defaultEntry(0L))), - mask, - extraKeys, - zeroValue, - minValue, - from, - defaultEntry - ) == - getCurrentListMapNoExtraKeys( - _keys, - _values, - mask, - extraKeys, - zeroValue, - minValue, - from, - defaultEntry - ) - ) - - @opaque - @inlineOnce - @pure - @ghost - def lemmaNoChangeToArrayThenSameMapNoExtras[V]( - _keys: Array[Long], - _values: Array[ValueCell[V]], - mask: Int, - extraKeysBefore: Int, - extraKeysAfter: Int, - zeroValueBefore: V, - zeroValueAfter: V, - minValueBefore: V, - minValueAfter: V, - from: Int, - defaultEntry: Long => V - ): Unit = { - require(validMask(mask)) - require(_values.length == mask + 1) - require(_keys.length == _values.length) - require(mask >= 0) - require(extraKeysBefore >= 0) - require(extraKeysBefore <= 3) - require(extraKeysAfter >= 0) - require(extraKeysAfter <= 3) - require(arrayForallSeekEntryOrOpenFound(0)(_keys, mask)) - require(arrayNoDuplicates(_keys, 0)) - require(from >= 0 && from <= _keys.length) - - decreases(_keys.length - from) - - if (from < _keys.length) { - lemmaNoChangeToArrayThenSameMapNoExtras( - _keys, - _values, - mask, - extraKeysBefore, - extraKeysAfter, - zeroValueBefore, - zeroValueAfter, - minValueBefore, - minValueAfter, - from + 1, - defaultEntry - ) - } - - } ensuring (_ => - getCurrentListMapNoExtraKeys( - _keys, - _values, - mask, - extraKeysBefore, - zeroValueBefore, - minValueBefore, - from, - defaultEntry - ) == - getCurrentListMapNoExtraKeys( - _keys, - _values, - mask, - extraKeysAfter, - zeroValueAfter, - minValueAfter, - from, - defaultEntry - ) - ) - - // ------------------EQUIVALENCE BETWEEN LISTMAP AND ARRAY------------------------------------------------------------------------------------------------ - // ------------------BEGIN-------------------------------------------------------------------------------------------------------------------------------- - - @opaque - @inlineOnce - @pure - @ghost - def lemmaKeyInListMapThenSameValueInArray[V]( - _keys: Array[Long], - _values: Array[ValueCell[V]], - mask: Int, - extraKeys: Int, - zeroValue: V, - minValue: V, - k: Long, - i: Int, - defaultEntry: Long => V - ): Unit = { - require(validMask(mask)) - require(_values.length == mask + 1) - require(_keys.length == _values.length) - require(mask >= 0) - - require(extraKeys >= 0) - require(extraKeys <= 3) - require(arrayForallSeekEntryOrOpenFound(0)(_keys, mask)) - require(arrayNoDuplicates(_keys, 0)) - - require( - getCurrentListMap(_keys, _values, mask, extraKeys, zeroValue, minValue, 0, defaultEntry) - .contains(k) - ) - require(inRange(i, mask)) - require(_keys(i) == k) - - if (k != 0 && k != Long.MinValue) { - lemmaKeyInListMapIsInArray( - _keys, - _values, - mask, - extraKeys, - zeroValue, - minValue, - k, - defaultEntry - ) - lemmaListMapApplyFromThenApplyFromZero( - _keys, - _values, - mask, - extraKeys, - zeroValue, - minValue, - k, - _values(i).get(defaultEntry(0L)), - i, - defaultEntry - ) - } - } ensuring (_ => - if (k == 0) - (extraKeys & 1) != 0 && getCurrentListMap( - _keys, - _values, - mask, - extraKeys, - zeroValue, - minValue, - 0, - defaultEntry - ).apply( - k - ) == zeroValue - else if (k == Long.MinValue) - (extraKeys & 2) != 0 && getCurrentListMap( - _keys, - _values, - mask, - extraKeys, - zeroValue, - minValue, - 0, - defaultEntry - ).apply( - k - ) == minValue - else - arrayContainsKey(_keys, k, 0) && getCurrentListMap( - _keys, - _values, - mask, - extraKeys, - zeroValue, - minValue, - 0, - defaultEntry - ).apply(k) == _values(i).get(defaultEntry(0L)) - ) - - @opaque - @inlineOnce - @pure - @ghost - def lemmaKeyInListMapIsInArray[V]( - _keys: Array[Long], - _values: Array[ValueCell[V]], - mask: Int, - extraKeys: Int, - zeroValue: V, - minValue: V, - k: Long, - defaultEntry: Long => V - ): Unit = { - require(validMask(mask)) - require(_values.length == mask + 1) - require(_keys.length == _values.length) - require(mask >= 0) - - require(extraKeys >= 0) - require(extraKeys <= 3) - require(arrayForallSeekEntryOrOpenFound(0)(_keys, mask)) - require(arrayNoDuplicates(_keys, 0)) - - require( - getCurrentListMap(_keys, _values, mask, extraKeys, zeroValue, minValue, 0, defaultEntry) - .contains(k) - ) - lemmaListMapContainsThenArrayContainsFrom( - _keys, - _values, - mask, - extraKeys, - zeroValue, - minValue, - k, - 0, - defaultEntry - ) - - } ensuring (_ => - if (k != 0 && k != Long.MinValue) arrayContainsKey(_keys, k, 0) - else if (k == 0) (extraKeys & 1) != 0 - else (extraKeys & 2) != 0 - ) - - @opaque - @inlineOnce - @pure - @ghost - def lemmaValidKeyInArrayIsInListMap[V]( - _keys: Array[Long], - _values: Array[ValueCell[V]], - mask: Int, - extraKeys: Int, - zeroValue: V, - minValue: V, - i: Int, - defaultEntry: Long => V - ): Unit = { - require(validMask(mask)) - require(_values.length == mask + 1) - require(_keys.length == _values.length) - require(mask >= 0) - - require(extraKeys >= 0) - require(extraKeys <= 3) - require(arrayForallSeekEntryOrOpenFound(0)(_keys, mask)) - require(arrayNoDuplicates(_keys, 0)) - - require(i >= 0 && i < _keys.length) - require(validKeyInArray(_keys(i))) - - lemmaInListMapFromThenFromZero( - _keys, - _values, - mask, - extraKeys, - zeroValue, - minValue, - i, - i, - defaultEntry - ) - - } ensuring (_ => - getCurrentListMap(_keys, _values, mask, extraKeys, zeroValue, minValue, 0, defaultEntry) - .contains(_keys(i)) - ) - - @opaque - @inlineOnce - @pure - @ghost - def lemmaArrayContainsKeyThenInListMap[V]( - _keys: Array[Long], - _values: Array[ValueCell[V]], - mask: Int, - extraKeys: Int, - zeroValue: V, - minValue: V, - k: Long, - from: Int, - defaultEntry: Long => V - ): Unit = { - require(validMask(mask)) - require(_values.length == mask + 1) - require(_keys.length == _values.length) - require(mask >= 0) - - require(extraKeys >= 0) - require(extraKeys <= 3) - require(arrayForallSeekEntryOrOpenFound(0)(_keys, mask)) - require(arrayNoDuplicates(_keys, 0)) - - require(validKeyInArray(k)) - require(from >= 0 && from < _keys.length) - require(arrayContainsKey(_keys, k, from)) - decreases(_keys.length - from) - - if (_keys(from) == k) { - lemmaValidKeyInArrayIsInListMap( - _keys, - _values, - mask, - extraKeys, - zeroValue, - minValue, - from, - defaultEntry - ) - } else { - lemmaArrayContainsFromAndNotEqualThenContainsFromPlusOne( - _keys, - _values, - mask, - extraKeys, - zeroValue, - minValue, - k, - from - ) - lemmaArrayContainsKeyThenInListMap( - _keys, - _values, - mask, - extraKeys, - zeroValue, - minValue, - k, - from + 1, - defaultEntry - ) - } - - } ensuring (_ => - getCurrentListMap(_keys, _values, mask, extraKeys, zeroValue, minValue, 0, defaultEntry) - .contains(k) - ) - - @opaque - @inlineOnce - @pure - @ghost - def lemmaListMapApplyFromThenApplyFromZero[V]( - _keys: Array[Long], - _values: Array[ValueCell[V]], - mask: Int, - extraKeys: Int, - zeroValue: V, - minValue: V, - k: Long, - v: V, - from: Int, - defaultEntry: Long => V - ): Unit = { - require(validMask(mask)) - require(_values.length == mask + 1) - require(_keys.length == _values.length) - require(mask >= 0) - - require(extraKeys >= 0) - require(extraKeys <= 3) - require(arrayForallSeekEntryOrOpenFound(0)(_keys, mask)) - require(arrayNoDuplicates(_keys, 0)) - - require(from >= 0 && from < _keys.length) - require( - getCurrentListMap(_keys, _values, mask, extraKeys, zeroValue, minValue, from, defaultEntry) - .contains(k) - ) - require( - getCurrentListMap(_keys, _values, mask, extraKeys, zeroValue, minValue, from, defaultEntry) - .apply(k) == v - ) - - decreases(from) - - if (from > 0) { - if (validKeyInArray(k)) { - if (validKeyInArray(_keys(from - 1))) { - lemmaListMapRecursiveValidKeyArray( - _keys, - _values, - mask, - extraKeys, - zeroValue, - minValue, - from - 1, - defaultEntry - ) - ListLongMapLemmas.addStillContains( - getCurrentListMap( - _keys, - _values, - mask, - extraKeys, - zeroValue, - minValue, - from, - defaultEntry - ), - _keys(from - 1), - _values(from - 1).get(defaultEntry(0L)), - k - ) - lemmaNoDuplicateFromThenFromBigger(_keys, 0, from - 1) - lemmaListMapContainsThenArrayContainsFrom( - _keys, - _values, - mask, - extraKeys, - zeroValue, - minValue, - k, - from, - defaultEntry - ) - lemmaArrayNoDuplicateThenKeysContainedNotEqual(_keys, k, from - 1, Nil()) - ListLongMapLemmas.addApplyDifferent( - getCurrentListMap( - _keys, - _values, - mask, - extraKeys, - zeroValue, - minValue, - from, - defaultEntry - ), - _keys(from - 1), - _values(from - 1).get(defaultEntry(0L)), - k - ) - } - } - lemmaListMapApplyFromThenApplyFromZero( - _keys, - _values, - mask, - extraKeys, - zeroValue, - minValue, - k, - v, - from - 1, - defaultEntry - ) - } - - } ensuring (_ => - getCurrentListMap(_keys, _values, mask, extraKeys, zeroValue, minValue, 0, defaultEntry) - .contains(k) && getCurrentListMap( - _keys, - _values, - mask, - extraKeys, - zeroValue, - minValue, - 0, - defaultEntry - ) - .apply(k) == v - ) - - @opaque - @inlineOnce - @pure - @ghost - def lemmaArrayContainsFromAndNotEqualThenContainsFromPlusOne[V]( - _keys: Array[Long], - _values: Array[ValueCell[V]], - mask: Int, - extraKeys: Int, - zeroValue: V, - minValue: V, - k: Long, - from: Int - ): Unit = { - require(validMask(mask)) - require(_values.length == mask + 1) - require(_keys.length == _values.length) - require(mask >= 0) - - require(extraKeys >= 0) - require(extraKeys <= 3) - require(arrayForallSeekEntryOrOpenFound(0)(_keys, mask)) - require(arrayNoDuplicates(_keys, 0)) - - require(from >= 0 && from < _keys.length) - require(arrayContainsKey(_keys, k, from)) - require(_keys(from) != k) - - } ensuring (_ => arrayContainsKey(_keys, k, from + 1)) - - @opaque - @inlineOnce - @pure - @ghost - def lemmaListMapRecursiveValidKeyArray[V]( - _keys: Array[Long], - _values: Array[ValueCell[V]], - mask: Int, - extraKeys: Int, - zeroValue: V, - minValue: V, - from: Int, - defaultEntry: Long => V - ): Unit = { - require(validMask(mask)) - require(_values.length == mask + 1) - require(_keys.length == _values.length) - require(mask >= 0) - - require(extraKeys >= 0) - require(extraKeys <= 3) - require(arrayForallSeekEntryOrOpenFound(0)(_keys, mask)) - require(arrayNoDuplicates(_keys, 0)) - - require(from >= 0 && from < _keys.length) - require(validKeyInArray(_keys(from))) - - if ((extraKeys & 1) != 0 && (extraKeys & 2) != 0) { - ListLongMapLemmas.addCommutativeForDiffKeys( - getCurrentListMapNoExtraKeys( - _keys, - _values, - mask, - extraKeys, - zeroValue, - minValue, - from + 1, - defaultEntry - ) + (0L, zeroValue), - Long.MinValue, - minValue, - _keys(from), - _values(from).get(defaultEntry(0L)) - ) - ListLongMapLemmas.addCommutativeForDiffKeys( - getCurrentListMapNoExtraKeys( - _keys, - _values, - mask, - extraKeys, - zeroValue, - minValue, - from + 1, - defaultEntry - ), - 0L, - zeroValue, - _keys(from), - _values(from).get(defaultEntry(0L)) - ) - } else if ((extraKeys & 1) != 0 && (extraKeys & 2) == 0) { - ListLongMapLemmas.addCommutativeForDiffKeys( - getCurrentListMapNoExtraKeys( - _keys, - _values, - mask, - extraKeys, - zeroValue, - minValue, - from + 1, - defaultEntry - ), - 0L, - zeroValue, - _keys(from), - _values(from).get(defaultEntry(0L)) - ) - } else if ((extraKeys & 2) != 0 && (extraKeys & 1) == 0) { - ListLongMapLemmas.addCommutativeForDiffKeys( - getCurrentListMapNoExtraKeys( - _keys, - _values, - mask, - extraKeys, - zeroValue, - minValue, - from + 1, - defaultEntry - ), - Long.MinValue, - minValue, - _keys(from), - _values(from).get(defaultEntry(0L)) - ) - } - - } ensuring (_ => - getCurrentListMap( - _keys, - _values, - mask, - extraKeys, - zeroValue, - minValue, - from, - defaultEntry - ) == getCurrentListMap( - _keys, - _values, - mask, - extraKeys, - zeroValue, - minValue, - from + 1, - defaultEntry - ) + (_keys(from), _values(from).get(defaultEntry(0L))) - ) - - @opaque - @inlineOnce - @pure - @ghost - def lemmaInListMapAfterAddingDiffThenInBefore[V]( - k: Long, - otherKey: Long, - value: V, - lm: ListLongMap[V] - ): Unit = { - require((lm + (otherKey, value)).contains(k)) - require(k != otherKey) - if (!lm.contains(k)) { - ListLongMapLemmas.addStillNotContains(lm, otherKey, value, k) - } - - } ensuring (_ => lm.contains(k)) - - @opaque - @inlineOnce - @pure - @ghost - def lemmaInListMapFromThenFromPlsOneIfNotEqToFstNoXMin[V]( - _keys: Array[Long], - _values: Array[ValueCell[V]], - mask: Int, - extraKeys: Int, - zeroValue: V, - minValue: V, - k: Long, - from: Int, - defaultEntry: Long => V - ): Unit = { - require(validMask(mask)) - require(_values.length == mask + 1) - require(_keys.length == _values.length) - require(mask >= 0) - - require(extraKeys >= 0) - require(extraKeys <= 3) - require(arrayForallSeekEntryOrOpenFound(0)(_keys, mask)) - require(arrayNoDuplicates(_keys, 0)) - - require((extraKeys & 2) == 0) - require(from >= 0 && from < _keys.length) - require(k != 0 && k != Long.MinValue) - require( - getCurrentListMap(_keys, _values, mask, extraKeys, zeroValue, minValue, from, defaultEntry) - .contains( - k - ) && _keys(from) != k - ) - - if (validKeyInArray(_keys(from))) { - if ((extraKeys & 1) != 0) { - lemmaInListMapAfterAddingDiffThenInBefore( - k, - 0L, - zeroValue, - getCurrentListMapNoExtraKeys( - _keys, - _values, - mask, - extraKeys, - zeroValue, - minValue, - from + 1, - defaultEntry - ) + (_keys( - from - ), _values(from).get(defaultEntry(0L))) - ) - lemmaInListMapAfterAddingDiffThenInBefore( - k, - _keys(from), - _values(from).get(defaultEntry(0L)), - getCurrentListMapNoExtraKeys( - _keys, - _values, - mask, - extraKeys, - zeroValue, - minValue, - from + 1, - defaultEntry - ) - ) - - ListLongMapLemmas.addStillContains( - getCurrentListMapNoExtraKeys( - _keys, - _values, - mask, - extraKeys, - zeroValue, - minValue, - from + 1, - defaultEntry - ), - 0L, - zeroValue, - k - ) - - } else { - lemmaInListMapAfterAddingDiffThenInBefore( - k, - _keys(from), - _values(from).get(defaultEntry(0L)), - getCurrentListMapNoExtraKeys( - _keys, - _values, - mask, - extraKeys, - zeroValue, - minValue, - from + 1, - defaultEntry - ) - ) - } - - } - - } ensuring (_ => - getCurrentListMap( - _keys, - _values, - mask, - extraKeys, - zeroValue, - minValue, - from + 1, - defaultEntry - ).contains(k) - ) - - @opaque - @inlineOnce - @pure - @ghost - def lemmaInListMapFromThenFromPlsOneIfNotEqToFstNoXZero[V]( - _keys: Array[Long], - _values: Array[ValueCell[V]], - mask: Int, - extraKeys: Int, - zeroValue: V, - minValue: V, - k: Long, - from: Int, - defaultEntry: Long => V - ): Unit = { - require(validMask(mask)) - require(_values.length == mask + 1) - require(_keys.length == _values.length) - require(mask >= 0) - - require(extraKeys >= 0) - require(extraKeys <= 3) - require(arrayForallSeekEntryOrOpenFound(0)(_keys, mask)) - require(arrayNoDuplicates(_keys, 0)) - - require((extraKeys & 1) == 0) - require(from >= 0 && from < _keys.length) - require(k != 0 && k != Long.MinValue) - require( - getCurrentListMap(_keys, _values, mask, extraKeys, zeroValue, minValue, from, defaultEntry) - .contains( - k - ) && _keys(from) != k - ) - - if (validKeyInArray(_keys(from))) { - if ((extraKeys & 2) != 0) { - - lemmaInListMapAfterAddingDiffThenInBefore( - k, - Long.MinValue, - minValue, - getCurrentListMapNoExtraKeys( - _keys, - _values, - mask, - extraKeys, - zeroValue, - minValue, - from + 1, - defaultEntry - ) + (_keys( - from - ), _values(from).get(defaultEntry(0L))) - ) - lemmaInListMapAfterAddingDiffThenInBefore( - k, - _keys(from), - _values(from).get(defaultEntry(0L)), - getCurrentListMapNoExtraKeys( - _keys, - _values, - mask, - extraKeys, - zeroValue, - minValue, - from + 1, - defaultEntry - ) - ) - - ListLongMapLemmas.addStillContains( - getCurrentListMapNoExtraKeys( - _keys, - _values, - mask, - extraKeys, - zeroValue, - minValue, - from + 1, - defaultEntry - ), - Long.MinValue, - minValue, - k - ) - - } else { - lemmaInListMapAfterAddingDiffThenInBefore( - k, - _keys(from), - _values(from).get(defaultEntry(0L)), - getCurrentListMapNoExtraKeys( - _keys, - _values, - mask, - extraKeys, - zeroValue, - minValue, - from + 1, - defaultEntry - ) - ) - } - } - - } ensuring (_ => - getCurrentListMap( - _keys, - _values, - mask, - extraKeys, - zeroValue, - minValue, - from + 1, - defaultEntry - ).contains(k) - ) - - @opaque - @inlineOnce - @pure - @ghost - def lemmaInListMapFromThenFromPlsOneIfNotEqToFstXKeys[V]( - _keys: Array[Long], - _values: Array[ValueCell[V]], - mask: Int, - extraKeys: Int, - zeroValue: V, - minValue: V, - k: Long, - from: Int, - defaultEntry: Long => V - ): Unit = { - require(validMask(mask)) - require(_values.length == mask + 1) - require(_keys.length == _values.length) - require(mask >= 0) - - require(extraKeys >= 0) - require(extraKeys <= 3) - require(arrayForallSeekEntryOrOpenFound(0)(_keys, mask)) - require(arrayNoDuplicates(_keys, 0)) - - require((extraKeys & 1) != 0 && (extraKeys & 2) != 0) - require(from >= 0 && from < _keys.length) - require(k != 0 && k != Long.MinValue) - require( - getCurrentListMap(_keys, _values, mask, extraKeys, zeroValue, minValue, from, defaultEntry) - .contains( - k - ) && _keys(from) != k - ) - - if (validKeyInArray(_keys(from))) { - lemmaInListMapAfterAddingDiffThenInBefore( - k, - Long.MinValue, - minValue, - getCurrentListMapNoExtraKeys( - _keys, - _values, - mask, - extraKeys, - zeroValue, - minValue, - from + 1, - defaultEntry - ) + (_keys( - from - ), _values(from).get(defaultEntry(0L))) + (0L, zeroValue) - ) - lemmaInListMapAfterAddingDiffThenInBefore( - k, - 0L, - zeroValue, - getCurrentListMapNoExtraKeys( - _keys, - _values, - mask, - extraKeys, - zeroValue, - minValue, - from + 1, - defaultEntry - ) + (_keys( - from - ), _values(from).get(defaultEntry(0L))) - ) - lemmaInListMapAfterAddingDiffThenInBefore( - k, - _keys(from), - _values(from).get(defaultEntry(0L)), - getCurrentListMapNoExtraKeys( - _keys, - _values, - mask, - extraKeys, - zeroValue, - minValue, - from + 1, - defaultEntry - ) - ) - - ListLongMapLemmas.addStillContains( - getCurrentListMapNoExtraKeys( - _keys, - _values, - mask, - extraKeys, - zeroValue, - minValue, - from + 1, - defaultEntry - ), - 0L, - zeroValue, - k - ) - ListLongMapLemmas.addStillContains( - getCurrentListMapNoExtraKeys( - _keys, - _values, - mask, - extraKeys, - zeroValue, - minValue, - from + 1, - defaultEntry - ) + (0L, zeroValue), - Long.MinValue, - minValue, - k - ) - } - } ensuring (_ => - getCurrentListMap( - _keys, - _values, - mask, - extraKeys, - zeroValue, - minValue, - from + 1, - defaultEntry - ).contains(k) - ) - - @opaque - @inlineOnce - @pure - @ghost - def lemmaInListMapFromThenFromPlsOneIfNotEqToFst[V]( - _keys: Array[Long], - _values: Array[ValueCell[V]], - mask: Int, - extraKeys: Int, - zeroValue: V, - minValue: V, - k: Long, - from: Int, - defaultEntry: Long => V - ): Unit = { - require(validMask(mask)) - require(_values.length == mask + 1) - require(_keys.length == _values.length) - require(mask >= 0) - - require(extraKeys >= 0) - require(extraKeys <= 3) - require(arrayForallSeekEntryOrOpenFound(0)(_keys, mask)) - require(arrayNoDuplicates(_keys, 0)) - - require(from >= 0 && from < _keys.length) - require(k != 0 && k != Long.MinValue) - require( - getCurrentListMap(_keys, _values, mask, extraKeys, zeroValue, minValue, from, defaultEntry) - .contains( - k - ) && _keys(from) != k - ) - - if (validKeyInArray(_keys(from))) { - if ((extraKeys & 1) == 0) { - lemmaInListMapFromThenFromPlsOneIfNotEqToFstNoXZero( - _keys, - _values, - mask, - extraKeys, - zeroValue, - minValue, - k, - from, - defaultEntry - ) - } else if ((extraKeys & 2) == 0) { - lemmaInListMapFromThenFromPlsOneIfNotEqToFstNoXMin( - _keys, - _values, - mask, - extraKeys, - zeroValue, - minValue, - k, - from, - defaultEntry - ) - } else { - lemmaInListMapFromThenFromPlsOneIfNotEqToFstXKeys( - _keys, - _values, - mask, - extraKeys, - zeroValue, - minValue, - k, - from, - defaultEntry - ) - } - } - - } ensuring (_ => - getCurrentListMap( - _keys, - _values, - mask, - extraKeys, - zeroValue, - minValue, - from + 1, - defaultEntry - ).contains(k) - ) - - @opaque - @inlineOnce - @pure - @ghost - def lemmaInListMapFromThenFromZero[V]( - _keys: Array[Long], - _values: Array[ValueCell[V]], - mask: Int, - extraKeys: Int, - zeroValue: V, - minValue: V, - from: Int, - i: Int, - defaultEntry: Long => V - ): Unit = { - require(validMask(mask)) - require(_values.length == mask + 1) - require(_keys.length == _values.length) - require(mask >= 0) - - require(extraKeys >= 0) - require(extraKeys <= 3) - require(arrayForallSeekEntryOrOpenFound(0)(_keys, mask)) - require(arrayNoDuplicates(_keys, 0)) - - require(from >= 0 && from < _keys.length) - require(i >= from && i < _keys.length) - require(validKeyInArray(_keys(i))) - require( - getCurrentListMap(_keys, _values, mask, extraKeys, zeroValue, minValue, from, defaultEntry) - .contains( - _keys(i) - ) - ) - - lemmaInListMapFromThenInFromSmaller( - _keys, - _values, - mask, - extraKeys, - zeroValue, - minValue, - from, - 0, - i, - defaultEntry - ) - - } ensuring (_ => - getCurrentListMap(_keys, _values, mask, extraKeys, zeroValue, minValue, 0, defaultEntry) - .contains(_keys(i)) - ) - - @opaque - @inlineOnce - @pure - @ghost - def lemmaInListMapFromThenInFromSmaller[V]( - _keys: Array[Long], - _values: Array[ValueCell[V]], - mask: Int, - extraKeys: Int, - zeroValue: V, - minValue: V, - from: Int, - newFrom: Int, - i: Int, - defaultEntry: Long => V - ): Unit = { - require(validMask(mask)) - require(_values.length == mask + 1) - require(_keys.length == _values.length) - require(mask >= 0) - - require(extraKeys >= 0) - require(extraKeys <= 3) - require(arrayForallSeekEntryOrOpenFound(0)(_keys, mask)) - require(arrayNoDuplicates(_keys, 0)) - - require(from >= 0 && from < _keys.length) - require(newFrom >= 0 && newFrom <= from) - require(i >= from && i < _keys.length) - require( - validKeyInArray(_keys(i)) && getCurrentListMap( - _keys, - _values, - mask, - extraKeys, - zeroValue, - minValue, - from, - defaultEntry - ) - .contains(_keys(i)) - ) - - decreases(from - newFrom) - if (from > newFrom) { - lemmaInListMapFromThenInFromMinusOne( - _keys, - _values, - mask, - extraKeys, - zeroValue, - minValue, - from, - i, - defaultEntry - ) - lemmaInListMapFromThenInFromSmaller( - _keys, - _values, - mask, - extraKeys, - zeroValue, - minValue, - from - 1, - newFrom, - i, - defaultEntry - ) - } - } ensuring (_ => - getCurrentListMap(_keys, _values, mask, extraKeys, zeroValue, minValue, newFrom, defaultEntry) - .contains( - _keys(i) - ) - ) - - @opaque - @inlineOnce - @pure - @ghost - def lemmaInListMapFromThenInFromMinusOne[V]( - _keys: Array[Long], - _values: Array[ValueCell[V]], - mask: Int, - extraKeys: Int, - zeroValue: V, - minValue: V, - from: Int, - i: Int, - defaultEntry: Long => V - ): Unit = { - require(validMask(mask)) - require(_values.length == mask + 1) - require(_keys.length == _values.length) - require(mask >= 0) - - require(extraKeys >= 0) - require(extraKeys <= 3) - require(arrayForallSeekEntryOrOpenFound(0)(_keys, mask)) - require(arrayNoDuplicates(_keys, 0)) - - require(from > 0 && from < _keys.length) - require(i >= from && i < _keys.length) - require( - validKeyInArray(_keys(i)) && getCurrentListMap( - _keys, - _values, - mask, - extraKeys, - zeroValue, - minValue, - from, - defaultEntry - ) - .contains(_keys(i)) - ) - - val currentLMFrom: ListLongMap[V] = - getCurrentListMap(_keys, _values, mask, extraKeys, zeroValue, minValue, from, defaultEntry) - val currentLMFromMinusOne: ListLongMap[V] = - getCurrentListMap( - _keys, - _values, - mask, - extraKeys, - zeroValue, - minValue, - from - 1, - defaultEntry - ) - if (validKeyInArray(_keys(from - 1))) { - lemmaListMapRecursiveValidKeyArray( - _keys, - _values, - mask, - extraKeys, - zeroValue, - minValue, - from - 1, - defaultEntry - ) - ListLongMapLemmas.addStillContains( - getCurrentListMap( - _keys, - _values, - mask, - extraKeys, - zeroValue, - minValue, - from, - defaultEntry - ), - _keys(from - 1), - _values(from - 1).get(defaultEntry(0L)), - _keys(i) - ) - } - } ensuring (_ => - getCurrentListMap( - _keys, - _values, - mask, - extraKeys, - zeroValue, - minValue, - from - 1, - defaultEntry - ).contains( - _keys(i) - ) - ) - - @opaque - @inlineOnce - @pure - @ghost - def lemmaListMapContainsThenArrayContainsFrom[V]( - _keys: Array[Long], - _values: Array[ValueCell[V]], - mask: Int, - extraKeys: Int, - zeroValue: V, - minValue: V, - k: Long, - from: Int, - defaultEntry: Long => V - ): Unit = { - require(validMask(mask)) - require(_values.length == mask + 1) - require(_keys.length == _values.length) - require(mask >= 0) - - require(extraKeys >= 0) - require(extraKeys <= 3) - require(arrayForallSeekEntryOrOpenFound(0)(_keys, mask)) - require(arrayNoDuplicates(_keys, 0)) - - require(from >= 0 && from < _keys.length) - require( - getCurrentListMap(_keys, _values, mask, extraKeys, zeroValue, minValue, from, defaultEntry) - .contains(k) - ) - - decreases(_keys.length - from) - val currentListMap: ListLongMap[V] = - getCurrentListMap(_keys, _values, mask, extraKeys, zeroValue, minValue, from, defaultEntry) - if (k != 0 && k != Long.MinValue) { - if (from + 1 < _keys.length) { - if (_keys(from) != k) { - lemmaInListMapFromThenFromPlsOneIfNotEqToFst( - _keys, - _values, - mask, - extraKeys, - zeroValue, - minValue, - k, - from, - defaultEntry - ) - lemmaListMapContainsThenArrayContainsFrom( - _keys, - _values, - mask, - extraKeys, - zeroValue, - minValue, - k, - from + 1, - defaultEntry - ) - } - } else { - if (validKeyInArray(_keys(from))) { - if ((extraKeys & 1) != 0 && (extraKeys & 2) != 0) { - lemmaInListMapAfterAddingDiffThenInBefore( - k, - Long.MinValue, - minValue, - (getCurrentListMapNoExtraKeys( - _keys, - _values, - mask, - extraKeys, - zeroValue, - minValue, - from, - defaultEntry - ) + (0L, zeroValue)) - ) - lemmaInListMapAfterAddingDiffThenInBefore( - k, - 0L, - zeroValue, - getCurrentListMapNoExtraKeys( - _keys, - _values, - mask, - extraKeys, - zeroValue, - minValue, - from, - defaultEntry - ) - ) - } else if ((extraKeys & 1) != 0 && (extraKeys & 2) == 0) { - lemmaInListMapAfterAddingDiffThenInBefore( - k, - 0L, - zeroValue, - getCurrentListMapNoExtraKeys( - _keys, - _values, - mask, - extraKeys, - zeroValue, - minValue, - from, - defaultEntry - ) - ) - } else if ((extraKeys & 2) != 0 && (extraKeys & 1) == 0) { - lemmaInListMapAfterAddingDiffThenInBefore( - k, - Long.MinValue, - minValue, - getCurrentListMapNoExtraKeys( - _keys, - _values, - mask, - extraKeys, - zeroValue, - minValue, - from, - defaultEntry - ) - ) - } - ListLongMapLemmas.emptyContainsNothing[V](k) - if (k != _keys(from)) { - ListLongMapLemmas.addStillNotContains( - ListLongMap.empty[V], - _keys(from), - _values(from).get(defaultEntry(0L)), - k - ) - check(false) - } - } else { - ListLongMapLemmas.emptyContainsNothing[V](k) - if ((extraKeys & 1) != 0 && (extraKeys & 2) != 0) { - ListLongMapLemmas.addStillNotContains( - getCurrentListMapNoExtraKeys( - _keys, - _values, - mask, - extraKeys, - zeroValue, - minValue, - from, - defaultEntry - ), - 0L, - zeroValue, - k - ) - ListLongMapLemmas.addStillNotContains( - getCurrentListMapNoExtraKeys( - _keys, - _values, - mask, - extraKeys, - zeroValue, - minValue, - from, - defaultEntry - ), - Long.MinValue, - minValue, - k - ) - lemmaInListMapAfterAddingDiffThenInBefore( - k, - Long.MinValue, - minValue, - (getCurrentListMapNoExtraKeys( - _keys, - _values, - mask, - extraKeys, - zeroValue, - minValue, - from, - defaultEntry - ) + (0L, zeroValue)) - ) - lemmaInListMapAfterAddingDiffThenInBefore( - k, - 0L, - zeroValue, - getCurrentListMapNoExtraKeys( - _keys, - _values, - mask, - extraKeys, - zeroValue, - minValue, - from, - defaultEntry - ) - ) - } else if ((extraKeys & 1) != 0 && (extraKeys & 2) == 0) { - ListLongMapLemmas.addStillNotContains( - getCurrentListMapNoExtraKeys( - _keys, - _values, - mask, - extraKeys, - zeroValue, - minValue, - from, - defaultEntry - ), - 0L, - zeroValue, - k - ) - lemmaInListMapAfterAddingDiffThenInBefore( - k, - 0L, - zeroValue, - getCurrentListMapNoExtraKeys( - _keys, - _values, - mask, - extraKeys, - zeroValue, - minValue, - from, - defaultEntry - ) - ) - } else if ((extraKeys & 2) != 0 && (extraKeys & 1) == 0) { - ListLongMapLemmas.addStillNotContains( - getCurrentListMapNoExtraKeys( - _keys, - _values, - mask, - extraKeys, - zeroValue, - minValue, - from, - defaultEntry - ), - Long.MinValue, - minValue, - k - ) - lemmaInListMapAfterAddingDiffThenInBefore( - k, - Long.MinValue, - minValue, - getCurrentListMapNoExtraKeys( - _keys, - _values, - mask, - extraKeys, - zeroValue, - minValue, - from, - defaultEntry - ) - ) - } - check(false) - } - } - } - } ensuring (_ => - (if (k != 0 && k != Long.MinValue) arrayContainsKey(_keys, k, from) - else if (k == 0) (extraKeys & 1) != 0 - else (extraKeys & 2) != 0) - ) - - // ------------------END---------------------------------------------------------------------------------------------------------------------------------- - // ------------------EQUIVALENCE BETWEEN LISTMAP AND ARRAY------------------------------------------------------------------------------------------------ - - // ------------------SEEKENTRY RELATED-------------------------------------------------------------------------------------------------------------------- - // ------------------BEGIN-------------------------------------------------------------------------------------------------------------------------------- - - @opaque - @inlineOnce - @pure - def lemmaArrayForallSeekEntryOrOpenFoundAlwaysTrueFor0Array(_keys: Array[Long], mask: Int, i: Int): Unit = { - require(validMask(mask)) - require(_keys.length == mask + 1) - require(_keys == Array.fill(mask + 1)(0L)) - require(i >= 0 && i <= _keys.length) - decreases(_keys.length - i) - - if (i < _keys.length) { - lemmaArrayForallSeekEntryOrOpenFoundAlwaysTrueFor0Array(_keys, mask, i + 1) - } - } ensuring (_ => arrayForallSeekEntryOrOpenFound(i)(_keys, mask)) - - @opaque - @inlineOnce - @pure - @ghost - def lemmaInListMapThenSeekEntryFinds[V]( - _keys: Array[Long], - _values: Array[ValueCell[V]], - mask: Int, - extraKeys: Int, - zeroValue: V, - minValue: V, - k: Long, - defaultEntry: Long => V - ): Unit = { - require(validMask(mask)) - require(_values.length == mask + 1) - require(_keys.length == _values.length) - require(mask >= 0) - require(extraKeys >= 0) - require(extraKeys <= 3) - require(arrayForallSeekEntryOrOpenFound(0)(_keys, mask)) - require(arrayNoDuplicates(_keys, 0)) - require( - getCurrentListMap(_keys, _values, mask, extraKeys, zeroValue, minValue, 0, defaultEntry) - .contains(k) - ) - require(validKeyInArray(k)) - - lemmaKeyInListMapIsInArray( - _keys, - _values, - mask, - extraKeys, - zeroValue, - minValue, - k, - defaultEntry - ) - assert(arrayContainsKey(_keys, k, 0)) - assert(arrayForallSeekEntryOrOpenFound(0)(_keys, mask)) - val i = arrayScanForKey(_keys, k, 0) - lemmaArrayForallSeekEntryOrOpenFoundFromSmallerThenFromBigger(_keys, mask, 0, i) - lemmaSeekEntryOrOpenFindsThenSeekEntryFinds(k, i, _keys, mask) - - } ensuring (_ => - (seekEntry(k)(_keys, mask) match { - case Found(index) => inRange(index, mask) && _keys(index) == k - case _ => false - }) - ) - - @opaque - @inlineOnce - @pure - @ghost - def lemmaNotInListMapThenSeekEntryFindsMissingBit[V]( - _keys: Array[Long], - _values: Array[ValueCell[V]], - mask: Int, - extraKeys: Int, - zeroValue: V, - minValue: V, - k: Long, - defaultEntry: Long => V - ): Unit = { - require(validMask(mask)) - require(_values.length == mask + 1) - require(_keys.length == _values.length) - require(mask >= 0) - require(extraKeys >= 0) - require(extraKeys <= 3) - require(arrayForallSeekEntryOrOpenFound(0)(_keys, mask)) - require(arrayNoDuplicates(_keys, 0)) - require(validKeyInArray(k)) - require( - !getCurrentListMap(_keys, _values, mask, extraKeys, zeroValue, minValue, 0, defaultEntry) - .contains(k) - ) - if (validKeyInArray(k)) { - if (arrayContainsKey(_keys, k, 0)) { - val i = arrayScanForKey(_keys, k, 0) - lemmaValidKeyInArrayIsInListMap( - _keys, - _values, - mask, - extraKeys, - zeroValue, - minValue, - i, - defaultEntry - ) - lemmaSeekEntryOrOpenFindsThenSeekEntryFinds(k, i, _keys, mask) - lemmaArrayForallSeekEntryOrOpenFoundFromSmallerThenFromBigger(_keys, mask, 0, i) - check(false) - } else { - seekEntry(k)(_keys, mask) match { - case Found(index) => { - // found but not in array --> Contradiction - lemmaValidKeyInArrayIsInListMap( - _keys, - _values, - mask, - extraKeys, - zeroValue, - minValue, - index, - defaultEntry - ) - check(false) - } - case _ => () - } - } - } - - } ensuring (_ => - (seekEntry(k)(_keys, mask) match { - case MissingZero(_) => true - case Undefined() => true - case _ => false - }) - ) - - @opaque - @inlineOnce - @pure - @ghost - def lemmaInListMapThenSeekEntryOrOpenFindsIt[V]( - _keys: Array[Long], - _values: Array[ValueCell[V]], - mask: Int, - extraKeys: Int, - zeroValue: V, - minValue: V, - k: Long, - defaultEntry: Long => V - ): Unit = { - require(validMask(mask)) - require(_values.length == mask + 1) - require(_keys.length == _values.length) - require(mask >= 0) - require(extraKeys >= 0) - require(extraKeys <= 3) - require(arrayForallSeekEntryOrOpenFound(0)(_keys, mask)) - require(arrayNoDuplicates(_keys, 0)) - require( - getCurrentListMap(_keys, _values, mask, extraKeys, zeroValue, minValue, 0, defaultEntry) - .contains(k) - ) - require(validKeyInArray(k)) - - lemmaKeyInListMapIsInArray( - _keys, - _values, - mask, - extraKeys, - zeroValue, - minValue, - k, - defaultEntry - ) - val i = arrayScanForKey(_keys, k, 0) - lemmaArrayForallSeekEntryOrOpenFoundFromSmallerThenFromBigger(_keys, mask, 0, i) - assert(arrayForallSeekEntryOrOpenFound(i)(_keys, mask)) - - } ensuring (_ => - (seekEntryOrOpen(k)(_keys, mask) match { - case Found(index) => inRange(index, mask) && _keys(index) == k - case _ => false - }) - ) - - @opaque - @inlineOnce - @pure - @ghost - def lemmaNotInListMapThenSeekEntryOrOpenFindsFreeOrNothing[V]( - _keys: Array[Long], - _values: Array[ValueCell[V]], - mask: Int, - extraKeys: Int, - zeroValue: V, - minValue: V, - k: Long, - defaultEntry: Long => V - ): Unit = { - require(validMask(mask)) - require(_values.length == mask + 1) - require(_keys.length == _values.length) - require(mask >= 0) - require(extraKeys >= 0) - require(extraKeys <= 3) - require(arrayForallSeekEntryOrOpenFound(0)(_keys, mask)) - require(arrayNoDuplicates(_keys, 0)) - require(validKeyInArray(k)) - require( - !getCurrentListMap(_keys, _values, mask, extraKeys, zeroValue, minValue, 0, defaultEntry) - .contains(k) - ) - - val seekEntryRes = seekEntryOrOpen(k)(_keys, mask) - seekEntryRes match { - case Found(index) => { - assert(_keys(index) == k) - assert(arrayContainsKey(_keys, k, index)) - lemmaArrayContainsFromImpliesContainsFromZero(_keys, k, index) - lemmaValidKeyInArrayIsInListMap( - _keys, - _values, - mask, - extraKeys, - zeroValue, - minValue, - index, - defaultEntry - ) - check(false) - } - case MissingZero(_) => { - if (arrayContainsKey(_keys, k, 0)) { - val i = arrayScanForKey(_keys, k, 0) - lemmaArrayContainsKeyThenInListMap( - _keys, - _values, - mask, - extraKeys, - zeroValue, - minValue, - k, - i, - defaultEntry - ) - check(false) - } - check(!arrayContainsKey(_keys, k, 0)) - } - case MissingVacant(_) => { - if (arrayContainsKey(_keys, k, 0)) { - val i = arrayScanForKey(_keys, k, 0) - lemmaArrayContainsKeyThenInListMap( - _keys, - _values, - mask, - extraKeys, - zeroValue, - minValue, - k, - i, - defaultEntry - ) - check(false) - } - check(!arrayContainsKey(_keys, k, 0)) - } - case Undefined() => () - } - - } ensuring (_ => { - val seekEntryRes = seekEntryOrOpen(k)(_keys, mask) - seekEntryRes match { - case MissingZero(index) => - inRange(index, mask) && _keys(index) == 0 && !arrayContainsKey(_keys, k, 0) - case MissingVacant(index) => - inRange(index, mask) && _keys(index) == Long.MinValue && !arrayContainsKey( - _keys, - k, - 0 - ) - case Undefined() => true - case _ => false - } - }) - - @opaque - @inlineOnce - @pure - @ghost - def lemmaSeekEntryOrOpenReturnsValidIndex[V]( - _keys: Array[Long], - _values: Array[ValueCell[V]], - mask: Int, - extraKeys: Int, - zeroValue: V, - minValue: V, - k: Long - ): Unit = { - require(validMask(mask)) - require(_values.length == mask + 1) - require(_keys.length == _values.length) - require(mask >= 0) - require(extraKeys >= 0) - require(extraKeys <= 3) - require(arrayForallSeekEntryOrOpenFound(0)(_keys, mask)) - require(arrayNoDuplicates(_keys, 0)) - require(validKeyInArray(k)) - - } ensuring (_ => - seekEntryOrOpen(k)(_keys, mask) match { - case Undefined() => true - case Found(index) => inRange(index, mask) - case MissingVacant(index) => inRange(index, mask) - case MissingZero(index) => inRange(index, mask) - case _ => false - } - ) - - @opaque - @inlineOnce - @pure - @ghost - def lemmaSeekEntryGivesInRangeIndex[V]( - _keys: Array[Long], - _values: Array[ValueCell[V]], - mask: Int, - extraKeys: Int, - zeroValue: V, - minValue: V, - k: Long - ): Unit = { - require(validMask(mask)) - require(_values.length == mask + 1) - require(_keys.length == _values.length) - require(mask >= 0) - require(extraKeys >= 0) - require(extraKeys <= 3) - require(arrayForallSeekEntryOrOpenFound(0)(_keys, mask)) - require(arrayNoDuplicates(_keys, 0)) - - require(validKeyInArray(k)) - - } ensuring (_ => - (seekEntry(k)(_keys, mask) match { - case Found(index) => inRange(index, mask) - case _ => true - }) - ) - - // ------------------END---------------------------------------------------------------------------------------------------------------------------------- - // ------------------SEEKENTRY RELATED-------------------------------------------------------------------------------------------------------------------- - - @opaque - @inlineOnce - @pure - @ghost - def lemmaPutLongMinValuePreservesForallSeekEntryOrOpenKey1Helper( - a: Array[Long], - i: Int, - j: Int, - x: Int, - index: Int, - vacantIndex: Int - )(implicit mask: Int): Unit = { - require(validMask(mask)) - require(a.length == mask + 1) - require(i >= 0) - require(i < a.length) - require(j >= 0) - require(j < a.length) - require(i != j) - require(validKeyInArray(a(i))) - require(validKeyInArray(a(j))) - require(arrayForallSeekEntryOrOpenFound(0)(a, mask)) - require(arrayNoDuplicates(a, 0)) - require(x >= 0 && x <= MAX_ITER) - require(index >= 0 && index < a.length) - require(vacantIndex >= 0 && vacantIndex < a.length) - require(a(vacantIndex) == Long.MinValue) - require( - seekKeyOrZeroReturnVacant(x, index, vacantIndex)(a(j), a, mask) == Found(j) - ) - - decreases(MAX_ITER - x) - if (a(index) == a(j)) { - // trivial - } else { - lemmaPutLongMinValuePreservesForallSeekEntryOrOpenKey1Helper( - a, - i, - j, - x + 1, - nextIndex(index, x + 1, mask), - vacantIndex - ) - } - - } ensuring (_ => - seekKeyOrZeroReturnVacant(x, index, vacantIndex)( - a.updated(i, Long.MinValue).apply(j), - a.updated(i, Long.MinValue), - mask - ) == seekKeyOrZeroReturnVacant(x, index, vacantIndex)( - a(j), - a, - mask - ) - ) - - @opaque - @inlineOnce - @pure - @ghost - def lemmaPutLongMinValuePreservesForallSeekEntryOrOpenKey1( - a: Array[Long], - i: Int, - j: Int, - x: Int, - index: Int, - resX: Int, - resIndex: Int - )(implicit mask: Int): Unit = { - require(validMask(mask)) - require(a.length == mask + 1) - require(i >= 0) - require(i < a.length) - require(j >= 0) - require(j < a.length) - require(i != j) - require(validKeyInArray(a(i))) - require(validKeyInArray(a(j))) - require(arrayForallSeekEntryOrOpenFound(0)(a, mask)) - require(arrayNoDuplicates(a, 0)) - require(x >= 0 && x <= MAX_ITER) - require(resX >= 0 && resX <= MAX_ITER) - require(index >= 0 && index < a.length) - require(resIndex >= 0 && resIndex < a.length) - - require( - seekKeyOrZeroOrLongMinValue(0, toIndex(a(j), mask))( - a(j), - a, - mask - ) == Intermediate( - false, - resIndex, - resX - ) - ) - require( - seekKeyOrZeroOrLongMinValue(x, index)(a(j), a, mask) == Intermediate( - false, - resIndex, - resX - ) - ) - - require( - seekKeyOrZeroOrLongMinValue(0, toIndex(a(j), mask))(a(j), a, mask) == - seekKeyOrZeroOrLongMinValue( - 0, - toIndex(a.updated(i, Long.MinValue).apply(j), mask) - )( - a.updated(i, Long.MinValue).apply(j), - a.updated(i, Long.MinValue), - mask - ) - ) - - decreases(MAX_ITER - x) - lemmaArrayForallSeekEntryOrOpenFoundFromSmallerThenFromBigger(a, mask, 0, j) - - check(seekEntry(a(j))(a, mask) == Found(j)) - val newArray = a.updated(i, Long.MinValue) - assert(a(j) == newArray(j)) - - if (x == resX) { - assert(index == resIndex) - if (a(index) == a(j)) { - assert(j == index) - // trivial - } else { - assert(a(index) == Long.MinValue) - check( - seekEntryOrOpen(a(j))(a, mask) == seekKeyOrZeroReturnVacant( - x, - index, - index - )( - a(j), - a, - mask - ) - ) - check( - seekEntryOrOpen(newArray(j))( - newArray, - mask - ) == seekKeyOrZeroReturnVacant( - x, - index, - index - )(newArray(j), newArray, mask) - ) - lemmaPutLongMinValuePreservesForallSeekEntryOrOpenKey1Helper(a, i, j, x, index, index) - } - } else { - lemmaPutLongMinValuePreservesForallSeekEntryOrOpenKey1( - a, - i, - j, - x + 1, - nextIndex(index, x + 1, mask), - resX, - resIndex - ) - } - - } ensuring (_ => - seekEntryOrOpen(a(j))(a, mask) == seekEntryOrOpen( - a.updated(i, Long.MinValue).apply(j) - )( - a.updated(i, Long.MinValue), - mask - ) - ) - - @opaque - @inlineOnce - @pure - @ghost - def lemmaPutLongMinValueIntermediateNotSameThenNewIsSmallerXAndAtI( - a: Array[Long], - i: Int, - j: Int, - x: Int, - index: Int, - intermediateBeforeX: Int, - intermediateBeforeIndex: Int, - intermediateAfterX: Int, - intermediateAfterIndex: Int, - undefinedAfter: Boolean - )(implicit mask: Int): Unit = { - require(validMask(mask)) - require(a.length == mask + 1) - require(i >= 0) - require(i < a.length) - require(j >= 0) - require(j < a.length) - require(i != j) - require(validKeyInArray(a(i))) - require(validKeyInArray(a(j))) - require(arrayForallSeekEntryOrOpenFound(0)(a, mask)) - require(arrayNoDuplicates(a, 0)) - require(x >= 0 && x <= MAX_ITER) - require(intermediateBeforeX >= 0 && intermediateBeforeX <= MAX_ITER) - require(index >= 0 && index < a.length) - require(intermediateBeforeIndex >= 0 && intermediateBeforeIndex < a.length) - - require( - seekKeyOrZeroOrLongMinValue(0, toIndex(a(j), mask))( - a(j), - a, - mask - ) == Intermediate( - false, - intermediateBeforeIndex, - intermediateBeforeX - ) - ) - require( - seekKeyOrZeroOrLongMinValue( - 0, - toIndex(a.updated(i, Long.MinValue).apply(j), mask) - )( - a.updated(i, Long.MinValue).apply(j), - a.updated(i, Long.MinValue), - mask - ) == - Intermediate(undefinedAfter, intermediateAfterIndex, intermediateAfterX) - ) - - require( - seekKeyOrZeroOrLongMinValue(x, index)(a(j), a, mask) == Intermediate( - false, - intermediateBeforeIndex, - intermediateBeforeX - ) - ) - require( - seekKeyOrZeroOrLongMinValue(x, index)( - a.updated(i, Long.MinValue).apply(j), - a.updated(i, Long.MinValue), - mask - ) == - Intermediate(undefinedAfter, intermediateAfterIndex, intermediateAfterX) - ) - - require( - Intermediate(false, intermediateBeforeIndex, intermediateBeforeX) != Intermediate( - undefinedAfter, - intermediateAfterIndex, - intermediateAfterX - ) - ) - - decreases(MAX_ITER - x) - - lemmaArrayForallSeekEntryOrOpenFoundFromSmallerThenFromBigger(a, mask, 0, j) - check(seekEntryOrOpen(a(j))(a, mask) == Found(j)) - - if (a(index) == Long.MinValue) { - check(false) - } else if (a(index) == 0) { - check(false) - } else if (a(index) == a(j)) { - check(false) - } else if (a.updated(i, Long.MinValue).apply(index) == Long.MinValue) { - check(a(index) != Long.MinValue) - check(a(index) != 0) - check(a(index) != a(j)) - check(index == intermediateAfterIndex) - check(intermediateAfterIndex == i) - check(intermediateAfterX < intermediateBeforeX) - } else { - lemmaPutLongMinValueIntermediateNotSameThenNewIsSmallerXAndAtI( - a, - i, - j, - x + 1, - nextIndex(index, x + 1, mask), - intermediateBeforeX, - intermediateBeforeIndex, - intermediateAfterX, - intermediateAfterIndex, - undefinedAfter - ) - } - - } ensuring (_ => !undefinedAfter && intermediateAfterIndex == i && intermediateAfterX < intermediateBeforeX) - - @opaque - @inlineOnce - @pure - @ghost - def lemmaPutLongMinValuePreservesSeekKeyOrZeroReturnVacant( - a: Array[Long], - i: Int, - j: Int, - x: Int, - index: Int, - vacantBefore: Int, - vacantAfter: Int - )(implicit mask: Int): Unit = { - require(validMask(mask)) - require(a.length == mask + 1) - require(i >= 0) - require(i < a.length) - require(j >= 0) // 5 - require(j < a.length) - require(i != j) - require(validKeyInArray(a(i))) - require(validKeyInArray(a(j))) - require(arrayForallSeekEntryOrOpenFound(0)(a, mask)) - require(arrayNoDuplicates(a, 0)) - require(x >= 0 && x <= MAX_ITER) - require(vacantBefore >= 0 && vacantBefore < a.length) - require(vacantAfter >= 0 && vacantAfter < a.length) - require(a(vacantBefore) == Long.MinValue) - require(a.updated(i, Long.MinValue).apply(vacantAfter) == Long.MinValue) - require(vacantAfter == i) - require(index >= 0 && index < a.length) - require( - seekKeyOrZeroReturnVacant(x, index, vacantBefore)(a(j), a, mask) == Found(j) - ) - - decreases(MAX_ITER - x) - - if (a(index) == a(j)) { - check(j == index) - // trivial - } else { - lemmaPutLongMinValuePreservesSeekKeyOrZeroReturnVacant( - a, - i, - j, - x + 1, - nextIndex(index, x + 1, mask), - vacantBefore, - vacantAfter - ) - } - - } ensuring (_ => - seekKeyOrZeroReturnVacant(x, index, vacantAfter)( - a.updated(i, Long.MinValue).apply(j), - a.updated(i, Long.MinValue), - mask - ) == Found(j) - ) - - @opaque - @inlineOnce - @pure - @ghost - def lemmaPutLongMinValuePreservesForallSeekEntryOrOpenKey2Helper( - a: Array[Long], - i: Int, - j: Int, - x: Int, - index: Int, - intermediateBeforeX: Int, - intermediateBeforeIndex: Int, - intermediateAfterX: Int, - intermediateAfterIndex: Int - )(implicit mask: Int): Unit = { - - require(validMask(mask)) - require(a.length == mask + 1) - require(i >= 0) - require(i < a.length) - require(j >= 0) - require(j < a.length) - require(i != j) - require(validKeyInArray(a(i))) - require(validKeyInArray(a(j))) - require(arrayForallSeekEntryOrOpenFound(0)(a, mask)) - require(arrayNoDuplicates(a, 0)) - require(x >= 0 && x <= MAX_ITER) - require(intermediateBeforeX >= 0 && intermediateBeforeX <= MAX_ITER) - require(intermediateAfterX >= 0 && intermediateAfterX <= MAX_ITER) - require(index >= 0 && index < a.length) - require(intermediateBeforeIndex >= 0 && intermediateBeforeIndex < a.length) - require(intermediateAfterIndex >= 0 && intermediateAfterIndex < a.length) - - require(intermediateAfterX < intermediateBeforeX) - require(a.updated(i, Long.MinValue).apply(intermediateAfterIndex) == Long.MinValue) - - require( - seekKeyOrZeroOrLongMinValue(0, toIndex(a(j), mask))( - a(j), - a, - mask - ) == Intermediate( - false, - intermediateBeforeIndex, - intermediateBeforeX - ) - ) - require( - seekKeyOrZeroOrLongMinValue(x, index)(a(j), a, mask) == Intermediate( - false, - intermediateBeforeIndex, - intermediateBeforeX - ) - ) - require( - seekKeyOrZeroOrLongMinValue( - 0, - toIndex(a.updated(i, Long.MinValue).apply(j), mask) - )( - a.updated(i, Long.MinValue).apply(j), - a.updated(i, Long.MinValue), - mask - ) == - Intermediate(false, intermediateAfterIndex, intermediateAfterX) - ) - - require( - if (x <= intermediateAfterX) - seekKeyOrZeroOrLongMinValue( - 0, - toIndex(a.updated(i, Long.MinValue).apply(j), mask) - )( - a.updated(i, Long.MinValue).apply(j), - a.updated(i, Long.MinValue), - mask - ) == - seekKeyOrZeroOrLongMinValue(x, index)( - a.updated(i, Long.MinValue).apply(j), - a.updated(i, Long.MinValue), - mask - ) - else - seekKeyOrZeroReturnVacant(x, index, intermediateAfterIndex)( - a.updated(i, Long.MinValue).apply(j), - a.updated(i, Long.MinValue), - mask - ) == seekEntryOrOpen( - a.updated(i, Long.MinValue).apply(j) - )(a.updated(i, Long.MinValue), mask) - ) - require(x <= intermediateBeforeX) - require(intermediateAfterIndex == i) - decreases(MAX_ITER - x) - - lemmaArrayForallSeekEntryOrOpenFoundFromSmallerThenFromBigger(a, mask, 0, j) - check(seekEntryOrOpen(a(j))(a, mask) == Found(j)) - check(a(intermediateBeforeIndex) == Long.MinValue || a(intermediateBeforeIndex) == a(j)) - - if (a(index) == a.updated(i, Long.MinValue).apply(index) && a(index) == a(j)) { - check( - seekEntryOrOpen(a(j))(a, mask) == seekEntryOrOpen( - a.updated(i, Long.MinValue).apply(j) - )( - a.updated(i, Long.MinValue), - mask - ) - ) - } else if (a(index) == Long.MinValue) { - check( - seekEntryOrOpen(a(j))(a, mask) == seekKeyOrZeroReturnVacant( - x, - index, - index - )( - a(j), - a, - mask - ) - ) - check(index == intermediateBeforeIndex) - check(x == intermediateBeforeX) - lemmaPutLongMinValuePreservesSeekKeyOrZeroReturnVacant( - a, - i, - j, - x, - index, - intermediateBeforeIndex, - intermediateAfterIndex - )(mask) - } else if (x == intermediateAfterX) { - check(index == intermediateAfterIndex) - check(a.updated(i, Long.MinValue).apply(index) == Long.MinValue) - check( - seekEntryOrOpen(a.updated(i, Long.MinValue).apply(j))( - a.updated(i, Long.MinValue), - mask - ) == seekKeyOrZeroReturnVacant(x, index, intermediateAfterIndex)( - a.updated(i, Long.MinValue).apply(j), - a.updated(i, Long.MinValue), - mask - ) - ) - lemmaPutLongMinValuePreservesForallSeekEntryOrOpenKey2Helper( - a, - i, - j, - x + 1, - nextIndex(index, x + 1, mask), - intermediateBeforeX, - intermediateBeforeIndex, - intermediateAfterX, - intermediateAfterIndex - ) - } else { - lemmaPutLongMinValuePreservesForallSeekEntryOrOpenKey2Helper( - a, - i, - j, - x + 1, - nextIndex(index, x + 1, mask), - intermediateBeforeX, - intermediateBeforeIndex, - intermediateAfterX, - intermediateAfterIndex - ) - - } - - } ensuring (_ => - seekEntryOrOpen(a(j))(a, mask) == seekEntryOrOpen( - a.updated(i, Long.MinValue).apply(j) - )( - a.updated(i, Long.MinValue), - mask - ) - ) - - @opaque - @inlineOnce - @pure - @ghost - def lemmaPutLongMinValuePreservesForallSeekEntryOrOpenKey2(a: Array[Long], i: Int, j: Int)(implicit - mask: Int - ): Unit = { - require(validMask(mask)) - require(a.length == mask + 1) - require(i >= 0) - require(i < a.length) - require(j >= 0) - require(j < a.length) - require(i != j) - require(validKeyInArray(a(i))) - require(validKeyInArray(a(j))) - require(arrayForallSeekEntryOrOpenFound(0)(a, mask)) - require(arrayNoDuplicates(a, 0)) - val intermediateBefore = - seekKeyOrZeroOrLongMinValue(0, toIndex(a(j), mask))(a(j), a, mask) - - lemmaArrayForallSeekEntryOrOpenFoundFromSmallerThenFromBigger(a, mask, 0, j) - check(seekEntryOrOpen(a(j))(a, mask) == Found(j)) - intermediateBefore match { - case Intermediate(undefined, intermediateBeforeIndex, intermediateBeforeX) => { - if (undefined) { - check(false) - } else { - if ( - seekKeyOrZeroOrLongMinValue(0, toIndex(a(j), mask))(a(j), a, mask) == - seekKeyOrZeroOrLongMinValue( - 0, - toIndex(a.updated(i, Long.MinValue).apply(j), mask) - )( - a.updated(i, Long.MinValue).apply(j), - a.updated(i, Long.MinValue), - mask - ) - ) { - lemmaPutLongMinValuePreservesForallSeekEntryOrOpenKey1( - a, - i, - j, - 0, - toIndex(a(j), mask), - intermediateBeforeX, - intermediateBeforeIndex - )(mask) - } else { - val intermediateAfter = seekKeyOrZeroOrLongMinValue( - 0, - toIndex(a.updated(i, Long.MinValue).apply(j), mask) - )(a.updated(i, Long.MinValue).apply(j), a.updated(i, Long.MinValue), mask) - - intermediateAfter match { - case Intermediate(undefined, intermediateAfterIndex, intermediateAfterX) => { - lemmaPutLongMinValueIntermediateNotSameThenNewIsSmallerXAndAtI( - a, - i, - j, - 0, - toIndex(a(j), mask), - intermediateBeforeX, - intermediateBeforeIndex, - intermediateAfterX, - intermediateAfterIndex, - undefined - ) - check(!undefined) - check(intermediateAfterIndex == i) - check(a.updated(i, Long.MinValue).apply(intermediateAfterIndex) == Long.MinValue) - check( - seekEntryOrOpen(a.updated(i, Long.MinValue).apply(j))( - a.updated(i, Long.MinValue), - mask - ) == - seekKeyOrZeroReturnVacant( - intermediateAfterX, - intermediateAfterIndex, - intermediateAfterIndex - )(a(j), a.updated(i, Long.MinValue), mask) - ) - lemmaPutLongMinValuePreservesForallSeekEntryOrOpenKey2Helper( - a, - i, - j, - 0, - toIndex(a(j), mask), - intermediateBeforeX, - intermediateBeforeIndex, - intermediateAfterX, - intermediateAfterIndex - )(mask) - } - case _ => check(false) - } - } - } - } - case _ => check(false) - } - - } ensuring (_ => - seekEntryOrOpen(a(j))(a, mask) == seekEntryOrOpen( - a.updated(i, Long.MinValue).apply(j) - )( - a.updated(i, Long.MinValue), - mask - ) - ) - - @opaque - @inlineOnce - @pure - @ghost - def lemmaPutLongMinValuePreservesForallSeekEntryOrOpenStartIndex( - a: Array[Long], - i: Int, - startIndex: Int - )(implicit - mask: Int - ): Unit = { - require(validMask(mask)) - require(a.length == mask + 1) - require(i >= 0) - require(i < a.length) - require(startIndex >= 0) - require(startIndex < a.length) - require(validKeyInArray(a(i))) - require(arrayNoDuplicates(a, 0)) - require(arrayForallSeekEntryOrOpenFound(0)(a, mask)) - - decreases(a.length - startIndex) - - assert(a(i) != 0 && a(i) != Long.MinValue) - val newArray = a.updated(i, Long.MinValue) - - if (startIndex != i && validKeyInArray(a(startIndex))) { - lemmaPutLongMinValuePreservesForallSeekEntryOrOpenKey2(a, i, startIndex)(mask) - lemmaArrayForallSeekEntryOrOpenFoundFromSmallerThenFromBigger(a, mask, 0, startIndex) - } - if (startIndex < a.length - 1) { - lemmaPutLongMinValuePreservesForallSeekEntryOrOpenStartIndex(a, i, startIndex + 1) - } - - } ensuring (_ => arrayForallSeekEntryOrOpenFound(startIndex)(a.updated(i, Long.MinValue), mask)) - - @opaque - @inlineOnce - @pure - @ghost - def lemmaPutLongMinValuePreservesForallSeekEntryOrOpen(a: Array[Long], i: Int)(implicit - mask: Int - ): Unit = { - require(validMask(mask)) - require(a.length == mask + 1) - require(i >= 0) - require(i < a.length) - require(validKeyInArray(a(i))) - require(arrayNoDuplicates(a, 0)) - require(arrayForallSeekEntryOrOpenFound(0)(a, mask)) - - assert(a(i) != 0 && a(i) != Long.MinValue) - lemmaPutLongMinValuePreservesForallSeekEntryOrOpenStartIndex(a, i, 0)(mask) - - } ensuring (_ => arrayForallSeekEntryOrOpenFound(0)(a.updated(i, Long.MinValue), mask)) - - @opaque - @inlineOnce - @pure - @ghost - def lemmaPutValidKeyPreservesseekKeyOrZeroReturnVacant( - a: Array[Long], - i: Int, - k: Long, - j: Int, - x: Int, - index: Int, - vacantSpotIndex: Int - )(implicit mask: Int): Unit = { - require(validMask(mask)) - require(a.length == mask + 1) - require(i >= 0) - require(i < a.length) - require(j >= 0) - require(j < a.length) - require(i != j) - require(validKeyInArray(a(j))) - require(validKeyInArray(k)) - require(!arrayContainsKey(a, k, 0)) - require( - seekEntryOrOpen(k)(a, mask) == MissingZero(i) || seekEntryOrOpen(k)( - a, - mask - ) == MissingVacant( - i - ) - ) - require(arrayForallSeekEntryOrOpenFound(0)(a, mask)) - require(arrayNoDuplicates(a, 0)) - require(index >= 0 && index < mask + 1) - require(x <= MAX_ITER && x >= 0) - require(vacantSpotIndex >= 0 && vacantSpotIndex < mask + 1) - require(a(vacantSpotIndex) == Long.MinValue) - require(a.updated(i, k).apply(vacantSpotIndex) == Long.MinValue) - require( - seekKeyOrZeroReturnVacant(x, index, vacantSpotIndex)( - a(j), - a, - mask - ) == Found(j) - ) - - decreases(MAX_ITER - x) - - val resBefore = - seekKeyOrZeroReturnVacant(x, index, vacantSpotIndex)(a(j), a, mask) - - if (x >= MAX_ITER) { - check(false) - } else if (a(index) == a(j)) { - assert(index == j) - - } else if (a(index) == 0) { - check(false) - } else { - assert(index != j) - lemmaPutValidKeyPreservesseekKeyOrZeroReturnVacant( - a, - i, - k, - j, - x + 1, - nextIndex(index, x + 1, mask), - vacantSpotIndex - ) - check( - seekKeyOrZeroReturnVacant(x, index, vacantSpotIndex)(a(j), a, mask) == - seekKeyOrZeroReturnVacant( - x + 1, - nextIndex(index, x + 1, mask), - vacantSpotIndex - )( - a(j), - a, - mask - ) - ) - - if ( - seekKeyOrZeroReturnVacant(x, index, vacantSpotIndex)( - a.updated(i, k).apply(j), - a.updated(i, k), - mask - ) == Undefined() - ) { - check(false) - } - if ( - seekKeyOrZeroReturnVacant(x, index, vacantSpotIndex)( - a.updated(i, k).apply(j), - a.updated(i, k), - mask - ) == Found(index) - ) { - check(a.updated(i, k).apply(index) == a(j)) - val q = a(j) - val newArray = a.updated(i, k) - assert(q == a.updated(i, k).apply(j)) - assert(q == a.updated(i, k).apply(index)) - if (j < index) { - check(arrayContainsKey(newArray, q, j)) - check(arrayContainsKey(newArray, q, index)) - lemmaArrayContainsFromImpliesContainsFromSmaller(newArray, q, index, j + 1) - check(arrayContainsKey(newArray, q, j + 1)) - check(arrayNoDuplicates(a, 0)) - lemmaPutNewValidKeyPreservesNoDuplicate(a, k, i, 0, List()) - check(arrayNoDuplicates(newArray, 0)) - lemmaNoDuplicateFromThenFromBigger(newArray, 0, j) - lemmaArrayNoDuplicateThenKeysContainedNotEqual(newArray, q, j, Nil()) - - check(false) - } - if (index < j) { - check(arrayContainsKey(newArray, q, j)) - check(arrayContainsKey(newArray, q, index)) - lemmaArrayContainsFromImpliesContainsFromSmaller(newArray, q, j, index + 1) - check(arrayContainsKey(newArray, q, index + 1)) - check(arrayNoDuplicates(a, 0)) - lemmaPutNewValidKeyPreservesNoDuplicate(a, k, i, 0, List()) - check(arrayNoDuplicates(newArray, 0)) - lemmaNoDuplicateFromThenFromBigger(newArray, 0, index) - lemmaArrayNoDuplicateThenKeysContainedNotEqual(newArray, q, index, Nil()) - - check(false) - } - check(index == j) - check(false) - } - if ( - seekKeyOrZeroReturnVacant(x, index, vacantSpotIndex)( - a.updated(i, k).apply(j), - a.updated(i, k), - mask - ) == MissingVacant(vacantSpotIndex) - ) { - check(a.updated(i, k).apply(index) == 0) - check(false) - } - - } - - } ensuring (_ => - seekKeyOrZeroReturnVacant(x, index, vacantSpotIndex)(a(j), a, mask) == - seekKeyOrZeroReturnVacant(x, index, vacantSpotIndex)( - a.updated(i, k).apply(j), - a.updated(i, k), - mask - ) - ) - - @opaque - @inlineOnce - @pure - @ghost - def lemmaPutValidKeyPreservesForallSeekEntryOrOpenKey1(a: Array[Long], i: Int, k: Long, j: Int)(implicit - mask: Int - ): Unit = { - require(validMask(mask)) - require(a.length == mask + 1) - require(i >= 0) - require(i < a.length) - require(j >= 0) - require(j < a.length) - require(i != j) - require(validKeyInArray(a(j))) - require(validKeyInArray(k)) - require(!arrayContainsKey(a, k, 0)) - require( - seekEntryOrOpen(k)(a, mask) == MissingZero(i) || seekEntryOrOpen(k)( - a, - mask - ) == MissingVacant( - i - ) - ) - require(arrayForallSeekEntryOrOpenFound(0)(a, mask)) - require(arrayNoDuplicates(a, 0)) - require( - seekKeyOrZeroOrLongMinValue(0, toIndex(a(j), mask))(a(j), a, mask) == - seekKeyOrZeroOrLongMinValue(0, toIndex(a.updated(i, k).apply(j), mask))( - a.updated(i, k).apply(j), - a.updated(i, k), - mask - ) - ) - - lemmaArrayForallSeekEntryOrOpenFoundFromSmallerThenFromBigger(a, mask, 0, j) - check(seekEntryOrOpen(a(j))(a, mask) == Found(j)) - - val intermediateBefore = - seekKeyOrZeroOrLongMinValue(0, toIndex(a(j), mask))(a(j), a, mask) - val intermediateAfter = - seekKeyOrZeroOrLongMinValue(0, toIndex(a.updated(i, k).apply(j), mask))( - a.updated(i, k).apply(j), - a.updated(i, k), - mask - ) - assert(intermediateBefore == intermediateAfter) - intermediateBefore match { - case Intermediate(undefined, index, x) if (undefined) => {} - case Intermediate(undefined, index, x) if (!undefined) => { - if (a(index) == a(j)) {} else if (a(index) == 0) { - check(seekEntryOrOpen(a(j))(a, mask) == MissingZero(index)) - check(arrayForallSeekEntryOrOpenFound(0)(a, mask)) - lemmaArrayForallSeekEntryOrOpenFoundFromSmallerThenFromBigger(a, mask, 0, j) - check(false) - } else { - check( - seekEntryOrOpen(a(j))(a, mask) == seekKeyOrZeroReturnVacant( - x, - index, - index - )( - a(j), - a, - mask - ) - ) - check( - seekEntryOrOpen(a.updated(i, k).apply(j))(a.updated(i, k), mask) == - seekKeyOrZeroReturnVacant(x, index, index)( - a.updated(i, k).apply(j), - a.updated(i, k), - mask - ) - ) - lemmaPutValidKeyPreservesseekKeyOrZeroReturnVacant( - a, - i, - k, - j, - x, - index, - index - )(mask) - - } - } - } - - } ensuring (_ => - seekEntryOrOpen(a(j))(a, mask) == seekEntryOrOpen(a.updated(i, k).apply(j))( - a.updated(i, k), - mask - ) - ) - - @opaque - @inlineOnce - @pure - @ghost - def lemmaseekKeyOrZeroOrLongMinValueFoundKeyThenSameAfterChangingI( - a: Array[Long], - i: Int, - k: Long, - j: Int, - index: Int, - x: Int, - resIndex: Int, - resX: Int - )(implicit mask: Int): Unit = { - require(validMask(mask)) - require(a.length == mask + 1) - require(i >= 0) - require(i < a.length) - require(j >= 0) - require(j < a.length) - require(i != j) - require(validKeyInArray(a(j))) - require(validKeyInArray(k)) - require(!arrayContainsKey(a, k, 0)) - require( - seekEntryOrOpen(k)(a, mask) == MissingZero(i) || seekEntryOrOpen(k)( - a, - mask - ) == MissingVacant( - i - ) - ) - require(arrayForallSeekEntryOrOpenFound(0)(a, mask)) - require(arrayNoDuplicates(a, 0)) - require(resX <= MAX_ITER) - require(x <= resX) - require(x >= 0) - require(index >= 0 && index < a.length) - require(resIndex >= 0 && resIndex < a.length) - require(a(resIndex) == a(j)) - require( - seekKeyOrZeroOrLongMinValue(x, index)(a(j), a, mask) == Intermediate( - false, - resIndex, - resX - ) - ) - require( - seekKeyOrZeroOrLongMinValue(0, toIndex(a(j), mask))( - a(j), - a, - mask - ) == seekKeyOrZeroOrLongMinValue(x, index)(a(j), a, mask) - ) - - decreases(MAX_ITER - x) - if (a(index) == 0) { - check(false) - } else if (a(index) == Long.MinValue) { - check(false) - } else if (a(index) == a(j)) { - check(index == resIndex) - } else { - lemmaseekKeyOrZeroOrLongMinValueFoundKeyThenSameAfterChangingI( - a, - i, - k, - j, - nextIndex(index, x + 1, mask), - x + 1, - resIndex, - resX - ) - } - - } ensuring (_ => - seekKeyOrZeroOrLongMinValue(x, index)( - a.updated(i, k).apply(j), - a.updated(i, k), - mask - ) == Intermediate(false, resIndex, resX) - ) - - @opaque - @inlineOnce - @pure - @ghost - def lemmaPutValidKeyPreservesForallSeekEntryOrOpenKey2AfterFindingLongMinValueLater( - a: Array[Long], - i: Int, - k: Long, - j: Int, - index: Int, - x: Int, - vacantBefore: Int, - vacantAfter: Int - )(implicit mask: Int): Unit = { - require(validMask(mask)) - require(a.length == mask + 1) - require(i >= 0) - require(i < a.length) - require(j >= 0) - require(j < a.length) - require(i != j) - require(validKeyInArray(a(j))) - require(validKeyInArray(k)) - require(!arrayContainsKey(a, k, 0)) - require( - seekEntryOrOpen(k)(a, mask) == MissingZero(i) || seekEntryOrOpen(k)( - a, - mask - ) == MissingVacant( - i - ) - ) - require(arrayForallSeekEntryOrOpenFound(0)(a, mask)) - require(arrayNoDuplicates(a, 0)) - require(x <= MAX_ITER) - require(x >= 0) - require(index >= 0 && index < a.length) - require(vacantBefore == i) - require(a(vacantBefore) == Long.MinValue) - require(vacantAfter >= 0 && vacantAfter < a.length) - require(a(vacantAfter) == Long.MinValue) - require(vacantAfter != vacantBefore) - require( - seekEntryOrOpen(a.updated(i, k).apply(j))( - a.updated(i, k), - mask - ) == seekKeyOrZeroReturnVacant(x, index, vacantAfter)( - a.updated(i, k).apply(j), - a.updated(i, k), - mask - ) - ) - require( - seekEntryOrOpen(a(j))(a, mask) == seekKeyOrZeroReturnVacant( - x, - index, - vacantBefore - )( - a(j), - a, - mask - ) - ) - require( - seekKeyOrZeroReturnVacant(x, index, vacantBefore)(a(j), a, mask) == Found(j) - ) - - decreases(MAX_ITER - x) - - if (a(index) == a(j)) { - check( - seekKeyOrZeroReturnVacant(x, index, vacantAfter)( - a.updated(i, k).apply(j), - a.updated(i, k), - mask - ) == Found(index) - ) - check( - seekKeyOrZeroReturnVacant(x, index, vacantAfter)( - a.updated(i, k).apply(j), - a.updated(i, k), - mask - ) == Found(j) - ) - } else { - lemmaPutValidKeyPreservesForallSeekEntryOrOpenKey2AfterFindingLongMinValueLater( - a, - i, - k, - j, - nextIndex(index, x + 1, mask), - x + 1, - vacantBefore, - vacantAfter - ) - check( - seekKeyOrZeroReturnVacant(x, index, vacantAfter)( - a.updated(i, k).apply(j), - a.updated(i, k), - mask - ) == Found(j) - ) - } - - } ensuring (_ => - seekKeyOrZeroReturnVacant(x, index, vacantAfter)( - a.updated(i, k).apply(j), - a.updated(i, k), - mask - ) == seekKeyOrZeroReturnVacant(x, index, vacantBefore)(a(j), a, mask) - ) - - @opaque - @inlineOnce - @pure - @ghost - def lemmaPutValidKeyPreservesVacantIsAtI( - a: Array[Long], - i: Int, - k: Long, - j: Int, - index: Int, - x: Int, - resIntermediateIndex: Int, - resIntermediateX: Int - )(implicit mask: Int): Unit = { - require(validMask(mask)) - require(a.length == mask + 1) - require(i >= 0) - require(i < a.length) - require(j >= 0) - require(j < a.length) - require(i != j) - require(validKeyInArray(a(j))) - require(validKeyInArray(k)) - require(!arrayContainsKey(a, k, 0)) - require( - seekEntryOrOpen(k)(a, mask) == MissingZero(i) || seekEntryOrOpen(k)( - a, - mask - ) == MissingVacant( - i - ) - ) - require(arrayForallSeekEntryOrOpenFound(0)(a, mask)) - require(arrayNoDuplicates(a, 0)) - require(x <= MAX_ITER) - require(x >= 0) - require(resIntermediateX <= MAX_ITER) - require(index >= 0 && index < a.length) - require(resIntermediateIndex >= 0 && resIntermediateIndex < a.length) - require( - seekKeyOrZeroOrLongMinValue(0, toIndex(a(j), mask))( - a(j), - a, - mask - ) == Intermediate( - false, - resIntermediateIndex, - resIntermediateX - ) - ) - require(resIntermediateIndex == i) - require(a(resIntermediateIndex) == Long.MinValue) - require( - if (x <= resIntermediateX) - seekKeyOrZeroOrLongMinValue(x, index)(a(j), a, mask) == Intermediate( - false, - resIntermediateIndex, - resIntermediateX - ) - else - seekKeyOrZeroReturnVacant(x, index, resIntermediateIndex)( - a(j), - a, - mask - ) == Found(j) - ) - require( - seekKeyOrZeroOrLongMinValue(0, toIndex(a.updated(i, k).apply(j), mask))( - a.updated(i, k).apply(j), - a.updated(i, k), - mask - ) == - seekKeyOrZeroOrLongMinValue(x, index)( - a.updated(i, k).apply(j), - a.updated(i, k), - mask - ) - ) - - decreases(MAX_ITER - x) - - lemmaArrayForallSeekEntryOrOpenFoundFromSmallerThenFromBigger(a, mask, 0, j) - check(seekEntryOrOpen(a(j))(a, mask) == Found(j)) - check( - seekKeyOrZeroReturnVacant( - resIntermediateX, - resIntermediateIndex, - resIntermediateIndex - )( - a(j), - a, - mask - ) == Found(j) - ) - - val intermediateAfter = - seekKeyOrZeroOrLongMinValue(x, index)( - a.updated(i, k).apply(j), - a.updated(i, k), - mask - ) - intermediateAfter match { - case Intermediate(undefined, indexIntermediateAfter, xIntermediateAfter) => { - if (x < xIntermediateAfter) { - val nextIndexVal = nextIndex(index, x + 1, mask) - val nextX = x + 1 - - if (nextX <= resIntermediateX) { - check( - if (nextX <= resIntermediateX) - seekKeyOrZeroOrLongMinValue(nextX, nextIndexVal)(a(j), a, mask) == Intermediate( - false, - resIntermediateIndex, - resIntermediateX - ) - else - seekKeyOrZeroReturnVacant(nextX, nextIndexVal, resIntermediateIndex)( - a(j), - a, - mask - ) == Found(j) - ) - } else { - if ( - seekKeyOrZeroReturnVacant(x, index, resIntermediateIndex)( - a(j), - a, - mask - ) == Found(j) - ) { - check( - seekKeyOrZeroReturnVacant( - nextX, - nextIndexVal, - resIntermediateIndex - )( - a(j), - a, - mask - ) == Found(j) - ) - } else { - check( - seekKeyOrZeroReturnVacant( - nextX, - nextIndexVal, - resIntermediateIndex - )( - a(j), - a, - mask - ) == Found(j) - ) - } - } - lemmaPutValidKeyPreservesVacantIsAtI( - a, - i, - k, - j, - nextIndexVal, - nextX, - resIntermediateIndex, - resIntermediateX - ) - } else { - assert(x == xIntermediateAfter) - assert(index == indexIntermediateAfter) - assert( - seekKeyOrZeroReturnVacant(x, index, resIntermediateIndex)( - a(j), - a, - mask - ) == Found(j) - ) - if (a.updated(i, k).apply(index) == a.updated(i, k).apply(j)) { - assert(!undefined) - check( - seekEntryOrOpen(a.updated(i, k).apply(j))(a.updated(i, k), mask) == Found( - index - ) - ) - } else { - if (a.updated(i, k).apply(index) == 0) { - check( - seekEntryOrOpen(a(j))( - a, - mask - ) == seekKeyOrZeroReturnVacant( - x, - index, - resIntermediateIndex - )(a(j), a, mask) - ) - check( - seekKeyOrZeroReturnVacant(x, index, resIntermediateIndex)( - a(j), - a, - mask - ) == MissingVacant(resIntermediateIndex) - ) - check(false) - } - check(a.updated(i, k).apply(index) == Long.MinValue) - check( - seekEntryOrOpen(a.updated(i, k).apply(j))( - a.updated(i, k), - mask - ) == seekKeyOrZeroReturnVacant(x, index, index)( - a.updated(i, k).apply(j), - a.updated(i, k), - mask - ) - ) - check( - seekEntryOrOpen(a(j))( - a, - mask - ) == seekKeyOrZeroReturnVacant( - x, - index, - resIntermediateIndex - )(a(j), a, mask) - ) - check( - seekKeyOrZeroReturnVacant(x, index, resIntermediateIndex)( - a(j), - a, - mask - ) == Found(j) - ) - - lemmaPutValidKeyPreservesForallSeekEntryOrOpenKey2AfterFindingLongMinValueLater( - a, - i, - k, - j, - index, - x, - resIntermediateIndex, - index - ) - } - } - } - case _ => check(false) - } - - } ensuring (_ => seekEntryOrOpen(a.updated(i, k).apply(j))(a.updated(i, k), mask) == Found(j)) - - @opaque - @inlineOnce - @pure - @ghost - def lemmaseekKeyOrZeroOrLongMinValueThenChangedAtReturnedIndex( - a: Array[Long], - i: Int, - k: Long, - j: Int, - index: Int, - x: Int, - resIndex: Int, - resX: Int - )(implicit mask: Int): Unit = { - require(validMask(mask)) - require(a.length == mask + 1) - require(i >= 0) - require(i < a.length) - require(j >= 0) - require(j < a.length) - require(i != j) - require(validKeyInArray(a(j))) - require(validKeyInArray(k)) - require(!arrayContainsKey(a, k, 0)) - require( - seekEntryOrOpen(k)(a, mask) == MissingVacant(i) || seekEntryOrOpen(k)( - a, - mask - ) == MissingZero( - i - ) - ) - require(arrayForallSeekEntryOrOpenFound(0)(a, mask)) - require(arrayNoDuplicates(a, 0)) - require(x <= resX) - require(x >= 0) - require(resX <= MAX_ITER) - require(index >= 0 && index < a.length) - require(resIndex >= 0 && resIndex < a.length) - require( - seekKeyOrZeroOrLongMinValue(0, toIndex(a(j), mask))( - a(j), - a, - mask - ) == Intermediate( - false, - resIndex, - resX - ) - ) - require( - seekKeyOrZeroOrLongMinValue(x, index)(a(j), a, mask) == Intermediate( - false, - resIndex, - resX - ) - ) - require( - seekKeyOrZeroOrLongMinValue(0, toIndex(a(j), mask))( - a(j), - a, - mask - ) != seekKeyOrZeroOrLongMinValue( - 0, - toIndex(a.updated(i, k).apply(j), mask) - )( - a.updated(i, k).apply(j), - a.updated(i, k), - mask - ) - ) - require( - seekKeyOrZeroOrLongMinValue(x, index)( - a(j), - a, - mask - ) != seekKeyOrZeroOrLongMinValue(x, index)( - a.updated(i, k).apply(j), - a.updated(i, k), - mask - ) - ) - - decreases(MAX_ITER - x) - - if (index == resIndex) { - assert(x == resX) - // trivial - } else { - assert(index != i) - lemmaseekKeyOrZeroOrLongMinValueThenChangedAtReturnedIndex( - a, - i, - k, - j, - nextIndex(index, x + 1, mask), - x + 1, - resIndex, - resX - ) - - } - } ensuring (_ => resIndex == i) - - @opaque - @inlineOnce - @pure - @ghost - def lemmaPutValidKeyPreservesForallSeekEntryOrOpenKey2(a: Array[Long], i: Int, k: Long, j: Int)(implicit - mask: Int - ): Unit = { - require(validMask(mask)) - require(a.length == mask + 1) - require(i >= 0) - require(i < a.length) - require(j >= 0) - require(j < a.length) - require(i != j) - require(validKeyInArray(a(j))) - require(validKeyInArray(k)) - require(!arrayContainsKey(a, k, 0)) - require( - seekEntryOrOpen(k)(a, mask) == MissingZero(i) || seekEntryOrOpen(k)( - a, - mask - ) == MissingVacant( - i - ) - ) - require(arrayForallSeekEntryOrOpenFound(0)(a, mask)) - require(arrayNoDuplicates(a, 0)) - - lemmaArrayForallSeekEntryOrOpenFoundFromSmallerThenFromBigger(a, mask, 0, j) - check(seekEntryOrOpen(a(j))(a, mask) == Found(j)) - val newArray = a.updated(i, k) - - if ( - seekKeyOrZeroOrLongMinValue(0, toIndex(a(j), mask))(a(j), a, mask) == - seekKeyOrZeroOrLongMinValue(0, toIndex(newArray.apply(j), mask))( - newArray.apply(j), - newArray, - mask - ) - ) { - lemmaPutValidKeyPreservesForallSeekEntryOrOpenKey1(a, i, k, j)(mask) - check( - seekEntryOrOpen(a(j))(a, mask) == seekEntryOrOpen(newArray.apply(j))( - newArray, - mask - ) - ) - } else { - val intermediateBefore = - seekKeyOrZeroOrLongMinValue(0, toIndex(a(j), mask))(a(j), a, mask) - intermediateBefore match { - case Intermediate(undefined, index, x) if (undefined) => { - check(seekEntryOrOpen(a(j))(a, mask) == Undefined()) - lemmaArrayForallSeekEntryOrOpenFoundFromSmallerThenFromBigger(a, mask, 0, j) - check(seekEntryOrOpen(a(j))(a, mask) == Found(j)) - check(false) - } - case Intermediate(undefinedBefore, indexBefore, xBefore) if (!undefinedBefore) => { - check(!undefinedBefore) - check(xBefore < MAX_ITER) - check(a(indexBefore) == a(j) || a(indexBefore) == 0 || a(indexBefore) == Long.MinValue) - if (a(indexBefore) == a(j)) { - lemmaseekKeyOrZeroOrLongMinValueFoundKeyThenSameAfterChangingI( - a, - i, - k, - j, - toIndex(a(j), mask), - 0, - indexBefore, - xBefore - )(mask) - check(false) - } - check(a(indexBefore) == 0 || a(indexBefore) == Long.MinValue) - if (a(indexBefore) == 0) { - check(false) - } - check(a(indexBefore) == Long.MinValue) - - lemmaseekKeyOrZeroOrLongMinValueThenChangedAtReturnedIndex( - a, - i, - k, - j, - toIndex(a(j), mask), - 0, - indexBefore, - xBefore - )(mask) - lemmaPutValidKeyPreservesVacantIsAtI( - a, - i, - k, - j, - toIndex(a(j), mask), - 0, - indexBefore, - xBefore - )(mask) - } - case _ => { - check(false) - check( - seekEntryOrOpen(a(j))(a, mask) == seekEntryOrOpen( - newArray.apply(j) - )(newArray, mask) - ) - } - } - } - - } ensuring (_ => - seekEntryOrOpen(a(j))(a, mask) == seekEntryOrOpen(a.updated(i, k).apply(j))( - a.updated(i, k), - mask - ) - ) - - @opaque - @inlineOnce - @pure - @ghost - def lemmaPutValidKeyAtRightPlaceThenFindsHelper1( - a: Array[Long], - i: Int, - k: Long, - x: Int, - index: Int, - resIndex: Int, - resX: Int - )(implicit mask: Int): Unit = { - require(validMask(mask)) - require(a.length == mask + 1) - require(i >= 0) - require(i < a.length) - require(validKeyInArray(k)) - require(!arrayContainsKey(a, k, 0)) - require(seekEntryOrOpen(k)(a, mask) == MissingZero(i)) - require(arrayForallSeekEntryOrOpenFound(0)(a, mask)) - require( - seekKeyOrZeroOrLongMinValue(0, toIndex(k, mask))( - k, - a, - mask - ) == Intermediate( - false, - resIndex, - resX - ) - ) - require(a(resIndex) == 0) - require(resIndex == i) - require(x <= resX) - require(x >= 0) - require(index >= 0 && index < a.length) - require( - seekKeyOrZeroOrLongMinValue(x, index)(k, a, mask) == Intermediate( - false, - resIndex, - resX - ) - ) - - decreases(MAX_ITER - x) - assert(resX < MAX_ITER) - assert(a(index) != k) - if (x == resX) { - if (resIndex != index) { - if (a(index) == 0 || a(index) == Long.MinValue) { - check( - seekKeyOrZeroOrLongMinValue(x, index)(k, a, mask) == Intermediate( - false, - index, - resX - ) - ) - check(false) - } else { - check( - seekKeyOrZeroOrLongMinValue(x, index)(k, a, mask) == - seekKeyOrZeroOrLongMinValue( - x + 1, - nextIndex(index, x + 1, mask) - )(k, a, mask) - ) - seekKeyOrZeroOrLongMinValue( - x + 1, - nextIndex(index, x + 1, mask) - )(k, a, mask) match { - case Intermediate(undefined, tempIndex, tempX) => assert(tempX >= x + 1) - case _ => check(false) - } - check(false) - } - } - check(index == resIndex) - check(a(index) == 0) - } else { - check( - seekKeyOrZeroOrLongMinValue(x, index)( - k, - a, - mask - ) == seekKeyOrZeroOrLongMinValue( - x + 1, - nextIndex(index, x + 1, mask) - )(k, a, mask) - ) - check( - seekKeyOrZeroOrLongMinValue(x, index)( - k, - a.updated(i, k), - mask - ) == seekKeyOrZeroOrLongMinValue( - x + 1, - nextIndex(index, x + 1, mask) - )( - k, - a.updated(i, k), - mask - ) - ) - lemmaPutValidKeyAtRightPlaceThenFindsHelper1( - a, - i, - k, - x + 1, - nextIndex(index, x + 1, mask), - resIndex, - resX - ) - } - - } ensuring (_ => - seekKeyOrZeroOrLongMinValue(x, index)( - k, - a.updated(i, k), - mask - ) == seekKeyOrZeroOrLongMinValue(x, index)(k, a, mask) - ) - - def lemmaPutValidKeyAtRightPlaceThenFindsHelper2( - a: Array[Long], - i: Int, - k: Long, - x: Int, - index: Int, - resIndex: Int, - resX: Int - )(implicit mask: Int): Unit = { - require(validMask(mask)) - require(a.length == mask + 1) - require(i >= 0) - require(i < a.length) - require(validKeyInArray(k)) - require(!arrayContainsKey(a, k, 0)) - require(seekEntryOrOpen(k)(a, mask) == MissingVacant(i)) - require(arrayForallSeekEntryOrOpenFound(0)(a, mask)) - require( - seekKeyOrZeroOrLongMinValue(0, toIndex(k, mask))( - k, - a, - mask - ) == Intermediate( - false, - resIndex, - resX - ) - ) - require(a(resIndex) == Long.MinValue) - require(resIndex == i) - require(x <= resX) - require(x >= 0) - require(index >= 0 && index < a.length) - require( - seekKeyOrZeroOrLongMinValue(x, index)(k, a, mask) == Intermediate( - false, - resIndex, - resX - ) - ) - decreases(MAX_ITER - x) - assert(resX < MAX_ITER) - assert(a(index) != k) - if (x == resX) { - if (resIndex != index) { - if (a(index) == 0 || a(index) == Long.MinValue) { - check( - seekKeyOrZeroOrLongMinValue(x, index)(k, a, mask) == Intermediate( - false, - index, - resX - ) - ) - check(false) - } else { - seekKeyOrZeroOrLongMinValue( - x + 1, - nextIndex(index, x + 1, mask) - )(k, a, mask) match { - case Intermediate(undefined, tempIndex, tempX) => assert(tempX >= x + 1) - case _ => check(false) - } - check(false) - } - } - check(index == resIndex) - check(a(index) == Long.MinValue) - } else { - lemmaPutValidKeyAtRightPlaceThenFindsHelper2( - a, - i, - k, - x + 1, - nextIndex(index, x + 1, mask), - resIndex, - resX - ) - } - - } ensuring (_ => - seekKeyOrZeroOrLongMinValue(x, index)( - k, - a.updated(i, k), - mask - ) == seekKeyOrZeroOrLongMinValue(x, index)(k, a, mask) - ) - - @opaque - @inlineOnce - @pure - @ghost - def lemmaPutValidKeyAtRightPlaceThenFinds(a: Array[Long], i: Int, k: Long)(implicit - mask: Int - ): Unit = { - require(validMask(mask)) - require(a.length == mask + 1) - require(i >= 0) - require(i < a.length) - require(validKeyInArray(k)) - require(!arrayContainsKey(a, k, 0)) - require( - seekEntryOrOpen(k)(a, mask) == MissingZero(i) || seekEntryOrOpen(k)( - a, - mask - ) == MissingVacant( - i - ) - ) - require(arrayForallSeekEntryOrOpenFound(0)(a, mask)) - - val intermediateBefore = - seekKeyOrZeroOrLongMinValue(0, toIndex(k, mask))(k, a, mask) - val intermediateAfter = - seekKeyOrZeroOrLongMinValue(0, toIndex(k, mask))(k, a.updated(i, k), mask) - - if (seekEntryOrOpen(k)(a, mask) == MissingZero(i)) { - intermediateBefore match { - case Intermediate(undefined, index, x) if (undefined) => check(false) - case Intermediate(undefined, index, x) if (a(index) == Long.MinValue) => check(false) - case Intermediate(undefined, index, x) if (a(index) == k) => check(false) - case Intermediate(undefined, index, x) => { - check(a(index) == 0) - check(index == i) - lemmaPutValidKeyAtRightPlaceThenFindsHelper1(a, i, k, 0, toIndex(k, mask), index, x)( - mask - ) - check(a.updated(i, k).apply(index) == k) - } - case _ => check(false) - } - check(intermediateAfter == intermediateBefore) - } else { - assert(seekEntryOrOpen(k)(a, mask) == MissingVacant(i)) - intermediateBefore match { - case Intermediate(undefined, index, x) if (undefined) => check(false) - case Intermediate(undefined, index, x) if (a(index) == Long.MinValue) => { - check(index == i) - lemmaPutValidKeyAtRightPlaceThenFindsHelper2(a, i, k, 0, toIndex(k, mask), index, x)( - mask - ) - - } - case Intermediate(undefined, index, x) if (a(index) == k) => check(false) - case Intermediate(undefined, index, x) => check(false) - case _ => check(false) - } - - } - - } ensuring (_ => seekEntryOrOpen(k)(a.updated(i, k), mask) == Found(i)) - - @opaque - @inlineOnce - @pure - @ghost - def lemmaPutValidKeyPreservesForallSeekEntryOrOpenStartIndex( - a: Array[Long], - i: Int, - k: Long, - startIndex: Int - )(implicit - mask: Int - ): Unit = { - require(validMask(mask)) - require(a.length == mask + 1) - require(i >= 0) - require(i < a.length) - require(startIndex >= 0) - require(startIndex < a.length) - require(validKeyInArray(k)) - require(arrayNoDuplicates(a, 0)) - require(!arrayContainsKey(a, k, 0)) - require( - seekEntryOrOpen(k)(a, mask) == MissingZero(i) || seekEntryOrOpen(k)( - a, - mask - ) == MissingVacant( - i - ) - ) - require(arrayForallSeekEntryOrOpenFound(0)(a, mask)) - - require(arrayForallSeekEntryOrOpenFound(0)(a, mask)) - - decreases(a.length - startIndex) - - val newArray = a.updated(i, k) - if (startIndex == i) { - if (startIndex < a.length - 1) { - lemmaPutValidKeyPreservesForallSeekEntryOrOpenStartIndex(a, i, k, startIndex + 1) - } - lemmaPutValidKeyAtRightPlaceThenFinds(a, i, k)(mask) - check(seekEntryOrOpen(k)(newArray, mask) == Found(i)) - } else { - if (validKeyInArray(a(startIndex))) { - lemmaPutNewValidKeyPreservesNoDuplicate(a, k, i, 0, Nil()) - lemmaPutValidKeyPreservesForallSeekEntryOrOpenKey2(a, i, k, startIndex)(mask) - lemmaArrayForallSeekEntryOrOpenFoundFromSmallerThenFromBigger(a, mask, 0, startIndex) - if (startIndex < a.length - 1) { - lemmaPutValidKeyPreservesForallSeekEntryOrOpenStartIndex(a, i, k, startIndex + 1) - } - - } else { - if (startIndex < a.length - 1) { - lemmaPutValidKeyPreservesForallSeekEntryOrOpenStartIndex(a, i, k, startIndex + 1) - } - } - } - } ensuring (_ => arrayForallSeekEntryOrOpenFound(startIndex)(a.updated(i, k), mask)) - - @opaque - @inlineOnce - @pure - @ghost - def lemmaPutValidKeyPreservesForallSeekEntryOrOpen(k: Long, a: Array[Long], i: Int)(implicit - mask: Int - ): Unit = { - require(validMask(mask)) - require(a.length == mask + 1) - require(i >= 0) - require(i < a.length) - require(validKeyInArray(k)) - require(!arrayContainsKey(a, k, 0)) - require( - seekEntryOrOpen(k)(a, mask) == MissingZero(i) || seekEntryOrOpen(k)( - a, - mask - ) == MissingVacant( - i - ) - ) - require(arrayForallSeekEntryOrOpenFound(0)(a, mask)) - require(arrayNoDuplicates(a, 0)) - - lemmaPutValidKeyPreservesForallSeekEntryOrOpenStartIndex(a, i, k, 0)(mask) - - } ensuring (_ => arrayForallSeekEntryOrOpenFound(0)(a.updated(i, k), mask)) - - @opaque - @inlineOnce - @pure - @ghost - def lemmaSeekEntryOrOpenFindsThenSeekEntryFinds( - k: Long, - i: Int, - a: Array[Long], - mask: Int - ): Unit = { - require(validMask(mask)) - require(a.length == mask + 1) - require(arrayForallSeekEntryOrOpenFound(0)(a, mask)) - require(validKeyInArray(k)) - require(seekEntryOrOpen(k)(a, mask) == Found(i)) - - } ensuring (_ => - (seekEntry(k)(a, mask) match { - case Found(index) => index == i - case _ => false - }) - ) - - @opaque - @inlineOnce - @pure - @ghost - def lemmaSeekEntryOrOpenMissThenSeekEntryMiss( - k: Long, - i: Int, - a: Array[Long], - mask: Int - ): Unit = { - require(validMask(mask)) - require(a.length == mask + 1) - require(arrayForallSeekEntryOrOpenFound(0)(a, mask)) - require(validKeyInArray(k)) - require(seekEntryOrOpen(k)(a, mask) match { - case Found(_) => false - case _ => true - }) - - } ensuring (_ => - seekEntry(k)(a, mask) match { - case Found(_) => false - case _ => true - } - ) - - // ARRAY UTILITY FUNCTIONS ---------------------------------------------------------------------------------------- - - @pure - @ghost - def validKeyInArray(l: Long): Boolean = { - l != 0 && l != Long.MinValue - } - - @pure - @ghost - def arrayCountValidKeys( - a: Array[Long], - from: Int, - to: Int - ): Int = { - require(from <= to && from >= 0 && to <= a.length) - require(a.length < Integer.MAX_VALUE) - - decreases(a.length - from) - if (from >= to) { - 0 - } else { - if (validKeyInArray(a(from))) { - 1 + arrayCountValidKeys(a, from + 1, to) - } else { - arrayCountValidKeys(a, from + 1, to) - } - } - } ensuring (res => res >= 0 && res <= a.length - from) - - @pure - @ghost - def arrayContainsKey(a: Array[Long], k: Long, from: Int): Boolean = { - require(from >= 0) - require(from < a.length) - require(a.length < Integer.MAX_VALUE) - - decreases(a.length - from) - if (a(from) == k) { - true - } else if (from + 1 < a.length) { - arrayContainsKey(a, k, from + 1) - } else { - false - } - } - - @pure - @ghost - def arrayScanForKey(a: Array[Long], k: Long, from: Int): Int = { - require(from >= 0 && from < a.length && a.length < Integer.MAX_VALUE) - require(arrayContainsKey(a, k, from)) - decreases(a.length - from) - - if (a(from) == k) from - else arrayScanForKey(a, k, from + 1) - } ensuring (res => res >= 0 && res < a.length && a(res) == k) - - @pure - @ghost - def arrayNoDuplicates(a: Array[Long], from: Int, acc: List[Long] = Nil[Long]()): Boolean = { - require(from >= 0 && from <= a.length) - require(a.length < Integer.MAX_VALUE) - require(ListOps.noDuplicate(acc)) - require(!acc.contains(0) && !acc.contains(Long.MinValue)) - decreases(a.length - from) - - if (from >= a.length) true - else if (validKeyInArray(a(from)) && acc.contains(a(from))) false - else if (validKeyInArray(a(from))) arrayNoDuplicates(a, from + 1, Cons(a(from), acc)) - else arrayNoDuplicates(a, from + 1, acc) - } - - /** Return true iff the two arrays contain the same elements from the index "from" included to the index "to" not included - * - * @param a1 - * @param a2 - * @param from - * @param to - * @return - */ - @pure - @ghost - def arraysEqualsFromTo(a1: Array[Long], a2: Array[Long], from: Int, to: Int): Boolean = { - require( - a1.length == a2.length && from >= 0 && from <= to && to <= a1.length && a1.length < Integer.MAX_VALUE - ) - - decreases(to + 1 - from) - if (from >= to) { - true - } else if (a1(from) != a2(from)) { - false - } else { - arraysEqualsFromTo(a1, a2, from + 1, to) - } - } - - // --------------------- ARRAY RELATED LEMMAS ------------------------------------------ - - @opaque - @inlineOnce - @pure - @ghost - def lemmaArrayNoDuplicateRemoveOneThenNotContain(a: Array[Long], i: Int, k: Long): Unit = { - require(i >= 0) - require(i < a.length) - require(a.length < Integer.MAX_VALUE) - require(arrayNoDuplicates(a, 0)) - require(validKeyInArray(k)) - require(a(i) == k) - - if (arrayContainsKey(a.updated(i, Long.MinValue), k, 0)) { - val j = arrayScanForKey(a.updated(i, Long.MinValue), k, 0) - assert(j != i) - check(a.updated(i, Long.MinValue).apply(j) == k) - if (j > i) { - check(arrayContainsKey(a, k, j)) - lemmaNoDuplicateFromThenFromBigger(a, 0, i) - lemmaArrayContainsFromImpliesContainsFromSmaller(a, k, j, i + 1) - lemmaArrayNoDuplicateThenKeysContainedNotEqual(a, k, i, Nil()) - check(false) - } else if (i > j) { - check(arrayContainsKey(a, k, i)) - lemmaNoDuplicateFromThenFromBigger(a, 0, j) - lemmaArrayContainsFromImpliesContainsFromSmaller(a, k, i, j + 1) - lemmaArrayNoDuplicateThenKeysContainedNotEqual(a, k, j, Nil()) - check(false) - } - - } - - } ensuring (_ => !arrayContainsKey(a.updated(i, Long.MinValue), k, 0)) - - @opaque - @inlineOnce - @pure - @ghost - def lemmaArrayContainsFromImpliesContainsFromZero( - a: Array[Long], - k: Long, - from: Int - ): Unit = { - require(from >= 0) - require(from < a.length) - require(a.length < Integer.MAX_VALUE) - require(arrayContainsKey(a, k, from)) - - decreases(from) - if (from > 0) { - lemmaArrayContainsFromImpliesContainsFromZero(a, k, from - 1) - } - } ensuring (_ => arrayContainsKey(a, k, 0)) - - @opaque - @inlineOnce - @pure - @ghost - def lemmaArrayContainsFromImpliesContainsFromSmaller( - a: Array[Long], - k: Long, - from: Int, - newFrom: Int - ): Unit = { - require(from >= 0) - require(from < a.length) - require(newFrom >= 0) - require(newFrom <= from) - require(a.length < Integer.MAX_VALUE) - require(arrayContainsKey(a, k, from)) - - decreases(from) - if (from > newFrom) { - lemmaArrayContainsFromImpliesContainsFromSmaller(a, k, from - 1, newFrom) - } - } ensuring (_ => arrayContainsKey(a, k, newFrom)) - - @opaque - @inlineOnce - @pure - @ghost - def lemmaAddKeyNoContainsInAccStillNoDuplicate( - a: Array[Long], - k: Long, - from: Int, - acc: List[Long], - newAcc: List[Long] - ): Unit = { - require(a.length < Integer.MAX_VALUE) - require(from >= 0) - require(from < a.length) - require(ListOps.noDuplicate(acc)) - require(ListOps.noDuplicate(newAcc)) - require(!acc.contains(0) && !acc.contains(Long.MinValue)) - require(!arrayContainsKey(a, k, from)) - require(!acc.contains(k)) - require(validKeyInArray(k)) - require(arrayNoDuplicates(a, from, acc)) - require(ListSpecs.subseq(acc, newAcc)) - require(newAcc.contains(k)) - require(newAcc - k == acc) - require(!newAcc.contains(0) && !newAcc.contains(Long.MinValue)) - decreases(a.length - from) - - if (from + 1 < a.length) { - if (validKeyInArray(a(from))) { - lemmaAddKeyNoContainsInAccStillNoDuplicate( - a, - k, - from + 1, - a(from) :: acc, - a(from) :: newAcc - ) - } else { - lemmaAddKeyNoContainsInAccStillNoDuplicate(a, k, from + 1, acc, newAcc) - } - } - } ensuring (_ => arrayNoDuplicates(a, from, newAcc)) - - @opaque - @inlineOnce - @pure - @ghost - def lemmaListMinusENotContainedEqualsList(e: Long, l: List[Long]): Unit = { - require(!l.contains(e)) - decreases(l) - - l match { - case head :: tl => lemmaListMinusENotContainedEqualsList(e, tl) - case Nil() => - } - } ensuring (_ => l - e == l) - - @opaque - @inlineOnce - @pure - @ghost - def lemmaLMinusHeadEqualsTail(head: Long, tail: List[Long]): Unit = { - require(!tail.contains(head)) - assert(!tail.content.contains(head)) - - val l = head :: tail - l match { - case hd :: tl => lemmaListMinusENotContainedEqualsList(head, tl) - case Nil() => () - } - - } ensuring (_ => (head :: tail) - head == tail) - - @opaque - @inlineOnce - @pure - @ghost - def lemmaPutNewValidKeyPreservesNoDuplicate( - a: Array[Long], - k: Long, - i: Int, - from: Int, - acc: List[Long] - ): Unit = { - require(a.length < Integer.MAX_VALUE) - require(from >= 0) - require(from < a.length) - require(ListOps.noDuplicate(acc)) - require(!acc.contains(0) && !acc.contains(Long.MinValue)) - require((from > i && acc.contains(k)) || (from <= i && !acc.contains(k))) - require(arrayNoDuplicates(a, 0)) - require(arrayNoDuplicates(a, from, acc)) - require(i >= 0 && i < a.length) - require(validKeyInArray(k)) - require(!arrayContainsKey(a, k, 0)) - decreases(a.length - from) - - if (from + 1 < a.length) { - if (from == i) { - val newArray = a.updated(i, k) - check(arrayContainsKey(newArray, k, from)) - - if (arrayContainsKey(a, k, from + 1)) { - lemmaArrayContainsFromImpliesContainsFromZero(a, k, from) - check(false) - } - if (validKeyInArray(a(from))) { - lemmaListSubSeqRefl(acc) - lemmaArrayNoDuplicateWithAnAccThenWithSubSeqAcc(a, a(from) :: acc, acc, from + 1) - } else {} - check(arrayNoDuplicates(a, from + 1, acc)) - lemmaListSubSeqRefl(acc) - lemmaLMinusHeadEqualsTail(k, acc) - lemmaAddKeyNoContainsInAccStillNoDuplicate(a, k, from + 1, acc, k :: acc) - lemmaPutNewValidKeyPreservesNoDuplicate(a, k, i, from + 1, k :: acc) - } else { - if (validKeyInArray(a(from))) { - if (a(from) == k) { - check(arrayContainsKey(a, k, from)) - lemmaArrayContainsFromImpliesContainsFromZero(a, k, from) - check(false) - } - assert(a(from) != k) - lemmaPutNewValidKeyPreservesNoDuplicate(a, k, i, from + 1, a(from) :: acc) - } else { - lemmaPutNewValidKeyPreservesNoDuplicate(a, k, i, from + 1, acc) - } - } - } - } ensuring (_ => arrayNoDuplicates(a.updated(i, k), from, acc)) - - @opaque - @inlineOnce - @pure - @ghost - def lemmaPutNonValidKeyPreservesNoDuplicate( - a: Array[Long], - l: Long, - i: Int, - from: Int, - acc: List[Long] - ): Unit = { - require(a.length < Integer.MAX_VALUE) - require(from >= 0) - require(from < a.length) - require(ListOps.noDuplicate(acc)) - require(!acc.contains(0) && !acc.contains(Long.MinValue)) - require(arrayNoDuplicates(a, 0)) - require(arrayNoDuplicates(a, from, acc)) - require(i >= 0 && i < a.length) - require(!validKeyInArray(l)) - decreases(a.length - from) - - if (from + 1 < a.length) { - if (validKeyInArray(a(from))) { - lemmaListSubSeqRefl(acc) - lemmaArrayNoDuplicateWithAnAccThenWithSubSeqAcc(a, a(from) :: acc, acc, from + 1) - } - if (from == i) { - lemmaPutNonValidKeyPreservesNoDuplicate(a, l, i, from + 1, acc) - } else { - if (validKeyInArray(a(from))) { - lemmaPutNonValidKeyPreservesNoDuplicate(a, l, i, from + 1, a(from) :: acc) - } else { - lemmaPutNonValidKeyPreservesNoDuplicate(a, l, i, from + 1, acc) - } - } - } - } ensuring (_ => arrayNoDuplicates(a.updated(i, l), from, acc)) - - @opaque - @inlineOnce - @pure - @ghost - def lemmaArrayNoDuplicateThenKeysContainedNotEqual( - a: Array[Long], - k: Long, - from: Int, - acc: List[Long] - ): Unit = { - require(a.length < Integer.MAX_VALUE) - require(validKeyInArray(k)) - require(from >= 0) - require(from + 1 < a.length) - require(from < a.length) - require(ListOps.noDuplicate(acc)) - require(!acc.contains(0) && !acc.contains(Long.MinValue)) - require(arrayContainsKey(a, k, from + 1)) - require(arrayNoDuplicates(a, from, acc)) - - if (validKeyInArray(a(from))) { - check(arrayNoDuplicates(a, from + 1, Cons(a(from), acc))) - lemmaArrayNoDuplicateWithAnAccThenWithSubSeqAcc(a, acc, Nil(), from) - check(arrayNoDuplicates(a, from + 1, Cons(a(from), Nil()))) - lemmaArrayNoDuplicateFromNotContainsKeysInAcc(a, from + 1, a(from), Cons(a(from), Nil())) - check(!arrayContainsKey(a, a(from), from + 1)) - } - - } ensuring (_ => a(from) != k) - - @opaque - @inlineOnce - @pure - @ghost - def lemmaArrayNoDuplicateFromNotContainsKeysInAcc( - a: Array[Long], - from: Int, - k: Long, - acc: List[Long] - ): Unit = { - require(a.length < Integer.MAX_VALUE) - require(from >= 0) - require(from < a.length) - require(ListOps.noDuplicate(acc)) - require(!acc.contains(0) && !acc.contains(Long.MinValue)) - require(arrayNoDuplicates(a, from, acc)) - require(acc.contains(k)) - - decreases(a.length - from) - - if (from != a.length - 1) { - if (validKeyInArray(a(from))) { - lemmaListSubSeqRefl(Cons(a(from), acc)) - ListSpecs.subseqTail(Cons(a(from), acc), Cons(a(from), acc)) - check(ListSpecs.subseq(acc, Cons(a(from), acc))) - lemmaArrayNoDuplicateWithAnAccThenWithSubSeqAcc(a, Cons(a(from), acc), acc, from + 1) - } - lemmaArrayNoDuplicateFromNotContainsKeysInAcc(a, from + 1, k, acc) - } - - } ensuring (_ => !arrayContainsKey(a, k, from)) - - @opaque - @inlineOnce - @pure - @ghost - def lemmaListSubSeqRefl(l: List[Long]): Unit = { - decreases(l) - l match { - case head :: tl => { - assert(head == head) - lemmaListSubSeqRefl(tl) - } - case Nil() => - } - - } ensuring (_ => ListSpecs.subseq(l, l)) - - @opaque - @inlineOnce - @pure - @ghost - def lemmaArrayNoDuplicateWithAnAccThenWithSubSeqAcc( - a: Array[Long], - acc: List[Long], - newAcc: List[Long], - from: Int - ): Unit = { - require(a.length < Integer.MAX_VALUE) - require(from >= 0) - require(from <= a.length) - require(ListOps.noDuplicate(acc)) - require(!acc.contains(0) && !acc.contains(Long.MinValue)) - require(!newAcc.contains(0) && !newAcc.contains(Long.MinValue)) - require(ListSpecs.subseq(newAcc, acc)) - require({ - ListSpecs.noDuplicateSubseq(newAcc, acc) - arrayNoDuplicates(a, from, acc) - }) - decreases(a.length - from) - - if (from < a.length) { - val k = a(from) - if (validKeyInArray(k)) { - if (!acc.contains(k)) { - lemmaArrayNoDuplicateWithAnAccThenWithSubSeqAcc( - a, - Cons(k, acc), - Cons(k, newAcc), - from + 1 - ) - ListSpecs.noDuplicateSubseq(Cons(k, newAcc), Cons(k, acc)) - } - } else { - lemmaArrayNoDuplicateWithAnAccThenWithSubSeqAcc(a, acc, newAcc, from + 1) - } - } - - } ensuring (_ => arrayNoDuplicates(a, from, newAcc)) - - @opaque - @inlineOnce - @pure - @ghost - def lemmaNoDuplicateFromThenFromBigger(a: Array[Long], from: Int, newFrom: Int): Unit = { - require(a.length < Integer.MAX_VALUE) - require(from >= 0) - require(from <= a.length) - require(newFrom >= from && newFrom <= a.length) - require(arrayNoDuplicates(a, from)) - - decreases(newFrom - from) - - if (from != newFrom) { - assert(from < a.length) - if (newFrom != a.length) { - if (validKeyInArray(a(from))) { - lemmaArrayNoDuplicateWithAnAccThenWithSubSeqAcc( - a, - Cons(a(from), Nil()), - Nil(), - from + 1 - ) - } - lemmaNoDuplicateFromThenFromBigger(a, from + 1, newFrom) - } - } - - } ensuring (_ => arrayNoDuplicates(a, newFrom)) - - @opaque - @inlineOnce - @pure - @ghost - def lemmaArrayForallSeekEntryOrOpenFoundFromSmallerThenFromBigger( - a: Array[Long], - mask: Int, - from: Int, - newFrom: Int - ): Unit = { - require(validMask(mask)) - require(a.length == mask + 1) - require(from >= 0 && from <= a.length) - require(newFrom >= from && newFrom <= a.length) - require(arrayForallSeekEntryOrOpenFound(from)(a, mask)) - - decreases(newFrom - from) - - if (from < newFrom) { - lemmaArrayForallSeekEntryOrOpenFoundFromSmallerThenFromBigger(a, mask, from + 1, newFrom) - } - } ensuring (_ => arrayForallSeekEntryOrOpenFound(newFrom)(a, mask)) - - @opaque - @inlineOnce - @pure - @ghost - def lemmaCountingValidKeysAtTheEnd(a: Array[Long], from: Int, to: Int): Unit = { - require(a.length < Integer.MAX_VALUE && from >= 0 && to > from && to <= a.length) - - decreases(to - from) - if (from + 1 < to) { - lemmaCountingValidKeysAtTheEnd(a, from + 1, to) - } - } ensuring (_ => - if (validKeyInArray(a(to - 1))) - arrayCountValidKeys(a, from, to - 1) + 1 == arrayCountValidKeys(a, from, to) - else - arrayCountValidKeys(a, from, to - 1) == arrayCountValidKeys(a, from, to) - ) - - @opaque - @inlineOnce - @pure - @ghost - def lemmaKnownPivotPlusOneIsPivot(a: Array[Long], from: Int, to: Int, pivot: Int): Unit = { - require( - a.length < Integer.MAX_VALUE && from >= 0 && to > from && to <= a.length && pivot >= from && pivot < to - 1 && - LongMapFixedSize.isPivot(a, from, to, pivot) - ) - - lemmaCountingValidKeysAtTheEnd(a, from, pivot + 1) - - } ensuring (_ => LongMapFixedSize.isPivot(a, from, to, pivot + 1)) - - @pure - @ghost - def isPivot(a: Array[Long], from: Int, to: Int, pivot: Int): Boolean = { - require( - a.length < Integer.MAX_VALUE && from >= 0 && to > from && to <= a.length && pivot >= from && pivot < to - ) - arrayCountValidKeys(a, from, pivot) + arrayCountValidKeys( - a, - pivot, - to - ) == arrayCountValidKeys(a, from, to) - } - - @opaque - @inlineOnce - @pure - @ghost - def lemmaSumOfNumOfKeysOfSubArraysIsEqualToWholeFromTo( - a: Array[Long], - from: Int, - to: Int, - pivot: Int, - knownPivot: Int - ): Unit = { - require( - a.length < Integer.MAX_VALUE && from >= 0 && to >= from && to <= a.length && - pivot >= from && pivot < to && - knownPivot <= pivot && knownPivot >= from && - isPivot(a, from, to, knownPivot) - ) - - decreases(pivot - knownPivot) - if (knownPivot != pivot) { - lemmaKnownPivotPlusOneIsPivot(a, from, to, knownPivot) - lemmaSumOfNumOfKeysOfSubArraysIsEqualToWholeFromTo(a, from, to, pivot, knownPivot + 1) - } - } ensuring (_ => isPivot(a, from, to, pivot)) - - @opaque - @inlineOnce - @pure - @ghost - def lemmaSumOfNumOfKeysOfSubArraysIsEqualToWhole( - a: Array[Long], - from: Int, - to: Int, - pivot: Int - ): Unit = { - require( - a.length < Integer.MAX_VALUE && from >= 0 && to >= from && to <= a.length && pivot >= from && pivot <= to - ) - - if (pivot < to) { - lemmaSumOfNumOfKeysOfSubArraysIsEqualToWholeFromTo(a, from, to, pivot, from) - } - - } ensuring (_ => - arrayCountValidKeys(a, from, pivot) + arrayCountValidKeys( - a, - pivot, - to - ) == arrayCountValidKeys(a, from, to) - ) - - @opaque - @inlineOnce - @pure - @ghost - def lemmaRemoveValidKeyAndNumKeysToImpliesToALength( - a: Array[Long], - i: Int, - k: Long, - to: Int - ): Unit = { - require(i >= 0 && i < a.length) - require(validKeyInArray(a(i))) - require(!validKeyInArray(k)) - require(a.length < Integer.MAX_VALUE) - require(to >= 0 && to <= a.length && to > i) - require( - (arrayCountValidKeys(a.updated(i, k), i + 1, to) == arrayCountValidKeys( - a, - i + 1, - to - )) - ) - decreases(a.length + 1 - to) - - if (to != a.length) { - if (validKeyInArray(a(to))) { - lemmaValidKeyIncreasesNumOfKeys(a, i + 1, to) - lemmaValidKeyIncreasesNumOfKeys(a.updated(i, k), i + 1, to) - } else { - lemmaNotValidKeyDoesNotIncreaseNumOfKeys(a, i + 1, to) - lemmaNotValidKeyDoesNotIncreaseNumOfKeys(a.updated(i, k), i + 1, to) - } - lemmaRemoveValidKeyAndNumKeysToImpliesToALength(a, i, k, to + 1) - } - } ensuring (_ => - arrayCountValidKeys(a.updated(i, k), i + 1, a.length) == arrayCountValidKeys( - a, - i + 1, - a.length - ) - ) - - @opaque - @inlineOnce - @pure - @ghost - def lemmaRemoveValidKeyAndNumKeysFromImpliesFromZero( - a: Array[Long], - i: Int, - k: Long, - from: Int - ): Unit = { - require(i >= 0 && i < a.length) - require(validKeyInArray(a(i))) - require(!validKeyInArray(k)) - require(a.length < Integer.MAX_VALUE) - require(from >= 0 && from <= a.length && i >= from) - require( - (arrayCountValidKeys(a.updated(i, k), from, i + 1) == arrayCountValidKeys( - a, - from, - i + 1 - ) - 1) - ) - - decreases(from) - - if (from > 0) { - lemmaSumOfNumOfKeysOfSubArraysIsEqualToWhole(a, from - 1, i + 1, from) - lemmaSumOfNumOfKeysOfSubArraysIsEqualToWhole(a.updated(i, k), from - 1, i + 1, from) - lemmaRemoveValidKeyAndNumKeysFromImpliesFromZero(a, i, k, from - 1) - } - - } ensuring (_ => { - arrayCountValidKeys(a.updated(i, k), 0, i + 1) == arrayCountValidKeys( - a, - 0, - i + 1 - ) - 1 - }) - - @opaque - @inlineOnce - @pure - @ghost - def lemmaRemoveValidKeyDecreasesNumberOfValidKeysInArray( - a: Array[Long], - i: Int, - k: Long - ): Unit = { - require( - i >= 0 && i < a.length && validKeyInArray(a(i)) && !validKeyInArray( - k - ) && a.length < Integer.MAX_VALUE - ) - - lemmaRemoveValidKeyAndNumKeysFromImpliesFromZero(a, i, k, i) - lemmaRemoveValidKeyAndNumKeysToImpliesToALength(a, i, k, i + 1) - lemmaSumOfNumOfKeysOfSubArraysIsEqualToWhole(a, 0, a.length, i + 1) - lemmaSumOfNumOfKeysOfSubArraysIsEqualToWhole(a.updated(i, k), 0, a.length, i + 1) - - } ensuring (_ => - arrayCountValidKeys(a.updated(i, k), 0, a.length) == arrayCountValidKeys( - a, - 0, - a.length - ) - 1 - ) - - @opaque - @inlineOnce - @pure - @ghost - def lemmaValidKeyIncreasesNumOfKeys(a: Array[Long], from: Int, to: Int): Unit = { - require( - a.length < Integer.MAX_VALUE && from >= 0 && to >= from && to < a.length && validKeyInArray( - a(to) - ) - ) - - lemmaSumOfNumOfKeysOfSubArraysIsEqualToWhole(a, from, to + 1, to) - - } ensuring (_ => arrayCountValidKeys(a, from, to + 1) == arrayCountValidKeys(a, from, to) + 1) - - @opaque - @inlineOnce - @pure - @ghost - def lemmaNotValidKeyDoesNotIncreaseNumOfKeys(a: Array[Long], from: Int, to: Int): Unit = { - require( - a.length < Integer.MAX_VALUE && from >= 0 && to >= from && to < a.length && !validKeyInArray( - a(to) - ) - ) - - lemmaSumOfNumOfKeysOfSubArraysIsEqualToWhole(a, from, to + 1, to) - - } ensuring (_ => arrayCountValidKeys(a, from, to + 1) == arrayCountValidKeys(a, from, to)) - - @opaque - @inlineOnce - @pure - @ghost - def lemmaAddValidKeyAndNumKeysToImpliesToALength( - a: Array[Long], - i: Int, - k: Long, - to: Int - ): Unit = { - require(i >= 0 && i < a.length) - require(!validKeyInArray(a(i)) && validKeyInArray(k)) - require(a.length < Integer.MAX_VALUE) - require(to >= 0 && to <= a.length && to > i) - require( - (arrayCountValidKeys(a.updated(i, k), i + 1, to) == arrayCountValidKeys( - a, - i + 1, - to - )) - ) - decreases(a.length + 1 - to) - - if (to != a.length) { - if (validKeyInArray(a(to))) { - lemmaValidKeyIncreasesNumOfKeys(a, i + 1, to) - lemmaValidKeyIncreasesNumOfKeys(a.updated(i, k), i + 1, to) - } else { - lemmaNotValidKeyDoesNotIncreaseNumOfKeys(a, i + 1, to) - lemmaNotValidKeyDoesNotIncreaseNumOfKeys(a.updated(i, k), i + 1, to) - } - lemmaAddValidKeyAndNumKeysToImpliesToALength(a, i, k, to + 1) - } - } ensuring (_ => - arrayCountValidKeys(a.updated(i, k), i + 1, a.length) == arrayCountValidKeys( - a, - i + 1, - a.length - ) - ) - - @opaque - @inlineOnce - @pure - @ghost - def lemmaAddValidKeyAndNumKeysFromImpliesFromZero( - a: Array[Long], - i: Int, - k: Long, - from: Int - ): Unit = { - require(i >= 0 && i < a.length) - require(!validKeyInArray(a(i))) - require(validKeyInArray(k)) - require(a.length < Integer.MAX_VALUE) - require(from >= 0 && from <= a.length && i >= from) - require( - (arrayCountValidKeys(a.updated(i, k), from, i + 1) == arrayCountValidKeys( - a, - from, - i + 1 - ) + 1) - ) - - decreases(from) - - if (from > 0) { - lemmaSumOfNumOfKeysOfSubArraysIsEqualToWhole(a, from - 1, i + 1, from) - lemmaSumOfNumOfKeysOfSubArraysIsEqualToWhole(a.updated(i, k), from - 1, i + 1, from) - lemmaAddValidKeyAndNumKeysFromImpliesFromZero(a, i, k, from - 1) - } - - } ensuring (_ => { - arrayCountValidKeys(a.updated(i, k), 0, i + 1) == arrayCountValidKeys( - a, - 0, - i + 1 - ) + 1 - }) - - @opaque - @inlineOnce - @pure - @ghost - def lemmaAddValidKeyIncreasesNumberOfValidKeysInArray(a: Array[Long], i: Int, k: Long): Unit = { - require( - i >= 0 && i < a.length && !validKeyInArray(a(i)) && validKeyInArray( - k - ) && a.length < Integer.MAX_VALUE - ) - - lemmaAddValidKeyAndNumKeysFromImpliesFromZero(a, i, k, i) - lemmaAddValidKeyAndNumKeysToImpliesToALength(a, i, k, i + 1) - lemmaSumOfNumOfKeysOfSubArraysIsEqualToWhole(a, 0, a.length, i + 1) - lemmaSumOfNumOfKeysOfSubArraysIsEqualToWhole(a.updated(i, k), 0, a.length, i + 1) - - } ensuring (_ => - arrayCountValidKeys(a.updated(i, k), 0, a.length) == arrayCountValidKeys( - a, - 0, - a.length - ) + 1 - ) - - @opaque - @inlineOnce - @pure - @ghost - def lemmaValidKeyAtIImpliesCountKeysIsOne(a: Array[Long], i: Int): Unit = { - require(i >= 0 && i < a.length && validKeyInArray(a(i)) && a.length < Integer.MAX_VALUE) - - } ensuring (_ => arrayCountValidKeys(a, i, i + 1) == 1) - - @opaque - @inlineOnce - @pure - @ghost - def lemmaArrayEqualsFromToReflexivity(a: Array[Long], from: Int, to: Int): Unit = { - require(from >= 0 && from < to && to <= a.length && a.length < Integer.MAX_VALUE) - decreases(to - from) - if (from + 1 < to) { - lemmaArrayEqualsFromToReflexivity(a, from + 1, to) - } - } ensuring (_ => arraysEqualsFromTo(a, snapshot(a), from, to)) - - @opaque - @inlineOnce - @pure - @ghost - def lemmaValidKeyIndexImpliesArrayContainsKey(a: Array[Long], k: Long, i: Int): Unit = { - require(a.length < Integer.MAX_VALUE) - require(i >= 0 && i < a.length) - require(a(i) == k) - - LongMapFixedSize.lemmaArrayContainsFromImpliesContainsFromZero(a, k, i) - } ensuring (_ => arrayContainsKey(a, k, 0)) - - @opaque - @inlineOnce - @pure - @ghost - def lemmaArrayCountValidKeysOfFilled0ArrayIs0(a: Array[Long], i: Int, size: Int): Unit = { - require(a.length < Integer.MAX_VALUE) - require(i >= 0 && i <= a.length) - require(a.length == size) - require(a == Array.fill(size)(0L)) - decreases(size - i) - if (i != size) { - assert(a(i) == 0) - lemmaArrayCountValidKeysOfFilled0ArrayIs0(a, i + 1, size) - } - - } ensuring (_ => arrayCountValidKeys(a, i, size) == 0) - - @opaque - @inlineOnce - @pure - @ghost - def lemmaArrayNoDuplicatesInAll0Array(a: Array[Long], from: Int, size: Int, acc: List[Long] = Nil[Long]()): Unit = { - require(a.length == size) - require(size < Integer.MAX_VALUE) - require(a == Array.fill(size)(0L)) - require(from >= 0 && from <= a.length) - require(!acc.contains(0) && !acc.contains(Long.MinValue)) - require(ListOps.noDuplicate(acc)) - decreases(size - from) - if (from < size) { - lemmaArrayNoDuplicatesInAll0Array(a, from + 1, size, acc) - } - } ensuring (res => arrayNoDuplicates(a, from, acc)) - - } - -} diff --git a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/chassot/MutableLongMap.scala b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/chassot/MutableLongMap.scala new file mode 120000 index 00000000..345ac4c1 --- /dev/null +++ b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/chassot/MutableLongMap.scala @@ -0,0 +1 @@ +../../../../../../../../../data-structures/maps/mutablemaps/src/main/scala/ch/epfl/chassot/MutableLongMap.scala \ No newline at end of file diff --git a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/chassot/iMutableMaps.scala b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/chassot/iMutableMaps.scala new file mode 120000 index 00000000..8fe7da3c --- /dev/null +++ b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/chassot/iMutableMaps.scala @@ -0,0 +1 @@ +../../../../../../../../../data-structures/maps/mutablemaps/src/main/scala/ch/epfl/chassot/iMutableMaps.scala \ No newline at end of file From 9694e9650d3736515fc30eb50940f3981927d013 Mon Sep 17 00:00:00 2001 From: Samuel Chassot Date: Mon, 12 Aug 2024 14:11:26 +0200 Subject: [PATCH 20/78] ensuring as method and not infix --- .../scala/ch/epfl/lexer/VerifiedLexer.scala | 74 ++++++------- .../scala/ch/epfl/lexer/VerifiedRegex.scala | 104 +++++++++--------- 2 files changed, 89 insertions(+), 89 deletions(-) diff --git a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedLexer.scala b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedLexer.scala index a26bfbd0..35350975 100644 --- a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedLexer.scala +++ b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedLexer.scala @@ -168,7 +168,7 @@ object VerifiedLexer { case None() => (Nil(), input) } ret - } ensuring (res => + }.ensuring (res => if (res._1.size > 0) res._2.size < input.size && !res._1.isEmpty else res._2 == input ) @@ -263,7 +263,7 @@ object VerifiedLexer { } } ret - } ensuring (res => res.isEmpty || res.isDefined && (res.get._2.size < input.size && res.get._1.characters ++ res.get._2 == input)) + }.ensuring (res => res.isEmpty || res.isDefined && (res.get._2.size < input.size && res.get._1.characters ++ res.get._2 == input)) /** Finds the biggest prefix matching any rule in the input list of characters If nothing matched a rule, returns None Else, returns the matched * prefix and the remaining suffix @@ -285,7 +285,7 @@ object VerifiedLexer { Some[(Token[C], List[C])]((Token(longestPrefix, rule.tag, rule.isSeparator), suffix)) } - } ensuring (res => + }.ensuring (res => res.isEmpty || matchR( rule.regex, res.get._1.characters @@ -368,7 +368,7 @@ object VerifiedLexer { } } - } ensuring (_ => + }.ensuring (_ => if (ListUtils.getIndex(rules, otherR) < ListUtils.getIndex(rules, r)) !matchR(otherR.regex, otherP) else tokens.size > 0 && otherP.size <= tokens.head.characters.size || !matchR(otherR.regex, otherP) ) @@ -440,7 +440,7 @@ object VerifiedLexer { } case Nil() => () } - } ensuring (_ => lex(rules, printWithSeparatorTokenWhenNeeded(rules, tokens, separatorToken))._1.filter(!_.isSeparator) == tokens) + }.ensuring (_ => lex(rules, printWithSeparatorTokenWhenNeeded(rules, tokens, separatorToken))._1.filter(!_.isSeparator) == tokens) def theoremInvertFromTokensSepTokenBetweenEach[C](rules: List[Rule[C]], tokens: List[Token[C]], separatorToken: Token[C]): Unit = { require(!rules.isEmpty) @@ -562,7 +562,7 @@ object VerifiedLexer { } } - } ensuring (_ => lex(rules, printWithSeparatorToken(tokens, separatorToken))._1.filter(!_.isSeparator) == tokens) + }.ensuring (_ => lex(rules, printWithSeparatorToken(tokens, separatorToken))._1.filter(!_.isSeparator) == tokens) def theoremInvertFromString[C](rules: List[Rule[C]], input: List[C]): Unit = { require(!rules.isEmpty) @@ -594,7 +594,7 @@ object VerifiedLexer { case Nil() => assert(print(tokens) ++ suffix == input) } } - } ensuring (_ => { + }.ensuring (_ => { val (tokens, suffix) = lex(rules, input) print(tokens) ++ suffix == input }) @@ -611,7 +611,7 @@ object VerifiedLexer { } case Nil() => None[Rule[C]]() } - } ensuring (res => res.isEmpty || rules.contains(res.get) && res.get.tag == tag) + }.ensuring (res => res.isEmpty || rules.contains(res.get) && res.get.tag == tag) // Lemmas -------------------------------------------------------------------------------------------------------------------------------- @@ -662,7 +662,7 @@ object VerifiedLexer { case Nil() => () } - } ensuring (_ => + }.ensuring (_ => tokens.isEmpty || (!tokens.isEmpty && maxPrefix(rules, printWithSeparatorTokenWhenNeeded(rules, tokens, separatorToken)).isDefined && @@ -793,7 +793,7 @@ object VerifiedLexer { ) ListUtils.lemmaSamePrefixThenSameSuffix(token.characters, suffix, foundToken.characters, foundSuffix, input) - } ensuring (_ => maxPrefix(rules, token.characters ++ suffix) == Some((token, suffix))) + }.ensuring (_ => maxPrefix(rules, token.characters ++ suffix) == Some((token, suffix))) def lemmaLexIsDefinedWithStrThenLexWithSuffixIsDefined[C](rules: List[Rule[C]], input: List[C], suffix: List[C]): Unit = { require(!rules.isEmpty) @@ -814,7 +814,7 @@ object VerifiedLexer { check(false) } - } ensuring (_ => maxPrefix(rules, input ++ suffix).isDefined) + }.ensuring (_ => maxPrefix(rules, input ++ suffix).isDefined) def lemmaMaxPrefReturnTokenSoItsTagBelongsToARule[C](rules: List[Rule[C]], input: List[C], token: Token[C]): Unit = { require(rulesInvariant(rules)) @@ -839,7 +839,7 @@ object VerifiedLexer { } case Nil() => () } - } ensuring (_ => + }.ensuring (_ => getRuleFromTag(rules, token.tag).isDefined && matchR(getRuleFromTag(rules, token.tag).get.regex, token.characters) && token.isSeparator == getRuleFromTag(rules, token.tag).get.isSeparator ) @@ -854,7 +854,7 @@ object VerifiedLexer { lemmaInvariantOnRulesThenOnTail(newHd, rules) lemmaNoDuplicateAndTagInAccThenRuleCannotHaveSame(rules, getRuleFromTag(rules, tag).get, newHd.tag, List(newHd.tag)) - } ensuring (_ => getRuleFromTag(rules, tag).get == getRuleFromTag(Cons(newHd, rules), tag).get) + }.ensuring (_ => getRuleFromTag(rules, tag).get == getRuleFromTag(Cons(newHd, rules), tag).get) def lemmaRemovingFirstTokensCharactersPreservesLexSuffix[C]( rules: List[Rule[C]], @@ -866,7 +866,7 @@ object VerifiedLexer { require(!rules.isEmpty) require(producedTokens.size > 0) require(lex(rules, input) == (producedTokens, suffix)) - } ensuring (_ => lex(rules, maxPrefix(rules, input).get._2) == (producedTokens.tail, suffix)) + }.ensuring (_ => lex(rules, maxPrefix(rules, input).get._2) == (producedTokens.tail, suffix)) def lemmaMaxPrefNoneThenNoRuleMatches[C](rules: List[Rule[C]], r: Rule[C], p: List[C], input: List[C]): Unit = { require(ListUtils.isPrefix(p, input)) @@ -880,7 +880,7 @@ object VerifiedLexer { lemmaMaxPrefixReturnsNoneThenAnyRuleReturnsNone(r, rules, input) lemmaMaxPrefOneRuleReturnsNoneThenNoPrefMaxRegex(r, p, input) - } ensuring (_ => !matchR(r.regex, p)) + }.ensuring (_ => !matchR(r.regex, p)) def lemmaMaxPrefNoSmallerRuleMatches[C]( rules: List[Rule[C]], @@ -954,7 +954,7 @@ object VerifiedLexer { } case Nil() => check(false) } - } ensuring (_ => !matchR(rBis.regex, p)) + }.ensuring (_ => !matchR(rBis.regex, p)) /** Lemma which proves that indeed the getMaxPrefix indeed returns the maximal prefix that matches any rules * @@ -1002,7 +1002,7 @@ object VerifiedLexer { // Main lemma lemmaMaxPrefixOutputsMaxPrefixInner(rules, r, p, input, pBis, rBis) - } ensuring (_ => !matchR(rBis.regex, pBis)) + }.ensuring (_ => !matchR(rBis.regex, pBis)) def lemmaMaxPrefixOutputsMaxPrefixInner[C]( rules: List[Rule[C]], @@ -1090,7 +1090,7 @@ object VerifiedLexer { } - } ensuring (_ => !matchR(rBis.regex, pBis)) + }.ensuring (_ => !matchR(rBis.regex, pBis)) def lemmaMaxPrefixTagSoFindMaxPrefOneRuleWithThisRule[C]( rules: List[Rule[C]], @@ -1145,7 +1145,7 @@ object VerifiedLexer { } - } ensuring (_ => maxPrefixOneRule(r, input) == Some(Token(p, r.tag, r.isSeparator), suffix)) + }.ensuring (_ => maxPrefixOneRule(r, input) == Some(Token(p, r.tag, r.isSeparator), suffix)) def lemmaNoDuplTagThenTailRulesCannotProduceHeadTagInTok[C](rHead: Rule[C], rTail: List[Rule[C]], input: List[C]): Unit = { require(!rTail.isEmpty) @@ -1165,7 +1165,7 @@ object VerifiedLexer { case Nil() => check(false) } - } ensuring (_ => maxPrefix(rTail, input).isEmpty || maxPrefix(rTail, input).get._1.tag != rHead.tag) + }.ensuring (_ => maxPrefix(rTail, input).isEmpty || maxPrefix(rTail, input).get._1.tag != rHead.tag) def lemmaRuleReturnsPrefixSmallerEqualThanGlobalMaxPref[C]( rules: List[Rule[C]], @@ -1224,7 +1224,7 @@ object VerifiedLexer { case Nil() => check(false) } - } ensuring (_ => pBis.size <= p.size) + }.ensuring (_ => pBis.size <= p.size) def lemmaMaxPrefixReturnsNoneThenAnyRuleReturnsNone[C]( r: Rule[C], @@ -1249,7 +1249,7 @@ object VerifiedLexer { case Nil() => () } - } ensuring (_ => maxPrefixOneRule(r, input).isEmpty) + }.ensuring (_ => maxPrefixOneRule(r, input).isEmpty) def lemmaMaxPrefixOneRuleOutputsMaxPrefix[C]( r: Rule[C], @@ -1279,7 +1279,7 @@ object VerifiedLexer { ListUtils.lemmaIsPrefixRefl(input, input) longestMatchNoBiggerStringMatch(r.regex, input, p, pBis) - } ensuring (_ => !matchR(r.regex, pBis)) + }.ensuring (_ => !matchR(r.regex, pBis)) def lemmaMaxPrefOneRuleReturnsNoneThenNoPrefMaxRegex[C]( r: Rule[C], @@ -1292,7 +1292,7 @@ object VerifiedLexer { longestMatchNoBiggerStringMatch(r.regex, input, Nil(), p) - } ensuring (_ => !matchR(r.regex, p)) + }.ensuring (_ => !matchR(r.regex, p)) def lemmaRuleInListAndRulesValidThenRuleIsValid[C](r: Rule[C], rules: List[Rule[C]]): Unit = { require(rules.contains(r)) @@ -1305,7 +1305,7 @@ object VerifiedLexer { } case Nil() => assert(false) } - } ensuring (_ => ruleValid(r)) + }.ensuring (_ => ruleValid(r)) def lemmaInvariantOnRulesThenOnTail[C](r: Rule[C], rules: List[Rule[C]]): Unit = { require(rulesInvariant(Cons(r, rules))) @@ -1316,7 +1316,7 @@ object VerifiedLexer { lemmaNoDupTagThenAlsoWithSubListAcc(List(r.tag), Nil(), rules) assert(noDuplicateTag(rules, Nil())) - } ensuring (_ => rulesInvariant(rules)) + }.ensuring (_ => rulesInvariant(rules)) def lemmaNoDuplicateCanReorder[C](e1: Rule[C], e2: Rule[C], l: List[Rule[C]]): Unit = { require(noDuplicateTag(Cons(e1, Cons(e2, l)), List())) @@ -1326,7 +1326,7 @@ object VerifiedLexer { assert(List(e2.tag, e1.tag).toSet == List(e1.tag, e2.tag).toSet) lemmaNoDuplicateSameWithAccWithSameContent(l, List(e2.tag, e1.tag), List(e1.tag, e2.tag)) assert(noDuplicateTag(l, List(e2.tag, e1.tag)) == noDuplicateTag(l, List(e1.tag, e2.tag))) // TODO - } ensuring (_ => noDuplicateTag(Cons(e2, Cons(e1, l)), List())) + }.ensuring (_ => noDuplicateTag(Cons(e2, Cons(e1, l)), List())) def lemmaNoDuplicateSameWithAccWithSameContent[C](l: List[Rule[C]], acc: List[String], newAcc: List[String]): Unit = { require(noDuplicateTag(l, acc)) @@ -1343,7 +1343,7 @@ object VerifiedLexer { case Nil() => () } - } ensuring (_ => noDuplicateTag(l, newAcc)) + }.ensuring (_ => noDuplicateTag(l, newAcc)) def lemmaNoDupTagThenAlsoWithSubListAcc[C](acc: List[String], newAcc: List[String], rules: List[Rule[C]]): Unit = { require(ListSpecs.subseq(newAcc, acc)) @@ -1357,7 +1357,7 @@ object VerifiedLexer { case Nil() => () } - } ensuring (_ => noDuplicateTag(rules, newAcc)) + }.ensuring (_ => noDuplicateTag(rules, newAcc)) def lemmaNoDuplicateTagAndDiffIndexThenNoTwoRulesEq[C](rules: List[Rule[C]], r1: Rule[C], r2: Rule[C]): Unit = { require(rules.contains(r1)) @@ -1365,7 +1365,7 @@ object VerifiedLexer { require(noDuplicateTag(rules)) require(ListUtils.getIndex(rules, r1) < ListUtils.getIndex(rules, r2)) - } ensuring (_ => r1 != r2) + }.ensuring (_ => r1 != r2) def lemmaNoDuplicateTagAndDiffIndexThenNoTwoRulesTagsEq[C](rules: List[Rule[C]], r1: Rule[C], r2: Rule[C]): Unit = { require(rules.contains(r1)) @@ -1383,7 +1383,7 @@ object VerifiedLexer { lemmaNoDuplicateTagAndDiffIndexThenNoTwoRulesTagsEq(rules.tail, r1, r2) } - } ensuring (_ => r1.tag != r2.tag) + }.ensuring (_ => r1.tag != r2.tag) def lemmaNoDuplicateAndTagInAccThenRuleCannotHaveSame[C](rules: List[Rule[C]], r: Rule[C], tag: String, acc: List[String]): Unit = { require(acc.contains(tag)) @@ -1395,7 +1395,7 @@ object VerifiedLexer { case Cons(hd, tl) if hd == r => () case Cons(hd, tl) if hd != r => lemmaNoDuplicateAndTagInAccThenRuleCannotHaveSame(tl, r, tag, Cons(hd.tag, acc)) } - } ensuring (_ => r.tag != tag) + }.ensuring (_ => r.tag != tag) def lemmaNonSepRuleNotContainsCharContainedInASepRule[C]( rules: List[Rule[C]], @@ -1419,7 +1419,7 @@ object VerifiedLexer { case Nil() => () } - } ensuring (_ => !usedCharacters(rNSep.regex).contains(c)) + }.ensuring (_ => !usedCharacters(rNSep.regex).contains(c)) def lemmaNonSepRuleNotContainsCharContainedInASepRuleInner[C](rules: List[Rule[C]], rNSep: Rule[C], rSep: Rule[C], c: C): Unit = { require(rulesInvariant(rules)) @@ -1441,7 +1441,7 @@ object VerifiedLexer { case Nil() => () } - } ensuring (_ => !usedCharacters(rNSep.regex).contains(c)) + }.ensuring (_ => !usedCharacters(rNSep.regex).contains(c)) def lemmaSepRuleNotContainsCharContainedInANonSepRule[C]( rules: List[Rule[C]], @@ -1465,7 +1465,7 @@ object VerifiedLexer { case Nil() => () } - } ensuring (_ => !usedCharacters(rSep.regex).contains(c)) + }.ensuring (_ => !usedCharacters(rSep.regex).contains(c)) def lemmaSepRuleNotContainsCharContainedInANonSepRuleInner[C](rules: List[Rule[C]], rNSep: Rule[C], rSep: Rule[C], c: C): Unit = { require(rulesInvariant(rules)) @@ -1487,7 +1487,7 @@ object VerifiedLexer { case Nil() => () } - } ensuring (_ => !usedCharacters(rSep.regex).contains(c)) + }.ensuring (_ => !usedCharacters(rSep.regex).contains(c)) def lemmaRulesProduceEachTokenIndividuallyThenForAnyToken[C](rules: List[Rule[C]], tokens: List[Token[C]], t: Token[C]): Unit = { require(!rules.isEmpty) @@ -1501,7 +1501,7 @@ object VerifiedLexer { case Cons(hd, tl) => lemmaRulesProduceEachTokenIndividuallyThenForAnyToken(rules, tl, t) case Nil() => () } - } ensuring (_ => rulesProduceIndivualToken(rules, t)) + }.ensuring (_ => rulesProduceIndivualToken(rules, t)) } diff --git a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedRegex.scala b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedRegex.scala index 98353899..b8def542 100644 --- a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedRegex.scala +++ b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedRegex.scala @@ -57,7 +57,7 @@ object Memoisation { ) }) } - } ensuring (_ => cache.contains((r, c)) ==> (derivativeStep(r, c) == cache((r, c)))) + }.ensuring (_ => cache.contains((r, c)) ==> (derivativeStep(r, c) == cache((r, c)))) def contains(r: Regex[C], c: C): Boolean = { require(validCacheMap(cache)) @@ -74,7 +74,7 @@ object Memoisation { } else { None() } - } ensuring (res => res.isEmpty || res.get == derivativeStep(r, c)) + }.ensuring (res => res.isEmpty || res.get == derivativeStep(r, c)) def update(r: Regex[C], c: C, res: Regex[C]): Unit = { require(validCacheMap(cache)) @@ -91,7 +91,7 @@ object Memoisation { val _ = cache.update((r, c), res) () - } ensuring (_ => validCacheMap(this.cache)) + }.ensuring (_ => validCacheMap(this.cache)) } } @@ -120,7 +120,7 @@ object VerifiedRegex { case EmptyExpr() => BigInt(1) case EmptyLang() => BigInt(1) } - } ensuring (res => + }.ensuring (res => res > 0 && (r match { case Union(rOne, rTwo) => res > regexDepth(rOne) && res > regexDepth(rTwo) case Concat(rOne, rTwo) => res > regexDepth(rOne) && res > regexDepth(rTwo) @@ -255,7 +255,7 @@ object VerifiedRegexMatcher { } } res - } ensuring (res => validRegex(res)) + }.ensuring (res => validRegex(res)) def derivativeStepMem[C](r: Regex[C], a: C)(implicit cache: Cache[C]): Regex[C] = { require(validRegex(r)) @@ -281,7 +281,7 @@ object VerifiedRegexMatcher { } } - } ensuring (res => res == derivativeStep(r, a)) + }.ensuring (res => res == derivativeStep(r, a)) def derivativeStepMemSimp[C](r: Regex[C], a: C)(implicit cache: Cache[C]): Regex[C] = { require(validRegex(r)) @@ -308,7 +308,7 @@ object VerifiedRegexMatcher { } } - } ensuring (res => res == derivativeStep(r, a)) + }.ensuring (res => res == derivativeStep(r, a)) def derivative[C](r: Regex[C], input: List[C]): Regex[C] = { require(validRegex(r)) @@ -316,7 +316,7 @@ object VerifiedRegexMatcher { case Cons(hd, tl) => derivative(derivativeStep(r, hd), tl) case Nil() => r } - } ensuring (res => validRegex(res)) + }.ensuring (res => validRegex(res)) def derivativeMem[C](r: Regex[C], input: List[C])(implicit cache: Cache[C]): Regex[C] = { require(validRegex(r)) @@ -325,13 +325,13 @@ object VerifiedRegexMatcher { case Cons(hd, tl) => derivative(derivativeStepMem(r, hd)(cache: Cache[C]), tl) case Nil() => r } - } ensuring (res => validRegex(res) && res == derivative(r, input)) + }.ensuring (res => validRegex(res) && res == derivative(r, input)) def matchR[C](r: Regex[C], input: List[C]): Boolean = { require(validRegex(r)) decreases(input.size) if (input.isEmpty) nullable(r) else matchR(derivativeStep(r, input.head), input.tail) - } ensuring (res => + }.ensuring (res => r match { case EmptyExpr() => res == input.isEmpty case EmptyLang() => !res @@ -346,7 +346,7 @@ object VerifiedRegexMatcher { require(cache.valid) decreases(input.size) if (input.isEmpty) nullable(r) else matchRMem(derivativeStepMem(r, input.head)(cache: Cache[C]), input.tail) - } ensuring (res => res == matchR(r, input)) + }.ensuring (res => res == matchR(r, input)) def matchRMemSimp[C](r: Regex[C], input: List[C])(implicit cache: Cache[C]): Boolean = { require(validRegex(r)) @@ -365,7 +365,7 @@ object VerifiedRegexMatcher { } } if (input.isEmpty) nullable(rr) else matchRMemSimp(derivativeStepMem(rr, input.head)(cache: Cache[C]), input.tail) - } ensuring (res => res == matchR(r, input)) + }.ensuring (res => res == matchR(r, input)) @ghost def matchRSpec[C](r: Regex[C], s: List[C]): Boolean = { @@ -445,7 +445,7 @@ object VerifiedRegexMatcher { } } - } ensuring (_ => matchR(r, s) == matchRSpec(r, s)) + }.ensuring (_ => matchR(r, s) == matchRSpec(r, s)) /** Enumerate all cuts in s and returns one that works, i.e., r1 matches s1 and r2 matches s2 Specifically, it is the right most one, i.e., s2 is the largest, if multiple exists Returns None is no valid cut * exists @@ -474,12 +474,12 @@ object VerifiedRegexMatcher { } res - } ensuring (res => (res.isDefined && matchR(r1, res.get._1) && matchR(r2, res.get._2) && res.get._1 ++ res.get._2 == s) || !res.isDefined) + }.ensuring (res => (res.isDefined && matchR(r1, res.get._1) && matchR(r2, res.get._2) && res.get._1 ++ res.get._2 == s) || !res.isDefined) def findLongestMatch[C](r: Regex[C], input: List[C]): (List[C], List[C]) = { require(validRegex(r)) findLongestMatchInner(r, Nil(), input) - } ensuring (res => res._1 ++ res._2 == input) + }.ensuring (res => res._1 ++ res._2 == input) def findLongestMatchInner[C](r: Regex[C], testedP: List[C], totalInput: List[C]): (List[C], List[C]) = { require(validRegex(r)) @@ -514,13 +514,13 @@ object VerifiedRegexMatcher { findLongestMatchInner(derivativeStep(r, suffix.head), newP, totalInput) } } - } ensuring (res => res._1 ++ res._2 == totalInput && (res._1.isEmpty || res._1.size >= testedP.size)) + }.ensuring (res => res._1 ++ res._2 == totalInput && (res._1.isEmpty || res._1.size >= testedP.size)) def findLongestMatchMem[C](r: Regex[C], input: List[C])(implicit cache: Cache[C]): (List[C], List[C]) = { require(validRegex(r)) require(cache.valid) findLongestMatchInnerMem(r, Nil(), input)(cache) - } ensuring (res => res == findLongestMatch(r, input) && cache.valid) + }.ensuring (res => res == findLongestMatch(r, input) && cache.valid) def findLongestMatchInnerMem[C](r: Regex[C], testedP: List[C], totalInput: List[C])(implicit cache: Cache[C]): (List[C], List[C]) = { require(validRegex(r)) @@ -557,7 +557,7 @@ object VerifiedRegexMatcher { findLongestMatchInnerMem(derivativeStepMem(r, suffix.head), newP, totalInput) } } - } ensuring (res => res == findLongestMatchInner(r, testedP, totalInput) && cache.valid) + }.ensuring (res => res == findLongestMatchInner(r, testedP, totalInput) && cache.valid) // Longest match theorems @ghost @@ -565,7 +565,7 @@ object VerifiedRegexMatcher { require(validRegex(r)) longestMatchIsAcceptedByMatchOrIsEmptyRec(r, r, Nil(), input) - } ensuring (_ => findLongestMatchInner(r, Nil(), input)._1.isEmpty || matchR(r, findLongestMatchInner(r, Nil(), input)._1)) + }.ensuring (_ => findLongestMatchInner(r, Nil(), input)._1.isEmpty || matchR(r, findLongestMatchInner(r, Nil(), input)._1)) @ghost def longestMatchNoBiggerStringMatch[C](baseR: Regex[C], input: List[C], returnP: List[C], bigger: List[C]): Unit = { @@ -584,7 +584,7 @@ object VerifiedRegexMatcher { } } - } ensuring (_ => bigger == returnP || !matchR(baseR, bigger)) + }.ensuring (_ => bigger == returnP || !matchR(baseR, bigger)) // ---------------------------------------------------- Regex normalisation and simplification ---------------------------------------------------- @@ -604,7 +604,7 @@ object VerifiedRegexMatcher { case Star(rInner) => Star(removeUselessConcat(rInner)) case _ => r } - } ensuring (res => validRegex(res) && nullable(res) == nullable(r)) + }.ensuring (res => validRegex(res) && nullable(res) == nullable(r)) @ghost def lemmaRemoveUselessConcatSound[C](r: Regex[C], s: List[C]) : Unit = { @@ -814,7 +814,7 @@ object VerifiedRegexMatcher { simpR1 else Concat(simpR1, simpR2) } - } ensuring (res => validRegex(res) && nullable(res) == nullable(r)) + }.ensuring (res => validRegex(res) && nullable(res) == nullable(r)) @ghost def lemmaSimplifySound[C](r: Regex[C], s: List[C]) : Unit = { @@ -1012,7 +1012,7 @@ object VerifiedRegexMatcher { assert(matchR(r, Nil())) assert(nullable(r)) - } ensuring (_ => findLongestMatchInner(r, testedP, input)._1.size >= testedP.size) + }.ensuring (_ => findLongestMatchInner(r, testedP, input)._1.size >= testedP.size) @ghost def lemmaKnownAcceptedStringThenFromSmallPAtLeastThat[C](baseR: Regex[C], r: Regex[C], input: List[C], testedP: List[C], knownP: List[C]): Unit = { @@ -1041,7 +1041,7 @@ object VerifiedRegexMatcher { check(findLongestMatchInner(r, testedP, input)._1.size >= knownP.size) } - } ensuring (_ => findLongestMatchInner(r, testedP, input)._1.size >= knownP.size) + }.ensuring (_ => findLongestMatchInner(r, testedP, input)._1.size >= knownP.size) @ghost def longestMatchIsAcceptedByMatchOrIsEmptyRec[C](baseR: Regex[C], r: Regex[C], testedP: List[C], input: List[C]): Unit = { @@ -1085,7 +1085,7 @@ object VerifiedRegexMatcher { } } - } ensuring (_ => findLongestMatchInner(r, testedP, input)._1.isEmpty || matchR(baseR, findLongestMatchInner(r, testedP, input)._1)) + }.ensuring (_ => findLongestMatchInner(r, testedP, input)._1.isEmpty || matchR(baseR, findLongestMatchInner(r, testedP, input)._1)) @ghost def lemmaMatchRIsSameAsWholeDerivativeAndNil[C](r: Regex[C], input: List[C]): Unit = { @@ -1094,7 +1094,7 @@ object VerifiedRegexMatcher { case Cons(hd, tl) => lemmaMatchRIsSameAsWholeDerivativeAndNil(derivativeStep(r, hd), tl) case Nil() => () } - } ensuring (_ => matchR(r, input) == matchR(derivative(r, input), Nil())) + }.ensuring (_ => matchR(r, input) == matchR(derivative(r, input), Nil())) @ghost def lemmaDerivativeOnLWithANewCharIsANewDerivativeStep[C](baseR: Regex[C], r: Regex[C], input: List[C], c: C): Unit = { @@ -1106,7 +1106,7 @@ object VerifiedRegexMatcher { case Nil() => () } - } ensuring (_ => derivative(baseR, input ++ List(c)) == derivativeStep(r, c)) + }.ensuring (_ => derivative(baseR, input ++ List(c)) == derivativeStep(r, c)) // Basic lemmas @ghost @@ -1114,20 +1114,20 @@ object VerifiedRegexMatcher { require(validRegex(r)) require(s.isEmpty) require(matchR(r, s)) - } ensuring (_ => nullable(r)) + }.ensuring (_ => nullable(r)) @ghost def lemmaRegexAcceptsStringThenDerivativeAcceptsTail[C](r: Regex[C], s: List[C]): Unit = { require(validRegex(r)) require(matchR(r, s)) - } ensuring (_ => if (s.isEmpty) nullable(r) else matchR(derivativeStep(r, s.head), s.tail)) + }.ensuring (_ => if (s.isEmpty) nullable(r) else matchR(derivativeStep(r, s.head), s.tail)) // EmptyString Lemma @ghost def lemmaRegexEmptyStringAcceptsTheEmptyString[C](r: EmptyExpr[C]): Unit = { require(validRegex(r)) - } ensuring (_ => matchR(r, List())) + }.ensuring (_ => matchR(r, List())) // Single Character Lemma @ghost @@ -1138,7 +1138,7 @@ object VerifiedRegexMatcher { ): Unit = { require(validRegex(r) && r == ElementMatch(c)) require(c != d) - } ensuring (_ => matchR(r, List(c)) && !matchR(r, List(d))) + }.ensuring (_ => matchR(r, List(c)) && !matchR(r, List(d))) @ghost def lemmaElementRegexDoesNotAcceptMultipleCharactersString[C]( @@ -1148,7 +1148,7 @@ object VerifiedRegexMatcher { ): Unit = { require(validRegex(r) && r == ElementMatch(c)) require(!s.isEmpty) - } ensuring (_ => !matchR(r, Cons(c, s))) + }.ensuring (_ => !matchR(r, Cons(c, s))) // Union lemmas @ghost @@ -1167,7 +1167,7 @@ object VerifiedRegexMatcher { } case Nil() => assert(matchR(Union(r1, r2), s)) } - } ensuring (_ => matchR(Union(r1, r2), s)) + }.ensuring (_ => matchR(Union(r1, r2), s)) @ghost def lemmaRegexUnionAcceptsThenOneOfTheTwoAccepts[C](r1: Regex[C], r2: Regex[C], s: List[C]): Unit = { @@ -1180,7 +1180,7 @@ object VerifiedRegexMatcher { } case Nil() => } - } ensuring (_ => matchR(r1, s) || matchR(r2, s)) + }.ensuring (_ => matchR(r1, s) || matchR(r2, s)) @ghost def lemmaReversedUnionAcceptsSameString[C]( @@ -1198,7 +1198,7 @@ object VerifiedRegexMatcher { } case Nil() => assert(matchR(Union(r1, r2), s)) } - } ensuring (_ => matchR(Union(r2, r1), s)) + }.ensuring (_ => matchR(Union(r2, r1), s)) // Concat lemmas @@ -1231,7 +1231,7 @@ object VerifiedRegexMatcher { } case Nil() => () } - } ensuring (_ => matchR(Concat(r2, r1), s)) + }.ensuring (_ => matchR(Concat(r2, r1), s)) @ghost def lemmaTwoRegexMatchThenConcatMatchesConcatString[C]( @@ -1272,7 +1272,7 @@ object VerifiedRegexMatcher { lemmaRegexConcatWithNullableAcceptsSameStr(r2, r1, s2) } - } ensuring (_ => matchR(Concat(r1, r2), s1 ++ s2)) + }.ensuring (_ => matchR(Concat(r1, r2), s1 ++ s2)) @ghost def lemmaFindSeparationIsDefinedThenConcatMatches[C](r1: Regex[C], r2: Regex[C], s1: List[C], s2: List[C], s: List[C]): Unit = { @@ -1284,7 +1284,7 @@ object VerifiedRegexMatcher { lemmaTwoRegexMatchThenConcatMatchesConcatString(r1, r2, s1, s2) - } ensuring (_ => matchR(Concat(r1, r2), s1 ++ s2)) + }.ensuring (_ => matchR(Concat(r1, r2), s1 ++ s2)) @ghost def lemmaR1MatchesS1AndR2MatchesS2ThenFindSeparationFindsAtLeastThem[C]( @@ -1334,7 +1334,7 @@ object VerifiedRegexMatcher { } } - } ensuring (_ => findConcatSeparation(r1, r2, s1Rec, s2Rec, s).isDefined) + }.ensuring (_ => findConcatSeparation(r1, r2, s1Rec, s2Rec, s).isDefined) @ghost def lemmaConcatAcceptsStringThenFindSeparationIsDefined[C](r1: Regex[C], r2: Regex[C], s: List[C]): Unit = { @@ -1376,13 +1376,13 @@ object VerifiedRegexMatcher { } } - } ensuring (_ => findConcatSeparation(r1, r2, Nil(), s, s).isDefined) + }.ensuring (_ => findConcatSeparation(r1, r2, Nil(), s, s).isDefined) // Star lemmas @ghost def lemmaStarAcceptsEmptyString[C](r: Star[C]): Unit = { require(validRegex(r)) - } ensuring (_ => matchR(r, List())) + }.ensuring (_ => matchR(r, List())) @ghost def lemmaStarApp[C](r: Regex[C], s1: List[C], s2: List[C]): Unit = { @@ -1397,7 +1397,7 @@ object VerifiedRegexMatcher { } case Nil() => () } - } ensuring (_ => matchR(Star(r), s1 ++ s2)) + }.ensuring (_ => matchR(Star(r), s1 ++ s2)) @ghost def lemmaStarAppConcat[C](r: Regex[C], s: List[C]): Unit = { @@ -1415,7 +1415,7 @@ object VerifiedRegexMatcher { } case Nil() => () } - } ensuring (_ => s.isEmpty || matchR(Concat(r, Star(r)), s)) + }.ensuring (_ => s.isEmpty || matchR(Concat(r, Star(r)), s)) // usedCharacters lemmas --------------------------------------------------------------------------------------------------- @@ -1435,7 +1435,7 @@ object VerifiedRegexMatcher { case Nil() => check(false) } - } ensuring (_ => !matchR(r, s)) + }.ensuring (_ => !matchR(r, s)) @ghost def lemmaRegexCannotMatchAStringStartingWithACharItDoesNotContain[C](r: Regex[C], s: List[C], c: C): Unit = { @@ -1450,7 +1450,7 @@ object VerifiedRegexMatcher { check(false) } - } ensuring (_ => !matchR(r, s)) + }.ensuring (_ => !matchR(r, s)) @ghost def lemmaRegexCannotMatchAStringStartingWithACharWhichIsNotInFirstChars[C](r: Regex[C], s: List[C], c: C): Unit = { @@ -1465,7 +1465,7 @@ object VerifiedRegexMatcher { check(false) } - } ensuring (_ => !matchR(r, s)) + }.ensuring (_ => !matchR(r, s)) // not used @ghost @@ -1509,7 +1509,7 @@ object VerifiedRegexMatcher { } } - } ensuring (_ => usedCharacters(r).contains(c)) + }.ensuring (_ => usedCharacters(r).contains(c)) // DONE @ghost @@ -1615,7 +1615,7 @@ object VerifiedRegexMatcher { } } - } ensuring (_ => usedCharacters(r).contains(c)) + }.ensuring (_ => usedCharacters(r).contains(c)) @ghost def lemmaDerivativeStepDoesNotAddCharToUsedCharacters[C](r: Regex[C], c: C, cNot: C): Unit = { @@ -1646,7 +1646,7 @@ object VerifiedRegexMatcher { } } - } ensuring (_ => !usedCharacters(derivativeStep(r, c)).contains(cNot)) + }.ensuring (_ => !usedCharacters(derivativeStep(r, c)).contains(cNot)) @ghost def lemmaEmptyLangDerivativeIsAFixPoint[C](r: Regex[C], s: List[C]): Unit = { @@ -1656,7 +1656,7 @@ object VerifiedRegexMatcher { case Nil() => () } - } ensuring (_ => derivative(r, s) == r) + }.ensuring (_ => derivative(r, s) == r) @ghost def lemmaUsedCharsContainsAllFirstChars[C](r: Regex[C], c: C): Unit = { @@ -1685,7 +1685,7 @@ object VerifiedRegexMatcher { case Concat(rOne, rTwo) if !nullable(rOne) => lemmaUsedCharsContainsAllFirstChars(rOne, c) } - } ensuring (_ => usedCharacters(r).contains(c)) + }.ensuring (_ => usedCharacters(r).contains(c)) @ghost def lemmaDerivAfterDerivStepIsNullableThenFirstCharsContainsHead[C](r: Regex[C], c: C, tl: List[C]): Unit = { @@ -1786,7 +1786,7 @@ object VerifiedRegexMatcher { } } - } ensuring (_ => firstChars(r).contains(c)) + }.ensuring (_ => firstChars(r).contains(c)) } object Utils { From d80226527042c19cc51e4a83760a4b574c35775d Mon Sep 17 00:00:00 2001 From: Samuel Chassot Date: Tue, 13 Aug 2024 11:34:31 +0200 Subject: [PATCH 21/78] implementation of Zippers (with List for Set, I'm changing to Set to try) --- .../main/scala/ch/epfl/benchmark/Utils.scala | 15 +++ .../main/scala/ch/epfl/lexer/ListSet.scala | 16 ++++ .../src/main/scala/ch/epfl/lexer/Main.scala | 48 +++++++++- .../scala/ch/epfl/lexer/VerifiedRegex.scala | 96 +++++++++++++++++++ lexers/regex/verifiedlexer/verify.sh | 2 +- 5 files changed, 172 insertions(+), 5 deletions(-) create mode 100644 lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/ListSet.scala diff --git a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/benchmark/Utils.scala b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/benchmark/Utils.scala index c3a5e339..dd5c2829 100644 --- a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/benchmark/Utils.scala +++ b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/benchmark/Utils.scala @@ -11,6 +11,7 @@ import stainless.collection._ import ch.epfl.chassot.Hashable import ch.epfl.lexer.VerifiedRegex._ +import ch.epfl.lexer.ZipperRegex._ object RegexUtils { @@ -24,6 +25,20 @@ object RegexUtils { extension (s: String) def * : Regex[Char] = r(s).* extension (s: String) def anyOf: Regex[Char] = s.toCharArray().toList.foldRight[Regex[Char]](EmptyLang())((c, acc) => Union(ElementMatch(c), acc)) extension (s: String) def toStainless: stainless.collection.List[Char] = toStainlessList(s.toCharArray().toList) + extension (r: Regex[Char]) def asString(): String = r match { + case EmptyLang() => "∅" + case EmptyExpr() => "ε" + case ElementMatch(c) => c.toString + case Union(r1, r2) => s"(${r1.asString()} | ${r2.asString()})" + case Concat(r1, r2) => s"${r1.asString()}${r2.asString()}" + case Star(r1) => s"${r1.asString()}*" + } + extension [A] (l: stainless.collection.List[A]) def mkString(inter: String) : String = l match { + case stainless.collection.Nil() => "" + case stainless.collection.Cons(h, t) => h.toString + (if t.isEmpty then "" else inter + t.mkString(inter)) + } + extension (c: Context[Char]) def asStringContext(): String = s"Sequence(${c.map(regex => regex.asString()).mkString(", ")})" + extension (z: Zipper[Char]) def asStringZipper(): String = s"Set(${z.map(c => c.asStringContext()).mkString(", ")})" def toStainlessList(l: scala.collection.immutable.List[Char]): stainless.collection.List[Char] = l match { diff --git a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/ListSet.scala b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/ListSet.scala new file mode 100644 index 00000000..feaf0c06 --- /dev/null +++ b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/ListSet.scala @@ -0,0 +1,16 @@ +package ch.epfl.lexer + +import stainless.collection.* +import stainless.annotation.* +import stainless.lang.* + +object ListSetObj { + case class ListSet[A](toList: List[A]) { + require(ListSpecs.noDuplicate(toList)) + def contains(elem: A): Boolean = toList.contains(elem) + def add(elem: A): ListSet[A] = { + if toList.contains(elem) then this else ListSet(elem :: toList) + }.ensuring(res => res.contains(elem)) + } + +} diff --git a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/Main.scala b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/Main.scala index d61ab010..29ed0091 100644 --- a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/Main.scala +++ b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/Main.scala @@ -4,6 +4,7 @@ import ch.epfl.chassot.MutableHashMap import ch.epfl.lexer.VerifiedRegex._ import ch.epfl.lexer.VerifiedRegexMatcher._ import ch.epfl.lexer.Memoisation._ +import ch.epfl.lexer.ZipperRegex._ import ch.epfl.benchmark.RegexUtils._ import stainless.annotation._ import stainless.lang._ @@ -14,16 +15,55 @@ import scala.collection.View.Empty object Main { def main(args: Array[String]): Unit = { - RegexBenchmark.benchmark01() - RegexBenchmark.benchmark02() - RegexBenchmark.benchmark03() - RegexBenchmark.benchmark03Simp() + testZippers1() + testZippers2() + testZippers3() + // RegexBenchmark.benchmark01() + // RegexBenchmark.benchmark02() + // RegexBenchmark.benchmark03() + // RegexBenchmark.benchmark03Simp() // testRegex() // println("\n\n\n") // testSimp() } } +def testZippers1(): Unit = { + val r = simplify(("a".r ~ "b".r)) + println(s"R = ${r.asString()}") + val z = focus(r) + println(s"Zipper = ${z.asStringZipper()}") + val zAfterA = derivationStepZipper(z, 'a') + val derivativeAfterA = derivativeStep(r, 'a') + println(s"Zipper after 'a' = ${zAfterA.asStringZipper()}") + println(s"Derivative after 'a' = ${derivativeAfterA.asString()}") + println("\n\n-----------------------------------------------------------\n\n") +} + +def testZippers2(): Unit = { + val r = simplify(("a".r ~ "b".r) | ("a".r ~ "c".r)) + println(s"r = ${r.asString()}") + val z = focus(r) + println(s"Zipper = ${z.asStringZipper()}") + val zAfterA = derivationStepZipper(z, 'a') + val derivativeAfterA = derivativeStep(r, 'a') + println(s"Zipper after 'a' = ${zAfterA.asStringZipper()}") + println(s"Derivative after 'a' = ${derivativeAfterA.asString()}") + println("\n\n-----------------------------------------------------------\n\n") +} + +def testZippers3(): Unit = { + val r = simplify((("a".r ~ "b".r) | ("a".r ~ "c".r)).*) + println(s"r = ${r.asString()}") + val z = focus(r) + println(s"Zipper = ${z.asStringZipper()}") + val zAfterA = derivationStepZipper(z, 'a') + val derivativeAfterA = derivativeStep(r, 'a') + println(s"Zipper after 'a' = ${zAfterA.asStringZipper()}") + println(s"Derivative after 'a' = ${derivativeAfterA.asString()}") + println("\n\n-----------------------------------------------------------\n\n") +} + def testRegex(): Unit = { val cache: Cache[Char] = Cache(MutableHashMap.getEmptyHashMap(_ => EmptyLang(), KeyHashable)) val r1 = ("a".r | "b".r).* diff --git a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedRegex.scala b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedRegex.scala index b8def542..ecd66c89 100644 --- a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedRegex.scala +++ b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedRegex.scala @@ -235,6 +235,102 @@ object VerifiedRegex { } } +object ZipperRegex { + import VerifiedRegex.* + import VerifiedRegexMatcher.* + /** + * Context[C] represent sequences of expressions + * Zipper[C] are sets of Context[C], and they represent disjunctions of expressions + */ + type Context[C] = List[Regex[C]] + type Zipper[C] = List[Context[C]] + + @ghost def validContext[C](c: Context[C]): Boolean = c.forall(validRegex) + @ghost def validZipper[C](z: Zipper[C]): Boolean = z.forall(c => c.forall(validRegex)) && ListSpecs.noDuplicate(z) + + def unfocusContext[C](c: Context[C]): Regex[C] = { + require(validContext(c)) + c match { + case Cons(hd, tl) if tl.isEmpty => hd + case Cons(hd, tl) => Concat(hd, unfocusContext(tl)) + case Nil() => EmptyExpr() + } + }.ensuring(res => validRegex(res)) + + def unfocusZipper[C](z: Zipper[C]): Regex[C] = { + require(validZipper(z)) + z match { + case Cons(hd, tl) if tl.isEmpty => unfocusContext(hd) + case Cons(hd, tl) => Union(unfocusContext(hd), unfocusZipper(tl)) + case Nil() => EmptyLang() + } + }.ensuring(res => validRegex(res)) + + def focus[C](r: Regex[C]): Zipper[C] = { + require(validRegex(r)) + List(List(r)) + }.ensuring(res => validZipper(res) && unfocusZipper(res) == r) + + def derivationStepZipperUp[C](context: Context[C], a: C): Zipper[C] = { + require(validContext(context)) + context match { + case Cons(right, parent) if nullable(right) => derivationStepZipperDown(right, parent, a) ++ derivationStepZipperUp(parent, a) + case Cons(right, parent) => derivationStepZipperDown(right, parent, a) + case Nil() => Nil() + } + }.ensuring(res => validZipper(res)) + + def derivationStepZipperDown[C](expr: Regex[C], context: Context[C], a: C): Zipper[C] = { + require(validContext(context)) + require(validRegex(expr)) + expr match { + case ElementMatch(c) if c == a => List(context) + case Union(rOne, rTwo) => derivationStepZipperDown(rOne, context, a) ++ derivationStepZipperDown(rTwo, context, a) + case Concat(rOne, rTwo) if nullable(rOne) => derivationStepZipperDown(rOne, rTwo :: context, a) ++ derivationStepZipperDown(rTwo, context, a) + case Concat(rOne, rTwo) => derivationStepZipperDown(rOne, rTwo :: context, a) + case Star(rInner) => derivationStepZipperDown(rInner, Star(rInner) :: context, a) + case _ => Nil() + } + }.ensuring(res => validZipper(res)) + + def derivationStepZipper[C](z: Zipper[C], a: C): Zipper[C] = { + require(validZipper(z)) + z match { + case Cons(hd, tl) => derivationStepZipperUp(hd, a) ++ derivationStepZipper(tl, a) + case Nil() => Nil() + } + }.ensuring(res => validZipper(res)) + + + // PROOFS ----------------------------------------------------------------------------------------------------- + + /** + * Corresponds to theorem 2.1 of Romain Edelmann's thesis + * + * @return + */ + def theoremUnfocusFocus[C](r: Regex[C], s: List[C]): Boolean = { + require(validRegex(r)) + matchR(unfocusZipper(focus(r)), s) == matchR(r, s) + }.holds + + def theoremUnfocusDerivativeSameAsDerivative[C](r: Regex[C], z: Zipper[C], a: C, s: List[C]): Unit = { + require(validRegex(r)) + require(validZipper(z)) + require(unfocusZipper(z) == r) + + z match + case Nil() => () + case Cons(c1, tl) if tl.isEmpty => () + case Cons(c1, tl) => () + + + + }.ensuring(_ => matchR(unfocusZipper(derivationStepZipper(z, a)), s) == matchR(derivativeStep(r, a), s)) + + +} + object VerifiedRegexMatcher { import VerifiedRegex._ import ListUtils._ diff --git a/lexers/regex/verifiedlexer/verify.sh b/lexers/regex/verifiedlexer/verify.sh index be625159..280d3c7e 100755 --- a/lexers/regex/verifiedlexer/verify.sh +++ b/lexers/regex/verifiedlexer/verify.sh @@ -1 +1 @@ -stainless-dotty --config-file=stainless.conf --watch -D-parallel=12 src/main/scala/ch/epfl/lexer/VerifiedRegex.scala src/main/scala/ch/epfl/lexer/ListUtils.scala src/main/scala/ch/epfl/chassot/* $1 \ No newline at end of file +stainless-dotty --config-file=stainless.conf --watch -D-parallel=12 src/main/scala/ch/epfl/lexer/VerifiedRegex.scala src/main/scala/ch/epfl/lexer/ListUtils.scala src/main/scala/ch/epfl/lexer/ListSet.scala src/main/scala/ch/epfl/chassot/* $1 From 3f07bcb14cd8f67c6db4f7fe7850291ebe9a1b5b Mon Sep 17 00:00:00 2001 From: Samuel Chassot Date: Tue, 13 Aug 2024 15:08:44 +0200 Subject: [PATCH 22/78] continue working on zippers implementation seems done, now working on the proof --- .../main/scala/ch/epfl/lexer/ListUtils.scala | 31 +++++ .../src/main/scala/ch/epfl/lexer/Main.scala | 39 ++++++ .../scala/ch/epfl/lexer/VerifiedRegex.scala | 116 ++++++++++++++++-- lexers/regex/verifiedlexer/stainless.conf | 4 +- 4 files changed, 179 insertions(+), 11 deletions(-) diff --git a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/ListUtils.scala b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/ListUtils.scala index 6cf1abce..0d45a7d4 100644 --- a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/ListUtils.scala +++ b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/ListUtils.scala @@ -474,6 +474,37 @@ object ListUtils { } ensuring (_ => ListSpecs.subseq(l1, l3)) + @inlineOnce + @opaque + @ghost + def lemmaConcatPreservesForall[B](l1: List[B], l2: List[B], p: B => Boolean): Unit = { + require(l1.forall(p)) + require(l2.forall(p)) + decreases(l1) + l1 match { + case Cons(hd, tl) => lemmaConcatPreservesForall(tl, l2, p) + case Nil() => () + } + } ensuring (_ => (l1 ++ l2).forall(p)) + + @inlineOnce + @opaque + @ghost + def lemmaContentSubsetPreservesForall[B](l1: List[B], l2: List[B], p: B => Boolean): Unit = { + require(l1.forall(p)) + require(l2.content.subsetOf(l1.content)) + decreases(l2) + l2 match { + case Cons(hd, tl) => { + lemmaContentSubsetPreservesForall(l1, tl, p) + assert(l1.contains(hd)) + ListSpecs.forallContained(l1, p, hd) + assert(p(hd)) + } + case Nil() => () + } + } ensuring (_ => l2.forall(p)) + @inlineOnce @opaque def lemmaConcatThenFirstSubseqOfTot[B](l1: List[B], l2: List[B]): Unit = { diff --git a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/Main.scala b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/Main.scala index 29ed0091..5403fdcb 100644 --- a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/Main.scala +++ b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/Main.scala @@ -18,6 +18,8 @@ object Main { testZippers1() testZippers2() testZippers3() + println("Running zipper match test...") + testZipperMatch() // RegexBenchmark.benchmark01() // RegexBenchmark.benchmark02() // RegexBenchmark.benchmark03() @@ -64,6 +66,43 @@ def testZippers3(): Unit = { println("\n\n-----------------------------------------------------------\n\n") } +def testZipperMatch(): Unit = { + val r1 = ("a".r | "b".r).* + val z1 = focus(r1) + println(s"r1 = ${r1.asString()}") + println(s"z1 = ${z1.asStringZipper()}") + val s1 = "abababababababababbbababbababbbabab" + println(s"Matching against '$s1'") + val matchResR1 = matchR(r1, s1.toStainless) + val matchResZ1 = matchZipper(z1, s1.toStainless) + println(s"matchResR1 = $matchResR1") + println(s"matchResZ1 = $matchResZ1") + assert(matchResR1 == matchResZ1) + assert(matchResR1 == true) + println("\n\n-----------------------------------------------------------\n\n") + val r2 = "abcdedfghijklmnopqrstuvwxyz.".anyOf.+ ~ "@".r ~ "abcdedfghijklmnopqrstuvwxyz".anyOf.+ ~ ".".r ~ "abcdedfghijklmnopqrstuvwxyz".anyOf.+ + val z2 = focus(r2) + println(s"r2 = ${r2.asString()}") + println(s"z2 = ${z2.asStringZipper()}") + val s2 = "samuel.chassot@epfl.ch" + println(s"Matching against '$s2'") + val matchResR2 = matchR(r2, s2.toStainless) + val matchResZ2 = matchZipper(z2, s2.toStainless) + println(s"matchResR2 = $matchResR2") + println(s"matchResZ2 = $matchResZ2") + assert(matchResR2 == matchResZ2) + assert(matchResR2 == true) + println("\n\n-----------------------------------------------------------\n\n") + val s22 = "samuel.chassot@epfl" + println(s"Matching against '$s22'") + val matchResR22 = matchR(r2, s22.toStainless) + val matchResZ22 = matchZipper(z2, s22.toStainless) + println(s"matchResR22 = $matchResR22") + println(s"matchResZ22 = $matchResZ22") + assert(matchResR22 == matchResZ22) + assert(matchResR22 == false) +} + def testRegex(): Unit = { val cache: Cache[Char] = Cache(MutableHashMap.getEmptyHashMap(_ => EmptyLang(), KeyHashable)) val r1 = ("a".r | "b".r).* diff --git a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedRegex.scala b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedRegex.scala index ecd66c89..9ae852b8 100644 --- a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedRegex.scala +++ b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedRegex.scala @@ -238,6 +238,7 @@ object VerifiedRegex { object ZipperRegex { import VerifiedRegex.* import VerifiedRegexMatcher.* + import ListUtils.* /** * Context[C] represent sequences of expressions * Zipper[C] are sets of Context[C], and they represent disjunctions of expressions @@ -245,8 +246,8 @@ object ZipperRegex { type Context[C] = List[Regex[C]] type Zipper[C] = List[Context[C]] - @ghost def validContext[C](c: Context[C]): Boolean = c.forall(validRegex) - @ghost def validZipper[C](z: Zipper[C]): Boolean = z.forall(c => c.forall(validRegex)) && ListSpecs.noDuplicate(z) + @ghost inline def validContext[C](c: Context[C]): Boolean = c.forall(validRegex) + @ghost inline def validZipper[C](z: Zipper[C]): Boolean = z.forall(c => c.forall(validRegex)) &&& ListSpecs.noDuplicate(z) def unfocusContext[C](c: Context[C]): Regex[C] = { require(validContext(c)) @@ -273,34 +274,107 @@ object ZipperRegex { def derivationStepZipperUp[C](context: Context[C], a: C): Zipper[C] = { require(validContext(context)) + decreases(context) context match { - case Cons(right, parent) if nullable(right) => derivationStepZipperDown(right, parent, a) ++ derivationStepZipperUp(parent, a) + // case Cons(right, parent) if nullable(right) => derivationStepZipperDown(right, parent, a) ++ derivationStepZipperUp(parent, a) + case Cons(right, parent) if nullable(right) => { + ghostExpr({ + lemmaConcatPreservesForall(derivationStepZipperDown(right, parent, a), derivationStepZipperUp(parent, a), c => c.forall(validRegex)) + lemmaContentSubsetPreservesForall( + + derivationStepZipperDown(right, parent, a) ++ derivationStepZipperUp(parent, a), + concatWithoutDuplicates(derivationStepZipperDown(right, parent, a), derivationStepZipperUp(parent, a)), + c => c.forall(validRegex) + ) + }) + concatWithoutDuplicates(derivationStepZipperDown(right, parent, a), derivationStepZipperUp(parent, a)) + } case Cons(right, parent) => derivationStepZipperDown(right, parent, a) case Nil() => Nil() } }.ensuring(res => validZipper(res)) + def derivationStepZipperDown[C](expr: Regex[C], context: Context[C], a: C): Zipper[C] = { require(validContext(context)) require(validRegex(expr)) + decreases(regexDepth(expr)) expr match { case ElementMatch(c) if c == a => List(context) - case Union(rOne, rTwo) => derivationStepZipperDown(rOne, context, a) ++ derivationStepZipperDown(rTwo, context, a) - case Concat(rOne, rTwo) if nullable(rOne) => derivationStepZipperDown(rOne, rTwo :: context, a) ++ derivationStepZipperDown(rTwo, context, a) + case Union(rOne, rTwo) => { + ghostExpr({ + lemmaConcatPreservesForall(derivationStepZipperDown(rOne, context, a), derivationStepZipperDown(rTwo, context, a), c => c.forall(validRegex)) + lemmaContentSubsetPreservesForall( + + derivationStepZipperDown(rOne, context, a) ++ derivationStepZipperDown(rTwo, context, a), + concatWithoutDuplicates(derivationStepZipperDown(rOne, context, a), derivationStepZipperDown(rTwo, context, a)), + c => c.forall(validRegex) + ) + }) + concatWithoutDuplicates(derivationStepZipperDown(rOne, context, a), derivationStepZipperDown(rTwo, context, a)) + } + // case Union(rOne, rTwo) => derivationStepZipperDown(rOne, context, a) ++ derivationStepZipperDown(rTwo, context, a) + case Concat(rOne, rTwo) if nullable(rOne) => { + ghostExpr({ + lemmaConcatPreservesForall(derivationStepZipperDown(rOne, rTwo :: context, a), derivationStepZipperDown(rTwo, context, a), c => c.forall(validRegex)) + lemmaContentSubsetPreservesForall( + + derivationStepZipperDown(rOne, rTwo :: context, a) ++ derivationStepZipperDown(rTwo, context, a), + concatWithoutDuplicates(derivationStepZipperDown(rOne, rTwo :: context, a), derivationStepZipperDown(rTwo, context, a)), + c => c.forall(validRegex) + ) + }) + concatWithoutDuplicates(derivationStepZipperDown(rOne, rTwo :: context, a), derivationStepZipperDown(rTwo, context, a)) + } + // case Concat(rOne, rTwo) if nullable(rOne) => derivationStepZipperDown(rOne, rTwo :: context, a) ++ derivationStepZipperDown(rTwo, context, a) case Concat(rOne, rTwo) => derivationStepZipperDown(rOne, rTwo :: context, a) case Star(rInner) => derivationStepZipperDown(rInner, Star(rInner) :: context, a) case _ => Nil() } }.ensuring(res => validZipper(res)) + @inlineOnce def derivationStepZipper[C](z: Zipper[C], a: C): Zipper[C] = { require(validZipper(z)) + decreases(z) z match { - case Cons(hd, tl) => derivationStepZipperUp(hd, a) ++ derivationStepZipper(tl, a) + case Cons(hd, tl) => { + ghostExpr({ + lemmaConcatPreservesForall(derivationStepZipperUp(hd, a), derivationStepZipper(tl, a), c => c.forall(validRegex)) + lemmaContentSubsetPreservesForall( + + derivationStepZipperUp(hd, a) ++ derivationStepZipper(tl, a), + removeDuplicates(derivationStepZipperUp(hd, a) ++ derivationStepZipper(tl, a)), + c => c.forall(validRegex) + ) + }) + removeDuplicates(derivationStepZipperUp(hd, a) ++ derivationStepZipper(tl, a)) + } case Nil() => Nil() } }.ensuring(res => validZipper(res)) + def nullableContext[C](c: Context[C]): Boolean = { + require(validContext(c)) + c match { + case Cons(hd, tl) => nullable(hd) && nullableContext(tl) + case Nil() => true + } + } + def nullableZipper[C](z: Zipper[C]): Boolean = { + require(validZipper(z)) + z match { + case Cons(hd, tl) => nullableContext(hd) || nullableZipper(tl) + case Nil() => false + } + } + + def matchZipper[C](z: Zipper[C], input: List[C]): Boolean = { + require(validZipper(z)) + decreases(input.size) + if (input.isEmpty) nullableZipper(z) else matchZipper(derivationStepZipper(z, input.head), input.tail) + } + // PROOFS ----------------------------------------------------------------------------------------------------- @@ -314,6 +388,17 @@ object ZipperRegex { matchR(unfocusZipper(focus(r)), s) == matchR(r, s) }.holds + /** + * More or less the theorem 2.2 of Romain Edelmann's thesis + * + * @param r + * @param z + * @param a + * @param s + */ + @inlineOnce + @ghost + @opaque def theoremUnfocusDerivativeSameAsDerivative[C](r: Regex[C], z: Zipper[C], a: C, s: List[C]): Unit = { require(validRegex(r)) require(validZipper(z)) @@ -321,12 +406,25 @@ object ZipperRegex { z match case Nil() => () - case Cons(c1, tl) if tl.isEmpty => () + case Cons(c1, tl) if tl.isEmpty => + assert(unfocusContext(c1) == r) + lemmaUnfocusDerivativesOfContextSameAsDerivative(r, c1, a, s) + case Cons(c1, tl) => () + }.ensuring(_ => matchR(unfocusZipper(derivationStepZipper(z, a)), s) == matchR(derivativeStep(r, a), s)) - - }.ensuring(_ => matchR(unfocusZipper(derivationStepZipper(z, a)), s) == matchR(derivativeStep(r, a), s)) + @inlineOnce + @opaque + @ghost + def lemmaUnfocusDerivativesOfContextSameAsDerivative[C](r: Regex[C], c: Context[C], a: C, s: List[C]): Unit = { + require(validContext(c)) + require(c.forall(validRegex)) + require(validRegex(r)) + require(unfocusContext(c) == r) + + }.ensuring(_ => matchR(unfocusZipper(derivationStepZipperUp(c, a)), s) == matchR(derivativeStep(r, a), s)) + } diff --git a/lexers/regex/verifiedlexer/stainless.conf b/lexers/regex/verifiedlexer/stainless.conf index 5a3ff625..1198636c 100644 --- a/lexers/regex/verifiedlexer/stainless.conf +++ b/lexers/regex/verifiedlexer/stainless.conf @@ -3,7 +3,7 @@ vc-cache = true # debug = ["verification", "smt"] -timeout = 30 +timeout = 60 check-models = false print-ids = false print-types = false @@ -12,4 +12,4 @@ strict-arithmetic = false solvers = "smt-cvc5,smt-z3,smt-cvc4" check-measures = yes infer-measures = true -simplifier = "bland" \ No newline at end of file +simplifier = "bland" From 05a79236afdcae49b916ecf7ed0089daf24810e5 Mon Sep 17 00:00:00 2001 From: Samuel Chassot Date: Wed, 14 Aug 2024 14:27:58 +0200 Subject: [PATCH 23/78] working on zipper --- .../main/scala/ch/epfl/lexer/ListUtils.scala | 107 ++++++++++++++++-- .../scala/ch/epfl/lexer/VerifiedRegex.scala | 28 ++++- lexers/regex/verifiedlexer/stainless.conf | 2 +- 3 files changed, 126 insertions(+), 11 deletions(-) diff --git a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/ListUtils.scala b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/ListUtils.scala index 0d45a7d4..af1e5191 100644 --- a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/ListUtils.scala +++ b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/ListUtils.scala @@ -5,7 +5,7 @@ package ch.epfl.lexer import stainless.annotation._ import stainless.collection._ import stainless.equations._ -import stainless.lang._ +import stainless.lang.{ghost => ghostExpr, _} import stainless.proof.check import scala.annotation.tailrec import stainless.lang.StaticChecks._ @@ -391,11 +391,104 @@ object ListUtils { decreases(newList) newList match { - case Cons(hd, tl) if baseList.contains(hd) => concatWithoutDuplicates(baseList, tl) - case Cons(hd, tl) if !baseList.contains(hd) => concatWithoutDuplicates(Cons(hd, baseList), tl) - case Nil() => baseList + case Cons(hd, tl) if baseList.contains(hd) => { + ghostExpr({ + lemmaSubseqRefl(baseList) + val res = concatWithoutDuplicates(baseList, tl) + assert(ListOps.noDuplicate(res) ) + assert((baseList ++ tl).content == res.content ) + assert(ListSpecs.subseq(res, baseList ++ tl)) + lemmaBiggerSndListPreservesSubSeq(res, baseList, tl, List(hd)) + + lemmaTwoListsConcatAssociativity(baseList, List(hd), tl) + assert( baseList ++ (List(hd) ++ tl) == baseList ++ List(hd) ++ tl) + assert(ListSpecs.subseq(res, baseList ++ (List(hd) ++ tl))) + + assert(isPrefix(baseList, res)) + }) + + concatWithoutDuplicates(baseList, tl) + } + case Cons(hd, tl) if !baseList.contains(hd) => { + ghostExpr({ + lemmaConcatTwoListThenFirstIsPrefix(baseList, List(hd)) + lemmaAppendNewElementElementPreserves(baseList, hd) + val res = concatWithoutDuplicates(baseList ++ List(hd), tl) + assert( ListOps.noDuplicate(res) ) + assert(((baseList ++ List(hd)) ++ newList).content == res.content) + assert(ListSpecs.subseq(res, (baseList ++ List(hd)) ++ tl)) + lemmaTwoListsConcatAssociativity(baseList, List(hd), tl) + assert(ListSpecs.subseq(res, baseList ++ (List(hd) ++ tl))) + + assert(isPrefix(baseList ++ List(hd), res)) + lemmaRemoveLastConcatenatedPrefixStillPrefix(baseList, hd, res) + assert(isPrefix(baseList, res)) + }) + concatWithoutDuplicates(baseList ++ List(hd), tl) + } + case Nil() => { + ghostExpr({ + lemmaSubseqRefl(baseList) + lemmaIsPrefixRefl(baseList, baseList) + }) + baseList + } + } + } ensuring (res => + ListOps.noDuplicate(res) + && (baseList ++ newList).content == res.content + && ListSpecs.subseq(res, baseList ++ newList) + && isPrefix(baseList, res) + ) + + @inlineOnce + @opaque + def lemmaAppendNewElementElementPreserves[B](l: List[B], elmt: B): Unit = { + require(ListSpecs.noDuplicate(l)) + require(!l.contains(elmt)) + decreases(l) + l match { + case Cons(hd, tl) => lemmaAppendNewElementElementPreserves(tl, elmt) + case Nil() => () + } + }.ensuring (_ => ListSpecs.noDuplicate(l ++ List(elmt))) + + @inlineOnce + @opaque + def lemmaBiggerSndListPreservesSubSeq[B](sub: List[B], l1: List[B], l2: List[B], l3: List[B]): Unit = { + require(ListSpecs.subseq(sub, l1 ++ l2)) + decreases((l1 ++ l2).size) + (sub, l1 ++ l2) match { + case (Nil(), _) => () + case (Cons(x, xs), Cons(y, ys)) if l1.isEmpty => lemmaConcatNewListPreservesSubSeq(sub, l3, l2) + case (Cons(x, xs), Cons(y, ys)) if x == y && ListSpecs.subseq(xs, ys) => lemmaBiggerSndListPreservesSubSeq(xs, l1.tail, l2, l3) + case (Cons(x, xs), Cons(y, ys)) => lemmaBiggerSndListPreservesSubSeq(sub, l1.tail, l2, l3) + case _ => () + } + + } ensuring (_ => ListSpecs.subseq(sub, l1 ++ l3 ++ l2)) + + @inlineOnce + @opaque + def lemmaConcatNewListPreservesSubSeq[B](l1: List[B], l2: List[B], l3: List[B]): Unit = { + require(ListSpecs.subseq(l1, l3)) + decreases(l2) + l2 match { + case Cons(hd, tl) => lemmaConcatNewListPreservesSubSeq(l1, tl, l3) + case Nil() => () } - } ensuring (res => ListOps.noDuplicate(res) && (baseList ++ newList).content == res.content) + } ensuring (_ => ListSpecs.subseq(l1, l2 ++ l3)) + + @inlineOnce + @opaque + def lemmaTailOfConcatIsTailConcat[B](l1: List[B], l2: List[B]): Unit = { + require(!l1.isEmpty) + decreases(l1) + l1 match { + case Cons(hd, tl) if !tl.isEmpty => lemmaTailOfConcatIsTailConcat(tl, l2) + case _ => () + } + } ensuring (_ => (l1 ++ l2).tail == l1.tail ++ l2) @inlineOnce @opaque @@ -474,7 +567,7 @@ object ListUtils { } ensuring (_ => ListSpecs.subseq(l1, l3)) - @inlineOnce + // @inlineOnce @opaque @ghost def lemmaConcatPreservesForall[B](l1: List[B], l2: List[B], p: B => Boolean): Unit = { @@ -487,7 +580,7 @@ object ListUtils { } } ensuring (_ => (l1 ++ l2).forall(p)) - @inlineOnce + // @inlineOnce @opaque @ghost def lemmaContentSubsetPreservesForall[B](l1: List[B], l2: List[B], p: B => Boolean): Unit = { diff --git a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedRegex.scala b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedRegex.scala index 9ae852b8..3e23e934 100644 --- a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedRegex.scala +++ b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedRegex.scala @@ -333,7 +333,7 @@ object ZipperRegex { } }.ensuring(res => validZipper(res)) - @inlineOnce + // @inlineOnce def derivationStepZipper[C](z: Zipper[C], a: C): Zipper[C] = { require(validZipper(z)) decreases(z) @@ -344,11 +344,11 @@ object ZipperRegex { lemmaContentSubsetPreservesForall( derivationStepZipperUp(hd, a) ++ derivationStepZipper(tl, a), - removeDuplicates(derivationStepZipperUp(hd, a) ++ derivationStepZipper(tl, a)), + concatWithoutDuplicates(derivationStepZipperUp(hd, a), derivationStepZipper(tl, a)), c => c.forall(validRegex) ) }) - removeDuplicates(derivationStepZipperUp(hd, a) ++ derivationStepZipper(tl, a)) + concatWithoutDuplicates(derivationStepZipperUp(hd, a), derivationStepZipper(tl, a)) } case Nil() => Nil() } @@ -408,7 +408,12 @@ object ZipperRegex { case Nil() => () case Cons(c1, tl) if tl.isEmpty => assert(unfocusContext(c1) == r) + assert(unfocusZipper(z) == unfocusContext(c1)) + lemmaDerivativeOfZipperWithOneContextIsUpOnIt(c1, z, a) + assert(derivationStepZipperUp(c1, a) == derivationStepZipper(z, a)) lemmaUnfocusDerivativesOfContextSameAsDerivative(r, c1, a, s) + assert(matchR(unfocusZipper(derivationStepZipperUp(c1, a)), s) == matchR(derivativeStep(r, a), s)) + check(matchR(unfocusZipper(derivationStepZipper(z, a)), s) == matchR(derivativeStep(r, a), s)) case Cons(c1, tl) => () }.ensuring(_ => matchR(unfocusZipper(derivationStepZipper(z, a)), s) == matchR(derivativeStep(r, a), s)) @@ -425,6 +430,23 @@ object ZipperRegex { }.ensuring(_ => matchR(unfocusZipper(derivationStepZipperUp(c, a)), s) == matchR(derivativeStep(r, a), s)) + @inlineOnce + @opaque + @ghost + def lemmaDerivativeOfZipperWithOneContextIsUpOnIt[C](c: Context[C], z: Zipper[C], a: C): Unit = { + require(validContext(c)) + require(validZipper(z)) + require(z == List(c)) + z match { + case Cons(hd, tl) => { + val res = concatWithoutDuplicates(derivationStepZipperUp(hd, a), derivationStepZipper(tl, a)) + assert(derivationStepZipper(z, a) == res) + assert(derivationStepZipper(tl, a).isEmpty) + assert(res == derivationStepZipperUp(c, a)) + } + case Nil() => check(false) + } + }.ensuring(_ => derivationStepZipper(z, a) == derivationStepZipperUp(c, a)) } diff --git a/lexers/regex/verifiedlexer/stainless.conf b/lexers/regex/verifiedlexer/stainless.conf index 1198636c..21f1c5b1 100644 --- a/lexers/regex/verifiedlexer/stainless.conf +++ b/lexers/regex/verifiedlexer/stainless.conf @@ -3,7 +3,7 @@ vc-cache = true # debug = ["verification", "smt"] -timeout = 60 +timeout = 5 check-models = false print-ids = false print-types = false From d9d54d06f5a40e48463386cbd4c28aa679505ad5 Mon Sep 17 00:00:00 2001 From: Samuel Chassot Date: Wed, 9 Oct 2024 15:05:13 +0200 Subject: [PATCH 24/78] remove chassot pacakge, obsolete --- .../src/main/scala/ch/epfl/chassot/ListLongMap.scala | 1 - .../verifiedlexer/src/main/scala/ch/epfl/chassot/ListMap.scala | 1 - .../src/main/scala/ch/epfl/chassot/MutableHashMap.scala | 1 - .../src/main/scala/ch/epfl/chassot/MutableLongMap.scala | 1 - .../src/main/scala/ch/epfl/chassot/iMutableMaps.scala | 1 - 5 files changed, 5 deletions(-) delete mode 120000 lexers/regex/verifiedlexer/src/main/scala/ch/epfl/chassot/ListLongMap.scala delete mode 120000 lexers/regex/verifiedlexer/src/main/scala/ch/epfl/chassot/ListMap.scala delete mode 120000 lexers/regex/verifiedlexer/src/main/scala/ch/epfl/chassot/MutableHashMap.scala delete mode 120000 lexers/regex/verifiedlexer/src/main/scala/ch/epfl/chassot/MutableLongMap.scala delete mode 120000 lexers/regex/verifiedlexer/src/main/scala/ch/epfl/chassot/iMutableMaps.scala diff --git a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/chassot/ListLongMap.scala b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/chassot/ListLongMap.scala deleted file mode 120000 index 47da45f9..00000000 --- a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/chassot/ListLongMap.scala +++ /dev/null @@ -1 +0,0 @@ -../../../../../../../../../data-structures/maps/mutablemaps/src/main/scala/ch/epfl/chassot/ListLongMap.scala \ No newline at end of file diff --git a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/chassot/ListMap.scala b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/chassot/ListMap.scala deleted file mode 120000 index 5b929f60..00000000 --- a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/chassot/ListMap.scala +++ /dev/null @@ -1 +0,0 @@ -../../../../../../../../../data-structures/maps/mutablemaps/src/main/scala/ch/epfl/chassot/ListMap.scala \ No newline at end of file diff --git a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/chassot/MutableHashMap.scala b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/chassot/MutableHashMap.scala deleted file mode 120000 index 333efe6a..00000000 --- a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/chassot/MutableHashMap.scala +++ /dev/null @@ -1 +0,0 @@ -../../../../../../../../../data-structures/maps/mutablemaps/src/main/scala/ch/epfl/chassot/MutableHashMap.scala \ No newline at end of file diff --git a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/chassot/MutableLongMap.scala b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/chassot/MutableLongMap.scala deleted file mode 120000 index 345ac4c1..00000000 --- a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/chassot/MutableLongMap.scala +++ /dev/null @@ -1 +0,0 @@ -../../../../../../../../../data-structures/maps/mutablemaps/src/main/scala/ch/epfl/chassot/MutableLongMap.scala \ No newline at end of file diff --git a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/chassot/iMutableMaps.scala b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/chassot/iMutableMaps.scala deleted file mode 120000 index 8fe7da3c..00000000 --- a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/chassot/iMutableMaps.scala +++ /dev/null @@ -1 +0,0 @@ -../../../../../../../../../data-structures/maps/mutablemaps/src/main/scala/ch/epfl/chassot/iMutableMaps.scala \ No newline at end of file From 19230d017b15f8dfc142af618403d39a01f911cf Mon Sep 17 00:00:00 2001 From: Samuel Chassot Date: Wed, 9 Oct 2024 15:41:40 +0200 Subject: [PATCH 25/78] finalise merge --- .../src/main/scala/ch/epfl/map/ListMap.scala | 44 ---------- .../main/scala/ch/epfl/lexer/ListSet.scala | 16 ---- .../main/scala/ch/epfl/lexer/ListUtils.scala | 12 +-- .../scala/ch/epfl/lexer/VerifiedRegex.scala | 87 +------------------ 4 files changed, 7 insertions(+), 152 deletions(-) delete mode 100644 lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/ListSet.scala diff --git a/data-structures/maps/mutablemaps/src/main/scala/ch/epfl/map/ListMap.scala b/data-structures/maps/mutablemaps/src/main/scala/ch/epfl/map/ListMap.scala index e9cb9369..c5f5d603 100644 --- a/data-structures/maps/mutablemaps/src/main/scala/ch/epfl/map/ListMap.scala +++ b/data-structures/maps/mutablemaps/src/main/scala/ch/epfl/map/ListMap.scala @@ -406,28 +406,6 @@ object TupleListOpsGenK { case Nil() => () } }.ensuring(_ => getKeysList(l).content - key == getKeysList(removePresrvNoDuplicatedKeys(l, key)).content) - - @opaque - @inlineOnce - def lemmainsertNoDuplicatedKeysPreservesForall[K, B]( - l: List[(K, B)], - key: K, - value: B, - p: ((K, B)) => Boolean - ): Unit = { - require(invariantList(l)) - require(l.forall(p)) - require(p((key, value))) - decreases(l) - - l match { - case Cons(head, tl) if (head._1 != key) => - lemmainsertNoDuplicatedKeysPreservesForall(tl, key, value, p) - case _ => () - } - - }.ensuring(_ => insertNoDuplicatedKeys(l, key, value).forall(p)) - @opaque @inlineOnce @@ -481,28 +459,6 @@ object TupleListOpsGenK { } }.ensuring(_ => l.map(_._1).contains(p._1)) - @opaque - @inlineOnce - def lemmainsertNoDuplicatedKeysPreservesForall[K, B]( - l: List[(K, B)], - key: K, - value: B, - p: ((K, B)) => Boolean - ): Unit = { - require(invariantList(l)) - require(l.forall(p)) - require(p((key, value))) - decreases(l) - - l match { - case Cons(head, tl) if (head._1 != key) => - lemmainsertNoDuplicatedKeysPreservesForall(tl, key, value, p) - case _ => () - } - - }.ensuring(_ => insertNoDuplicatedKeys(l, key, value).forall(p)) - - @opaque @inlineOnce def lemmaForallSubset[K, B]( diff --git a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/ListSet.scala b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/ListSet.scala deleted file mode 100644 index feaf0c06..00000000 --- a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/ListSet.scala +++ /dev/null @@ -1,16 +0,0 @@ -package ch.epfl.lexer - -import stainless.collection.* -import stainless.annotation.* -import stainless.lang.* - -object ListSetObj { - case class ListSet[A](toList: List[A]) { - require(ListSpecs.noDuplicate(toList)) - def contains(elem: A): Boolean = toList.contains(elem) - def add(elem: A): ListSet[A] = { - if toList.contains(elem) then this else ListSet(elem :: toList) - }.ensuring(res => res.contains(elem)) - } - -} diff --git a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/ListUtils.scala b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/ListUtils.scala index 39d8f466..e6b8265d 100644 --- a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/ListUtils.scala +++ b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/ListUtils.scala @@ -434,7 +434,7 @@ object ListUtils { baseList } } - } ensuring (res => + }.ensuring (res => ListOps.noDuplicate(res) && (baseList ++ newList).content == res.content && ListSpecs.subseq(res, baseList ++ newList) @@ -466,7 +466,7 @@ object ListUtils { case _ => () } - } ensuring (_ => ListSpecs.subseq(sub, l1 ++ l3 ++ l2)) + }.ensuring (_ => ListSpecs.subseq(sub, l1 ++ l3 ++ l2)) @inlineOnce @opaque @@ -477,7 +477,7 @@ object ListUtils { case Cons(hd, tl) => lemmaConcatNewListPreservesSubSeq(l1, tl, l3) case Nil() => () } - } ensuring (_ => ListSpecs.subseq(l1, l2 ++ l3)) + }.ensuring (_ => ListSpecs.subseq(l1, l2 ++ l3)) @inlineOnce @opaque @@ -488,7 +488,7 @@ object ListUtils { case Cons(hd, tl) if !tl.isEmpty => lemmaTailOfConcatIsTailConcat(tl, l2) case _ => () } - } ensuring (_ => (l1 ++ l2).tail == l1.tail ++ l2) + }.ensuring (_ => (l1 ++ l2).tail == l1.tail ++ l2) @inlineOnce @opaque @@ -578,7 +578,7 @@ object ListUtils { case Cons(hd, tl) => lemmaConcatPreservesForall(tl, l2, p) case Nil() => () } - } ensuring (_ => (l1 ++ l2).forall(p)) + }.ensuring (_ => (l1 ++ l2).forall(p)) // @inlineOnce @opaque @@ -596,7 +596,7 @@ object ListUtils { } case Nil() => () } - } ensuring (_ => l2.forall(p)) + }.ensuring (_ => l2.forall(p)) @inlineOnce @opaque diff --git a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedRegex.scala b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedRegex.scala index 8ba7d72f..721d0c6c 100644 --- a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedRegex.scala +++ b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedRegex.scala @@ -99,7 +99,7 @@ object Memoisation { object VerifiedRegex { abstract sealed class Regex[C] {} - @ghost + // @ghost def validRegex[C](r: Regex[C]): Boolean = r match { case ElementMatch(c) => true case Star(r) => !nullable(r) && validRegex(r) // && !isEmptyLang(r) @@ -473,61 +473,6 @@ object VerifiedRegexMatcher { res }.ensuring (res => validRegex(res)) -<<<<<<< - def derivativeStepMem[C](r: Regex[C], a: C)(implicit cache: Cache[C]): Regex[C] = { - require(validRegex(r)) - require(cache.valid) - decreases(r) - - cache.get(r, a) match { - case Some(res) => res - case None() => { - val res: Regex[C] = r match { - case EmptyExpr() => EmptyLang() - case EmptyLang() => EmptyLang() - case ElementMatch(c) => if (a == c) EmptyExpr() else EmptyLang() - case Union(rOne, rTwo) => Union(derivativeStepMem(rOne, a)(cache), derivativeStepMem(rTwo, a)(cache)) - case Star(rInner) => Concat(derivativeStepMem(rInner, a)(cache), Star(rInner)) - case Concat(rOne, rTwo) => { - if (nullable(rOne)) Union(Concat(derivativeStepMem(rOne, a)(cache), rTwo), derivativeStepMem(rTwo, a)(cache)) - else Union(Concat(derivativeStepMem(rOne, a)(cache), rTwo), EmptyLang()) - } - } - cache.update(r, a, res) - res - } - } - - }.ensuring (res => res == derivativeStep(r, a)) - - def derivativeStepMemSimp[C](r: Regex[C], a: C)(implicit cache: Cache[C]): Regex[C] = { - require(validRegex(r)) - require(cache.valid) - decreases(r) - - val rr = simplify(r) - cache.get(rr, a) match { - case Some(res) => res - case None() => { - val res: Regex[C] = r match { - case EmptyExpr() => EmptyLang() - case EmptyLang() => EmptyLang() - case ElementMatch(c) => if (a == c) EmptyExpr() else EmptyLang() - case Union(rOne, rTwo) => Union(derivativeStepMem(rOne, a)(cache), derivativeStepMem(rTwo, a)(cache)) - case Star(rInner) => Concat(derivativeStepMem(rInner, a)(cache), Star(rInner)) - case Concat(rOne, rTwo) => { - if (nullable(rOne)) Union(Concat(derivativeStepMem(rOne, a)(cache), rTwo), derivativeStepMem(rTwo, a)(cache)) - else Union(Concat(derivativeStepMem(rOne, a)(cache), rTwo), EmptyLang()) - } - } - cache.update(rr, a, res) - res - } - } - - }.ensuring (res => res == derivativeStep(r, a)) - -======= def derivativeStepMem[C](r: Regex[C], a: C)(implicit cache: Cache[C]): Regex[C] = { require(validRegex(r)) require(cache.valid) @@ -583,7 +528,6 @@ object VerifiedRegexMatcher { // }.ensuring (res => res == derivativeStep(r, a)) ->>>>>>> def derivative[C](r: Regex[C], input: List[C]): Regex[C] = { require(validRegex(r)) input match { @@ -615,35 +559,7 @@ object VerifiedRegexMatcher { } ) -<<<<<<< - def matchRMem[C](r: Regex[C], input: List[C])(implicit cache: Cache[C]): Boolean = { - require(validRegex(r)) - require(cache.valid) - decreases(input.size) - if (input.isEmpty) nullable(r) else matchRMem(derivativeStepMem(r, input.head)(cache: Cache[C]), input.tail) - }.ensuring (res => res == matchR(r, input)) - - def matchRMemSimp[C](r: Regex[C], input: List[C])(implicit cache: Cache[C]): Boolean = { - require(validRegex(r)) - require(cache.valid) - decreases(input.size) - val rr = simplify(r) - if(!input.isEmpty) { - // println(s"derivative wrt ${input.head}") - // println(s"r depth = ${regexDepth(r)}") - // println(s"rr depth = ${regexDepth(rr)}") - if(regexDepth(rr) >= 13) { - // println(s"r = $r") - // println("\n\n\n") - // println(s"rr = $rr") - return false - } - } - if (input.isEmpty) nullable(rr) else matchRMemSimp(derivativeStepMem(rr, input.head)(cache: Cache[C]), input.tail) - }.ensuring (res => res == matchR(r, input)) - @ghost -======= def matchRMem[C](r: Regex[C], input: List[C])(implicit cache: Cache[C]): Boolean = { require(validRegex(r)) require(cache.valid) @@ -672,7 +588,6 @@ object VerifiedRegexMatcher { // }.ensuring (res => res == matchR(r, input)) @ghost ->>>>>>> def matchRSpec[C](r: Regex[C], s: List[C]): Boolean = { require(validRegex(r)) decreases(s.size + regexDepth(r)) From 1a0bb230cd9be77b81f071ee43b120161661ea9e Mon Sep 17 00:00:00 2001 From: Samuel Chassot Date: Thu, 10 Oct 2024 14:11:05 +0200 Subject: [PATCH 26/78] add hashset symlink --- .../src/main/scala/ch/epfl/set/MutableHashSet.scala | 1 + .../src/main/scala/ch/epfl/set/MutableSetsInterface.scala | 1 + 2 files changed, 2 insertions(+) create mode 120000 lexers/regex/verifiedlexer/src/main/scala/ch/epfl/set/MutableHashSet.scala create mode 120000 lexers/regex/verifiedlexer/src/main/scala/ch/epfl/set/MutableSetsInterface.scala diff --git a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/set/MutableHashSet.scala b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/set/MutableHashSet.scala new file mode 120000 index 00000000..b81ff77b --- /dev/null +++ b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/set/MutableHashSet.scala @@ -0,0 +1 @@ +../../../../../../../../../data-structures/sets/mutablesets/src/main/scala/ch/epfl/set/MutableHashSet.scala \ No newline at end of file diff --git a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/set/MutableSetsInterface.scala b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/set/MutableSetsInterface.scala new file mode 120000 index 00000000..ffa3a7cd --- /dev/null +++ b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/set/MutableSetsInterface.scala @@ -0,0 +1 @@ +../../../../../../../../../data-structures/sets/mutablesets/src/main/scala/ch/epfl/set/MutableSetsInterface.scala \ No newline at end of file From bb9487c2a54089f490deb3ce877344ae1c6763c1 Mon Sep 17 00:00:00 2001 From: Samuel Chassot Date: Thu, 10 Oct 2024 14:21:43 +0200 Subject: [PATCH 27/78] add a few imports --- .../src/main/scala/ch/epfl/lexer/VerifiedRegex.scala | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedRegex.scala b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedRegex.scala index 721d0c6c..253cbd82 100644 --- a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedRegex.scala +++ b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedRegex.scala @@ -239,6 +239,8 @@ object ZipperRegex { import VerifiedRegex.* import VerifiedRegexMatcher.* import ListUtils.* + import ch.epfl.set.MutableSetInterface.* + /** * Context[C] represent sequences of expressions * Zipper[C] are sets of Context[C], and they represent disjunctions of expressions From ae4a712309f1f598e5a60a7a616180e0265eebd8 Mon Sep 17 00:00:00 2001 From: Samuel Chassot Date: Thu, 10 Oct 2024 14:48:14 +0200 Subject: [PATCH 28/78] start to migrate to sets --- .../src/main/scala/ch/epfl/lexer/VerifiedRegex.scala | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedRegex.scala b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedRegex.scala index 253cbd82..a3348e72 100644 --- a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedRegex.scala +++ b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedRegex.scala @@ -246,10 +246,10 @@ object ZipperRegex { * Zipper[C] are sets of Context[C], and they represent disjunctions of expressions */ type Context[C] = List[Regex[C]] - type Zipper[C] = List[Context[C]] + type Zipper[C] = MutableSet[Context[C]] @ghost inline def validContext[C](c: Context[C]): Boolean = c.forall(validRegex) - @ghost inline def validZipper[C](z: Zipper[C]): Boolean = z.forall(c => c.forall(validRegex)) &&& ListSpecs.noDuplicate(z) + @ghost inline def validZipper[C](z: Zipper[C]): Boolean = z.toList.abstractSet.forall(c => c.forall(validRegex)) def unfocusContext[C](c: Context[C]): Regex[C] = { require(validContext(c)) @@ -260,6 +260,9 @@ object ZipperRegex { } }.ensuring(res => validRegex(res)) + /** + * For now it is unimplemented as it needs to traverse all elements of the set, which is not possible with this version of the set structure + */ def unfocusZipper[C](z: Zipper[C]): Regex[C] = { require(validZipper(z)) z match { From c555173d0ea72ba8d90852b191e225c78c32f88f Mon Sep 17 00:00:00 2001 From: Samuel Chassot Date: Thu, 10 Oct 2024 15:14:14 +0200 Subject: [PATCH 29/78] continue replacing list by set --- .../scala/ch/epfl/lexer/VerifiedRegex.scala | 82 +++++++++---------- 1 file changed, 41 insertions(+), 41 deletions(-) diff --git a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedRegex.scala b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedRegex.scala index a3348e72..a1f70a00 100644 --- a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedRegex.scala +++ b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedRegex.scala @@ -263,19 +263,19 @@ object ZipperRegex { /** * For now it is unimplemented as it needs to traverse all elements of the set, which is not possible with this version of the set structure */ - def unfocusZipper[C](z: Zipper[C]): Regex[C] = { - require(validZipper(z)) - z match { - case Cons(hd, tl) if tl.isEmpty => unfocusContext(hd) - case Cons(hd, tl) => Union(unfocusContext(hd), unfocusZipper(tl)) - case Nil() => EmptyLang() - } - }.ensuring(res => validRegex(res)) + // def unfocusZipper[C](z: Zipper[C]): Regex[C] = { + // require(validZipper(z)) + // z match { + // case Cons(hd, tl) if tl.isEmpty => unfocusContext(hd) + // case Cons(hd, tl) => Union(unfocusContext(hd), unfocusZipper(tl)) + // case Nil() => EmptyLang() + // } + // }.ensuring(res => validRegex(res)) def focus[C](r: Regex[C]): Zipper[C] = { require(validRegex(r)) List(List(r)) - }.ensuring(res => validZipper(res) && unfocusZipper(res) == r) + }.ensuring(res => validZipper(res))// && unfocusZipper(res) == r) def derivationStepZipperUp[C](context: Context[C], a: C): Zipper[C] = { require(validContext(context)) @@ -388,10 +388,10 @@ object ZipperRegex { * * @return */ - def theoremUnfocusFocus[C](r: Regex[C], s: List[C]): Boolean = { - require(validRegex(r)) - matchR(unfocusZipper(focus(r)), s) == matchR(r, s) - }.holds + // def theoremUnfocusFocus[C](r: Regex[C], s: List[C]): Boolean = { + // require(validRegex(r)) + // matchR(unfocusZipper(focus(r)), s) == matchR(r, s) + // }.holds /** * More or less the theorem 2.2 of Romain Edelmann's thesis @@ -401,39 +401,39 @@ object ZipperRegex { * @param a * @param s */ - @inlineOnce - @ghost - @opaque - def theoremUnfocusDerivativeSameAsDerivative[C](r: Regex[C], z: Zipper[C], a: C, s: List[C]): Unit = { - require(validRegex(r)) - require(validZipper(z)) - require(unfocusZipper(z) == r) + // @inlineOnce + // @ghost + // @opaque + // def theoremUnfocusDerivativeSameAsDerivative[C](r: Regex[C], z: Zipper[C], a: C, s: List[C]): Unit = { + // require(validRegex(r)) + // require(validZipper(z)) + // require(unfocusZipper(z) == r) - z match - case Nil() => () - case Cons(c1, tl) if tl.isEmpty => - assert(unfocusContext(c1) == r) - assert(unfocusZipper(z) == unfocusContext(c1)) - lemmaDerivativeOfZipperWithOneContextIsUpOnIt(c1, z, a) - assert(derivationStepZipperUp(c1, a) == derivationStepZipper(z, a)) - lemmaUnfocusDerivativesOfContextSameAsDerivative(r, c1, a, s) - assert(matchR(unfocusZipper(derivationStepZipperUp(c1, a)), s) == matchR(derivativeStep(r, a), s)) - check(matchR(unfocusZipper(derivationStepZipper(z, a)), s) == matchR(derivativeStep(r, a), s)) + // z match + // case Nil() => () + // case Cons(c1, tl) if tl.isEmpty => + // assert(unfocusContext(c1) == r) + // assert(unfocusZipper(z) == unfocusContext(c1)) + // lemmaDerivativeOfZipperWithOneContextIsUpOnIt(c1, z, a) + // assert(derivationStepZipperUp(c1, a) == derivationStepZipper(z, a)) + // lemmaUnfocusDerivativesOfContextSameAsDerivative(r, c1, a, s) + // assert(matchR(unfocusZipper(derivationStepZipperUp(c1, a)), s) == matchR(derivativeStep(r, a), s)) + // check(matchR(unfocusZipper(derivationStepZipper(z, a)), s) == matchR(derivativeStep(r, a), s)) - case Cons(c1, tl) => () - }.ensuring(_ => matchR(unfocusZipper(derivationStepZipper(z, a)), s) == matchR(derivativeStep(r, a), s)) + // case Cons(c1, tl) => () + // }.ensuring(_ => matchR(unfocusZipper(derivationStepZipper(z, a)), s) == matchR(derivativeStep(r, a), s)) - @inlineOnce - @opaque - @ghost - def lemmaUnfocusDerivativesOfContextSameAsDerivative[C](r: Regex[C], c: Context[C], a: C, s: List[C]): Unit = { - require(validContext(c)) - require(c.forall(validRegex)) - require(validRegex(r)) - require(unfocusContext(c) == r) + // @inlineOnce + // @opaque + // @ghost + // def lemmaUnfocusDerivativesOfContextSameAsDerivative[C](r: Regex[C], c: Context[C], a: C, s: List[C]): Unit = { + // require(validContext(c)) + // require(c.forall(validRegex)) + // require(validRegex(r)) + // require(unfocusContext(c) == r) - }.ensuring(_ => matchR(unfocusZipper(derivationStepZipperUp(c, a)), s) == matchR(derivativeStep(r, a), s)) + // }.ensuring(_ => matchR(unfocusZipper(derivationStepZipperUp(c, a)), s) == matchR(derivativeStep(r, a), s)) @inlineOnce @opaque From ae6f550213a6951c3533ff617a726fe39d525105 Mon Sep 17 00:00:00 2001 From: Samuel Chassot Date: Mon, 21 Oct 2024 16:00:57 +0200 Subject: [PATCH 30/78] Move to Stainless Set, passing tests --- .../main/scala/ch/epfl/benchmark/Utils.scala | 2 +- .../src/main/scala/ch/epfl/lexer/Main.scala | 30 ++-- .../scala/ch/epfl/lexer/OptimisedChecks.scala | 2 +- .../scala/ch/epfl/lexer/VerifiedRegex.scala | 157 ++++++++---------- 4 files changed, 82 insertions(+), 109 deletions(-) diff --git a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/benchmark/Utils.scala b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/benchmark/Utils.scala index dd5c2829..7084ceec 100644 --- a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/benchmark/Utils.scala +++ b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/benchmark/Utils.scala @@ -9,7 +9,7 @@ import stainless.proof.check import stainless.lang.StaticChecks.* import stainless.collection._ -import ch.epfl.chassot.Hashable +import ch.epfl.map.Hashable import ch.epfl.lexer.VerifiedRegex._ import ch.epfl.lexer.ZipperRegex._ diff --git a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/Main.scala b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/Main.scala index 5403fdcb..586bcef5 100644 --- a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/Main.scala +++ b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/Main.scala @@ -1,6 +1,6 @@ package ch.epfl.lexer -import ch.epfl.chassot.MutableHashMap +import ch.epfl.map.MutableHashMap import ch.epfl.lexer.VerifiedRegex._ import ch.epfl.lexer.VerifiedRegexMatcher._ import ch.epfl.lexer.Memoisation._ @@ -9,7 +9,7 @@ import ch.epfl.benchmark.RegexUtils._ import stainless.annotation._ import stainless.lang._ import stainless.collection._ -import ch.epfl.chassot.Hashable +import ch.epfl.map.Hashable import ch.epfl.lexer.RegexBenchmark.testSimp import scala.collection.View.Empty @@ -183,19 +183,19 @@ object RegexBenchmark { assert(match32) } - def benchmark03Simp(): Unit = { - val r = removeUselessConcat((("a".r | "b".r).* | "c".r).*) - println(s"r = $r") - val s = "ababa" - val match31 = matchRMem(r, s.toStainless)(cache) - println(s"Matching $s with r -> $match31") - assert(match31) - - val s2 = "abbbabbabbababccaaaabababbababbbababa" - val match32 = matchRMemSimp(r, s2.toStainless)(cache) - println(s"Matching $s2 with r -> $match32") - assert(match32) - } + // def benchmark03Simp(): Unit = { + // val r = removeUselessConcat((("a".r | "b".r).* | "c".r).*) + // println(s"r = $r") + // val s = "ababa" + // val match31 = matchRMem(r, s.toStainless)(cache) + // println(s"Matching $s with r -> $match31") + // assert(match31) + + // val s2 = "abbbabbabbababccaaaabababbababbbababa" + // val match32 = matchRMemSimp(r, s2.toStainless)(cache) + // println(s"Matching $s2 with r -> $match32") + // assert(match32) + // } def testSimp(): Unit = { val r = Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Concat(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Concat(EmptyLang(),EmptyExpr()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Concat(EmptyLang(),EmptyExpr()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang())),Star(Union(Concat(ElementMatch('a'),EmptyExpr()),Concat(ElementMatch('b'),EmptyExpr())))),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Concat(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Concat(EmptyLang(),EmptyExpr()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Concat(EmptyLang(),EmptyExpr()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang())),Star(Union(Concat(ElementMatch('a'),EmptyExpr()),Concat(ElementMatch('b'),EmptyExpr())))),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Concat(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Concat(EmptyLang(),EmptyExpr()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Concat(EmptyLang(),EmptyExpr()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang())),Star(Union(Concat(ElementMatch('a'),EmptyExpr()),Concat(ElementMatch('b'),EmptyExpr())))),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Concat(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Concat(EmptyLang(),EmptyExpr()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Concat(EmptyLang(),EmptyExpr()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang())),Star(Union(Concat(ElementMatch('a'),EmptyExpr()),Concat(ElementMatch('b'),EmptyExpr())))),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Concat(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Concat(EmptyLang(),EmptyExpr()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Concat(EmptyLang(),EmptyExpr()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang())),Star(Union(Concat(ElementMatch('a'),EmptyExpr()),Concat(ElementMatch('b'),EmptyExpr())))),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Concat(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Concat(EmptyLang(),EmptyExpr()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Concat(EmptyLang(),EmptyExpr()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang())),Star(Union(Concat(ElementMatch('a'),EmptyExpr()),Concat(ElementMatch('b'),EmptyExpr())))),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Concat(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Concat(EmptyLang(),EmptyExpr()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Concat(EmptyLang(),EmptyExpr()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang())),Star(Union(Concat(ElementMatch('a'),EmptyExpr()),Concat(ElementMatch('b'),EmptyExpr())))),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()))))))) diff --git a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/OptimisedChecks.scala b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/OptimisedChecks.scala index 407b7c8b..e90f2cd1 100644 --- a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/OptimisedChecks.scala +++ b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/OptimisedChecks.scala @@ -4,7 +4,7 @@ package ch.epfl.lexer object OptimisedChecks { - extension [T](inline value: T) inline def.ensuring(condition: T => Boolean): T = value + extension [T](inline value: T) inline def ensuring(condition: T => Boolean): T = value inline def require(inline condition: Boolean): Unit = () inline def assert(inline condition: Boolean): Unit = () } diff --git a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedRegex.scala b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedRegex.scala index a1f70a00..b3eeef28 100644 --- a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedRegex.scala +++ b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedRegex.scala @@ -239,17 +239,17 @@ object ZipperRegex { import VerifiedRegex.* import VerifiedRegexMatcher.* import ListUtils.* - import ch.epfl.set.MutableSetInterface.* + import stainless.lang.Set /** * Context[C] represent sequences of expressions * Zipper[C] are sets of Context[C], and they represent disjunctions of expressions */ type Context[C] = List[Regex[C]] - type Zipper[C] = MutableSet[Context[C]] + type Zipper[C] = Set[Context[C]] @ghost inline def validContext[C](c: Context[C]): Boolean = c.forall(validRegex) - @ghost inline def validZipper[C](z: Zipper[C]): Boolean = z.toList.abstractSet.forall(c => c.forall(validRegex)) + @ghost inline def validZipper[C](z: Zipper[C]): Boolean = z.toList.forall(c => c.forall(validRegex)) def unfocusContext[C](c: Context[C]): Regex[C] = { require(validContext(c)) @@ -260,42 +260,40 @@ object ZipperRegex { } }.ensuring(res => validRegex(res)) - /** - * For now it is unimplemented as it needs to traverse all elements of the set, which is not possible with this version of the set structure - */ // def unfocusZipper[C](z: Zipper[C]): Regex[C] = { // require(validZipper(z)) - // z match { - // case Cons(hd, tl) if tl.isEmpty => unfocusContext(hd) - // case Cons(hd, tl) => Union(unfocusContext(hd), unfocusZipper(tl)) - // case Nil() => EmptyLang() - // } + // // z match { + // // case Cons(hd, tl) if tl.isEmpty => unfocusContext(hd) + // // case Cons(hd, tl) => Union(unfocusContext(hd), unfocusZipper(tl)) + // // case Nil() => EmptyLang() + // // } + // z.flatMap(c =>) // }.ensuring(res => validRegex(res)) def focus[C](r: Regex[C]): Zipper[C] = { require(validRegex(r)) - List(List(r)) + Set(List(r)) }.ensuring(res => validZipper(res))// && unfocusZipper(res) == r) def derivationStepZipperUp[C](context: Context[C], a: C): Zipper[C] = { require(validContext(context)) decreases(context) context match { - // case Cons(right, parent) if nullable(right) => derivationStepZipperDown(right, parent, a) ++ derivationStepZipperUp(parent, a) - case Cons(right, parent) if nullable(right) => { - ghostExpr({ - lemmaConcatPreservesForall(derivationStepZipperDown(right, parent, a), derivationStepZipperUp(parent, a), c => c.forall(validRegex)) - lemmaContentSubsetPreservesForall( + case Cons(right, parent) if nullable(right) => derivationStepZipperDown(right, parent, a) ++ derivationStepZipperUp(parent, a) + // case Cons(right, parent) if nullable(right) => { + // ghostExpr({ + // lemmaConcatPreservesForall(derivationStepZipperDown(right, parent, a), derivationStepZipperUp(parent, a), c => c.forall(validRegex)) + // lemmaContentSubsetPreservesForall( - derivationStepZipperDown(right, parent, a) ++ derivationStepZipperUp(parent, a), - concatWithoutDuplicates(derivationStepZipperDown(right, parent, a), derivationStepZipperUp(parent, a)), - c => c.forall(validRegex) - ) - }) - concatWithoutDuplicates(derivationStepZipperDown(right, parent, a), derivationStepZipperUp(parent, a)) - } + // derivationStepZipperDown(right, parent, a) ++ derivationStepZipperUp(parent, a), + // concatWithoutDuplicates(derivationStepZipperDown(right, parent, a), derivationStepZipperUp(parent, a)), + // c => c.forall(validRegex) + // ) + // }) + // concatWithoutDuplicates(derivationStepZipperDown(right, parent, a), derivationStepZipperUp(parent, a)) + // } case Cons(right, parent) => derivationStepZipperDown(right, parent, a) - case Nil() => Nil() + case Nil() => Set() } }.ensuring(res => validZipper(res)) @@ -305,36 +303,24 @@ object ZipperRegex { require(validRegex(expr)) decreases(regexDepth(expr)) expr match { - case ElementMatch(c) if c == a => List(context) - case Union(rOne, rTwo) => { - ghostExpr({ - lemmaConcatPreservesForall(derivationStepZipperDown(rOne, context, a), derivationStepZipperDown(rTwo, context, a), c => c.forall(validRegex)) - lemmaContentSubsetPreservesForall( + case ElementMatch(c) if c == a => Set(context) + case Union(rOne, rTwo) => derivationStepZipperDown(rOne, context, a) ++ derivationStepZipperDown(rTwo, context, a) + // case Concat(rOne, rTwo) if nullable(rOne) => { + // ghostExpr({ + // lemmaConcatPreservesForall(derivationStepZipperDown(rOne, rTwo :: context, a), derivationStepZipperDown(rTwo, context, a), c => c.forall(validRegex)) + // lemmaContentSubsetPreservesForall( - derivationStepZipperDown(rOne, context, a) ++ derivationStepZipperDown(rTwo, context, a), - concatWithoutDuplicates(derivationStepZipperDown(rOne, context, a), derivationStepZipperDown(rTwo, context, a)), - c => c.forall(validRegex) - ) - }) - concatWithoutDuplicates(derivationStepZipperDown(rOne, context, a), derivationStepZipperDown(rTwo, context, a)) - } - // case Union(rOne, rTwo) => derivationStepZipperDown(rOne, context, a) ++ derivationStepZipperDown(rTwo, context, a) - case Concat(rOne, rTwo) if nullable(rOne) => { - ghostExpr({ - lemmaConcatPreservesForall(derivationStepZipperDown(rOne, rTwo :: context, a), derivationStepZipperDown(rTwo, context, a), c => c.forall(validRegex)) - lemmaContentSubsetPreservesForall( - - derivationStepZipperDown(rOne, rTwo :: context, a) ++ derivationStepZipperDown(rTwo, context, a), - concatWithoutDuplicates(derivationStepZipperDown(rOne, rTwo :: context, a), derivationStepZipperDown(rTwo, context, a)), - c => c.forall(validRegex) - ) - }) - concatWithoutDuplicates(derivationStepZipperDown(rOne, rTwo :: context, a), derivationStepZipperDown(rTwo, context, a)) - } - // case Concat(rOne, rTwo) if nullable(rOne) => derivationStepZipperDown(rOne, rTwo :: context, a) ++ derivationStepZipperDown(rTwo, context, a) + // derivationStepZipperDown(rOne, rTwo :: context, a) ++ derivationStepZipperDown(rTwo, context, a), + // concatWithoutDuplicates(derivationStepZipperDown(rOne, rTwo :: context, a), derivationStepZipperDown(rTwo, context, a)), + // c => c.forall(validRegex) + // ) + // }) + // concatWithoutDuplicates(derivationStepZipperDown(rOne, rTwo :: context, a), derivationStepZipperDown(rTwo, context, a)) + // } + case Concat(rOne, rTwo) if nullable(rOne) => derivationStepZipperDown(rOne, rTwo :: context, a) ++ derivationStepZipperDown(rTwo, context, a) case Concat(rOne, rTwo) => derivationStepZipperDown(rOne, rTwo :: context, a) case Star(rInner) => derivationStepZipperDown(rInner, Star(rInner) :: context, a) - case _ => Nil() + case _ => Set() } }.ensuring(res => validZipper(res)) @@ -342,36 +328,22 @@ object ZipperRegex { def derivationStepZipper[C](z: Zipper[C], a: C): Zipper[C] = { require(validZipper(z)) decreases(z) - z match { - case Cons(hd, tl) => { - ghostExpr({ - lemmaConcatPreservesForall(derivationStepZipperUp(hd, a), derivationStepZipper(tl, a), c => c.forall(validRegex)) - lemmaContentSubsetPreservesForall( - - derivationStepZipperUp(hd, a) ++ derivationStepZipper(tl, a), - concatWithoutDuplicates(derivationStepZipperUp(hd, a), derivationStepZipper(tl, a)), - c => c.forall(validRegex) - ) - }) - concatWithoutDuplicates(derivationStepZipperUp(hd, a), derivationStepZipper(tl, a)) - } - case Nil() => Nil() - } + z.flatMap(c => derivationStepZipperUp(c, a)) }.ensuring(res => validZipper(res)) def nullableContext[C](c: Context[C]): Boolean = { require(validContext(c)) - c match { - case Cons(hd, tl) => nullable(hd) && nullableContext(tl) - case Nil() => true - } + c.forall(r => nullable(r)) } def nullableZipper[C](z: Zipper[C]): Boolean = { require(validZipper(z)) - z match { - case Cons(hd, tl) => nullableContext(hd) || nullableZipper(tl) - case Nil() => false - } + z.exists(c => nullableContext(c)) + } + + def matchZipperRomain[C](z: Zipper[C], input: List[C]): Boolean = { + require(validZipper(z)) + decreases(input.size) + if (input.isEmpty) nullableZipper(z) else matchZipperRomain(derivationStepZipper(z, input.head), input.tail) } def matchZipper[C](z: Zipper[C], input: List[C]): Boolean = { @@ -435,23 +407,24 @@ object ZipperRegex { // }.ensuring(_ => matchR(unfocusZipper(derivationStepZipperUp(c, a)), s) == matchR(derivativeStep(r, a), s)) - @inlineOnce - @opaque - @ghost - def lemmaDerivativeOfZipperWithOneContextIsUpOnIt[C](c: Context[C], z: Zipper[C], a: C): Unit = { - require(validContext(c)) - require(validZipper(z)) - require(z == List(c)) - z match { - case Cons(hd, tl) => { - val res = concatWithoutDuplicates(derivationStepZipperUp(hd, a), derivationStepZipper(tl, a)) - assert(derivationStepZipper(z, a) == res) - assert(derivationStepZipper(tl, a).isEmpty) - assert(res == derivationStepZipperUp(c, a)) - } - case Nil() => check(false) - } - }.ensuring(_ => derivationStepZipper(z, a) == derivationStepZipperUp(c, a)) + // @inlineOnce + // @opaque + // @ghost + // def lemmaDerivativeOfZipperWithOneContextIsUpOnIt[C](c: Context[C], z: Zipper[C], a: C): Unit = { + // require(validContext(c)) + // require(validZipper(z)) + // require(z == Set(c)) + // z match { + // case Cons(hd, tl) => { + // val res = derivationStepZipperUp(hd, a) ++ derivationStepZipper(tl, a) + // assert(derivationStepZipper(z, a) == res) + // assert(derivationStepZipper(tl, a).isEmpty) + // assert(res == derivationStepZipperUp(c, a)) + // res + // } + // case Nil() => check(false) + // } + // }.ensuring(_ => derivationStepZipper(z, a) == derivationStepZipperUp(c, a)) } From a1810a84f36755303a21c99f4ea62b3022827971 Mon Sep 17 00:00:00 2001 From: Samuel Chassot Date: Tue, 22 Oct 2024 17:27:47 +0200 Subject: [PATCH 31/78] working on zippers --- .../scala/ch/epfl/lexer/VerifiedRegex.scala | 103 +----------------- lexers/regex/verifiedlexer/stainless.conf | 1 - 2 files changed, 4 insertions(+), 100 deletions(-) diff --git a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedRegex.scala b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedRegex.scala index b3eeef28..9dbcbd28 100644 --- a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedRegex.scala +++ b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedRegex.scala @@ -280,18 +280,6 @@ object ZipperRegex { decreases(context) context match { case Cons(right, parent) if nullable(right) => derivationStepZipperDown(right, parent, a) ++ derivationStepZipperUp(parent, a) - // case Cons(right, parent) if nullable(right) => { - // ghostExpr({ - // lemmaConcatPreservesForall(derivationStepZipperDown(right, parent, a), derivationStepZipperUp(parent, a), c => c.forall(validRegex)) - // lemmaContentSubsetPreservesForall( - - // derivationStepZipperDown(right, parent, a) ++ derivationStepZipperUp(parent, a), - // concatWithoutDuplicates(derivationStepZipperDown(right, parent, a), derivationStepZipperUp(parent, a)), - // c => c.forall(validRegex) - // ) - // }) - // concatWithoutDuplicates(derivationStepZipperDown(right, parent, a), derivationStepZipperUp(parent, a)) - // } case Cons(right, parent) => derivationStepZipperDown(right, parent, a) case Nil() => Set() } @@ -305,18 +293,6 @@ object ZipperRegex { expr match { case ElementMatch(c) if c == a => Set(context) case Union(rOne, rTwo) => derivationStepZipperDown(rOne, context, a) ++ derivationStepZipperDown(rTwo, context, a) - // case Concat(rOne, rTwo) if nullable(rOne) => { - // ghostExpr({ - // lemmaConcatPreservesForall(derivationStepZipperDown(rOne, rTwo :: context, a), derivationStepZipperDown(rTwo, context, a), c => c.forall(validRegex)) - // lemmaContentSubsetPreservesForall( - - // derivationStepZipperDown(rOne, rTwo :: context, a) ++ derivationStepZipperDown(rTwo, context, a), - // concatWithoutDuplicates(derivationStepZipperDown(rOne, rTwo :: context, a), derivationStepZipperDown(rTwo, context, a)), - // c => c.forall(validRegex) - // ) - // }) - // concatWithoutDuplicates(derivationStepZipperDown(rOne, rTwo :: context, a), derivationStepZipperDown(rTwo, context, a)) - // } case Concat(rOne, rTwo) if nullable(rOne) => derivationStepZipperDown(rOne, rTwo :: context, a) ++ derivationStepZipperDown(rTwo, context, a) case Concat(rOne, rTwo) => derivationStepZipperDown(rOne, rTwo :: context, a) case Star(rInner) => derivationStepZipperDown(rInner, Star(rInner) :: context, a) @@ -340,12 +316,6 @@ object ZipperRegex { z.exists(c => nullableContext(c)) } - def matchZipperRomain[C](z: Zipper[C], input: List[C]): Boolean = { - require(validZipper(z)) - decreases(input.size) - if (input.isEmpty) nullableZipper(z) else matchZipperRomain(derivationStepZipper(z, input.head), input.tail) - } - def matchZipper[C](z: Zipper[C], input: List[C]): Boolean = { require(validZipper(z)) decreases(input.size) @@ -355,76 +325,11 @@ object ZipperRegex { // PROOFS ----------------------------------------------------------------------------------------------------- - /** - * Corresponds to theorem 2.1 of Romain Edelmann's thesis - * - * @return - */ - // def theoremUnfocusFocus[C](r: Regex[C], s: List[C]): Boolean = { - // require(validRegex(r)) - // matchR(unfocusZipper(focus(r)), s) == matchR(r, s) - // }.holds - - /** - * More or less the theorem 2.2 of Romain Edelmann's thesis - * - * @param r - * @param z - * @param a - * @param s - */ - // @inlineOnce - // @ghost - // @opaque - // def theoremUnfocusDerivativeSameAsDerivative[C](r: Regex[C], z: Zipper[C], a: C, s: List[C]): Unit = { - // require(validRegex(r)) - // require(validZipper(z)) - // require(unfocusZipper(z) == r) - - // z match - // case Nil() => () - // case Cons(c1, tl) if tl.isEmpty => - // assert(unfocusContext(c1) == r) - // assert(unfocusZipper(z) == unfocusContext(c1)) - // lemmaDerivativeOfZipperWithOneContextIsUpOnIt(c1, z, a) - // assert(derivationStepZipperUp(c1, a) == derivationStepZipper(z, a)) - // lemmaUnfocusDerivativesOfContextSameAsDerivative(r, c1, a, s) - // assert(matchR(unfocusZipper(derivationStepZipperUp(c1, a)), s) == matchR(derivativeStep(r, a), s)) - // check(matchR(unfocusZipper(derivationStepZipper(z, a)), s) == matchR(derivativeStep(r, a), s)) - - // case Cons(c1, tl) => () - // }.ensuring(_ => matchR(unfocusZipper(derivationStepZipper(z, a)), s) == matchR(derivativeStep(r, a), s)) - - - // @inlineOnce - // @opaque - // @ghost - // def lemmaUnfocusDerivativesOfContextSameAsDerivative[C](r: Regex[C], c: Context[C], a: C, s: List[C]): Unit = { - // require(validContext(c)) - // require(c.forall(validRegex)) - // require(validRegex(r)) - // require(unfocusContext(c) == r) - - // }.ensuring(_ => matchR(unfocusZipper(derivationStepZipperUp(c, a)), s) == matchR(derivativeStep(r, a), s)) + def theoremZipperRegexEquiv[C](r: Regex[C], z: Zipper[C], s: List[C]): Unit = { + require(validRegex(r)) + require(validZipper(z)) - // @inlineOnce - // @opaque - // @ghost - // def lemmaDerivativeOfZipperWithOneContextIsUpOnIt[C](c: Context[C], z: Zipper[C], a: C): Unit = { - // require(validContext(c)) - // require(validZipper(z)) - // require(z == Set(c)) - // z match { - // case Cons(hd, tl) => { - // val res = derivationStepZipperUp(hd, a) ++ derivationStepZipper(tl, a) - // assert(derivationStepZipper(z, a) == res) - // assert(derivationStepZipper(tl, a).isEmpty) - // assert(res == derivationStepZipperUp(c, a)) - // res - // } - // case Nil() => check(false) - // } - // }.ensuring(_ => derivationStepZipper(z, a) == derivationStepZipperUp(c, a)) + }.ensuring(_ => matchR(r, s) == matchZipper(focus(r), s)) } diff --git a/lexers/regex/verifiedlexer/stainless.conf b/lexers/regex/verifiedlexer/stainless.conf index 0839d7e4..1bf81008 100644 --- a/lexers/regex/verifiedlexer/stainless.conf +++ b/lexers/regex/verifiedlexer/stainless.conf @@ -13,4 +13,3 @@ solvers = "smt-cvc5,smt-z3,smt-cvc4" check-measures = yes infer-measures = true simplifier = "bland" -compact = true From f727a31d20e64211f4d0fdde90f181d676152915 Mon Sep 17 00:00:00 2001 From: Samuel Chassot Date: Wed, 23 Oct 2024 10:27:35 +0200 Subject: [PATCH 32/78] working --- .../src/main/scala/ch/epfl/lexer/VerifiedRegex.scala | 4 ++-- lexers/regex/verifiedlexer/stainless.conf | 2 +- lexers/regex/verifiedlexer/verify_dev.sh | 9 ++++++++- 3 files changed, 11 insertions(+), 4 deletions(-) diff --git a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedRegex.scala b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedRegex.scala index 9dbcbd28..40205a2f 100644 --- a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedRegex.scala +++ b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedRegex.scala @@ -248,8 +248,8 @@ object ZipperRegex { type Context[C] = List[Regex[C]] type Zipper[C] = Set[Context[C]] - @ghost inline def validContext[C](c: Context[C]): Boolean = c.forall(validRegex) - @ghost inline def validZipper[C](z: Zipper[C]): Boolean = z.toList.forall(c => c.forall(validRegex)) + @ghost def validContext[C](c: Context[C]): Boolean = c.forall(validRegex) + @ghost def validZipper[C](z: Zipper[C]): Boolean = z.toList.forall(c => c.forall(validRegex)) def unfocusContext[C](c: Context[C]): Regex[C] = { require(validContext(c)) diff --git a/lexers/regex/verifiedlexer/stainless.conf b/lexers/regex/verifiedlexer/stainless.conf index 1bf81008..b6fcd3c4 100644 --- a/lexers/regex/verifiedlexer/stainless.conf +++ b/lexers/regex/verifiedlexer/stainless.conf @@ -3,7 +3,7 @@ vc-cache = true # debug = ["verification", "smt"] -timeout = 180 +timeout = 30 check-models = false print-ids = false print-types = false diff --git a/lexers/regex/verifiedlexer/verify_dev.sh b/lexers/regex/verifiedlexer/verify_dev.sh index 04aabbed..a1f03e51 100755 --- a/lexers/regex/verifiedlexer/verify_dev.sh +++ b/lexers/regex/verifiedlexer/verify_dev.sh @@ -1 +1,8 @@ -stainless-dotty --config-file=stainless.conf --watch -D-parallel=12 src/main/scala/ch/epfl/lexer/VerifiedRegex.scala src/main/scala/ch/epfl/lexer/ListUtils.scala src/main/scala/ch/epfl/map/* $1 +stainless-dotty\ + src/main/scala/ch/epfl/lexer/VerifiedRegex.scala\ + src/main/scala/ch/epfl/lexer/VerifiedLexer.scala\ + src/main/scala/ch/epfl/lexer/ListUtils.scala\ + src/main/scala/ch/epfl/map/*\ + --config-file=stainless.conf\ + -D-parallel=12\ + $1 From ba7f96d7267e40e2c6001b638998a7612685daaa Mon Sep 17 00:00:00 2001 From: Samuel Chassot Date: Wed, 23 Oct 2024 11:20:18 +0200 Subject: [PATCH 33/78] remove useless imports --- .../mutablemaps/src/main/scala/ch/epfl/map/ListLongMap.scala | 1 - .../maps/mutablemaps/src/main/scala/ch/epfl/map/ListMap.scala | 2 -- 2 files changed, 3 deletions(-) diff --git a/data-structures/maps/mutablemaps/src/main/scala/ch/epfl/map/ListLongMap.scala b/data-structures/maps/mutablemaps/src/main/scala/ch/epfl/map/ListLongMap.scala index 735d2146..4a651f99 100644 --- a/data-structures/maps/mutablemaps/src/main/scala/ch/epfl/map/ListLongMap.scala +++ b/data-structures/maps/mutablemaps/src/main/scala/ch/epfl/map/ListLongMap.scala @@ -9,7 +9,6 @@ import stainless.equations._ import stainless.lang._ import stainless.proof.check import scala.annotation.tailrec -import scala.collection.immutable // Uncomment the following import to run benchmarks // import OptimisedChecks.* diff --git a/data-structures/maps/mutablemaps/src/main/scala/ch/epfl/map/ListMap.scala b/data-structures/maps/mutablemaps/src/main/scala/ch/epfl/map/ListMap.scala index c5f5d603..299aac32 100644 --- a/data-structures/maps/mutablemaps/src/main/scala/ch/epfl/map/ListMap.scala +++ b/data-structures/maps/mutablemaps/src/main/scala/ch/epfl/map/ListMap.scala @@ -9,9 +9,7 @@ import stainless.equations._ import stainless.lang._ import stainless.proof.check import scala.annotation.tailrec -import scala.collection.immutable import stainless.collection.ListOps.noDuplicate -import scala.collection.mutable // Uncomment the following import to run benchmarks // import OptimisedChecks.* From 8f6282bf44791c2342c58d5adec0a382e8d4ef29 Mon Sep 17 00:00:00 2001 From: Samuel Chassot Date: Thu, 24 Oct 2024 09:10:36 +0200 Subject: [PATCH 34/78] work on zippers --- .../lexer/{ListUtils.scala => Utils.scala} | 34 +++++++++++++++++++ .../scala/ch/epfl/lexer/VerifiedRegex.scala | 7 ++-- lexers/regex/verifiedlexer/stainless.conf | 2 +- lexers/regex/verifiedlexer/verify.sh | 2 +- lexers/regex/verifiedlexer/verify_dev.sh | 2 +- 5 files changed, 40 insertions(+), 7 deletions(-) rename lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/{ListUtils.scala => Utils.scala} (95%) diff --git a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/ListUtils.scala b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/Utils.scala similarity index 95% rename from lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/ListUtils.scala rename to lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/Utils.scala index e6b8265d..d0b2ab99 100644 --- a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/ListUtils.scala +++ b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/Utils.scala @@ -10,6 +10,21 @@ import stainless.proof.check import scala.annotation.tailrec import stainless.lang.StaticChecks._ +object SetUtils { + + @ghost + @opaque + @inlineOnce + def lemmaConcatPreservesForall[A](l1: Set[A], l2: Set[A], p: A => Boolean): Unit = { + require(l1.forall(p)) + require(l2.forall(p)) + ListUtils.lemmaConcatPreservesForall(l1.toList, l2.toList, p) + assert((l1.toList ++ l2.toList).forall(p)) + ListUtils.lemmaForallThenOnContent(l1.toList ++ l2.toList, p) + assert((l1.toList ++ l2.toList).content.forall(p)) + }.ensuring (_ => (l1 ++ l2).forall(p)) +} + object ListUtils { def isPrefix[B](prefix: List[B], l: List[B]): Boolean = { decreases(prefix) @@ -75,6 +90,25 @@ object ListUtils { } } + @inlineOnce + @opaque + @ghost + def lemmaForallThenOnContent[B](l: List[B], p: B => Boolean): Unit = { + require(l.forall(p)) + decreases(l) + l match { + case Cons(hd, tl) => { + lemmaForallThenOnContent(tl, p) + assert(tl.content.forall(p)) + assert(l.content == tl.content ++ Set(hd)) + assert(p(hd)) + assert((tl.content ++ Set(hd)).toList.content.subsetOf(l.content)) + lemmaContentSubsetPreservesForall(l, (tl.content ++ Set(hd)).toList, p) + } + case Nil() => () + } + }.ensuring (_ => l.content.forall(p)) + @inlineOnce @opaque def lemmaConsecutiveSubseqThenSubseq[B](l1: List[B], l2: List[B]): Unit = { diff --git a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedRegex.scala b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedRegex.scala index 40205a2f..824778c1 100644 --- a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedRegex.scala +++ b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedRegex.scala @@ -248,8 +248,8 @@ object ZipperRegex { type Context[C] = List[Regex[C]] type Zipper[C] = Set[Context[C]] - @ghost def validContext[C](c: Context[C]): Boolean = c.forall(validRegex) - @ghost def validZipper[C](z: Zipper[C]): Boolean = z.toList.forall(c => c.forall(validRegex)) + @ghost inline def validContext[C](c: Context[C]): Boolean = c.forall(validRegex) + @ghost inline def validZipper[C](z: Zipper[C]): Boolean = z.forall(c => validContext(c)) def unfocusContext[C](c: Context[C]): Regex[C] = { require(validContext(c)) @@ -273,7 +273,7 @@ object ZipperRegex { def focus[C](r: Regex[C]): Zipper[C] = { require(validRegex(r)) Set(List(r)) - }.ensuring(res => validZipper(res))// && unfocusZipper(res) == r) + }.ensuring(res => validZipper(res)) def derivationStepZipperUp[C](context: Context[C], a: C): Zipper[C] = { require(validContext(context)) @@ -303,7 +303,6 @@ object ZipperRegex { // @inlineOnce def derivationStepZipper[C](z: Zipper[C], a: C): Zipper[C] = { require(validZipper(z)) - decreases(z) z.flatMap(c => derivationStepZipperUp(c, a)) }.ensuring(res => validZipper(res)) diff --git a/lexers/regex/verifiedlexer/stainless.conf b/lexers/regex/verifiedlexer/stainless.conf index b6fcd3c4..8f1a2aa7 100644 --- a/lexers/regex/verifiedlexer/stainless.conf +++ b/lexers/regex/verifiedlexer/stainless.conf @@ -3,7 +3,7 @@ vc-cache = true # debug = ["verification", "smt"] -timeout = 30 +timeout = 10 check-models = false print-ids = false print-types = false diff --git a/lexers/regex/verifiedlexer/verify.sh b/lexers/regex/verifiedlexer/verify.sh index b281ce09..1753a525 100755 --- a/lexers/regex/verifiedlexer/verify.sh +++ b/lexers/regex/verifiedlexer/verify.sh @@ -1,7 +1,7 @@ stainless-dotty\ src/main/scala/ch/epfl/lexer/VerifiedRegex.scala\ src/main/scala/ch/epfl/lexer/VerifiedLexer.scala\ - src/main/scala/ch/epfl/lexer/ListUtils.scala\ + src/main/scala/ch/epfl/lexer/Utils.scala\ src/main/scala/ch/epfl/map/*\ --config-file=stainless.conf\ -D-parallel=12 --functions=Memoisation._,VerifiedRegex_,VerifiedRegexMatcher._,VerifiedLexer._,ListUtils._\ diff --git a/lexers/regex/verifiedlexer/verify_dev.sh b/lexers/regex/verifiedlexer/verify_dev.sh index a1f03e51..139b274e 100755 --- a/lexers/regex/verifiedlexer/verify_dev.sh +++ b/lexers/regex/verifiedlexer/verify_dev.sh @@ -1,7 +1,7 @@ stainless-dotty\ src/main/scala/ch/epfl/lexer/VerifiedRegex.scala\ src/main/scala/ch/epfl/lexer/VerifiedLexer.scala\ - src/main/scala/ch/epfl/lexer/ListUtils.scala\ + src/main/scala/ch/epfl/lexer/Utils.scala\ src/main/scala/ch/epfl/map/*\ --config-file=stainless.conf\ -D-parallel=12\ From 677aa75ac802865352d1f5e15b0c44608150db6a Mon Sep 17 00:00:00 2001 From: Samuel Chassot Date: Thu, 31 Oct 2024 16:15:48 +0100 Subject: [PATCH 35/78] add some lemmas about flatmap --- .../src/main/scala/ch/epfl/lexer/Utils.scala | 78 ++++++++++++++++--- 1 file changed, 67 insertions(+), 11 deletions(-) diff --git a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/Utils.scala b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/Utils.scala index d0b2ab99..66a7ca61 100644 --- a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/Utils.scala +++ b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/Utils.scala @@ -11,18 +11,66 @@ import scala.annotation.tailrec import stainless.lang.StaticChecks._ object SetUtils { + @opaque + @inlineOnce + def lemmaConcatPreservesForall[A](s1: Set[A], s2: Set[A], p: A => Boolean): Unit = { + require(s1.forall(p)) + require(s2.forall(p)) + ListUtils.lemmaConcatPreservesForall(s1.toList, s2.toList, p) + assert((s1.toList ++ s2.toList).forall(p)) + ListUtils.lemmaForallThenOnContent(s1.toList ++ s2.toList, p) + assert((s1.toList ++ s2.toList).content.forall(p)) + }.ensuring (_ => (s1 ++ s2).forall(p)) + @inlineOnce + @opaque @ghost + def lemmaFlatMapForallElem[A, B](s: Set[A], f: A => Set[B], p: B => Boolean, elm: B): Unit = { + require(s.flatMap(f).contains(elm)) + require(Forall.Forall[A](a => f(a).forall(p))) + + unfold(s.flatMapPost(f)(elm)) + assert(s.flatMap(f).contains(elm)) + assert(s.exists(a => f(a).contains(elm))) + assert(s.toList.exists(a => f(a).contains(elm))) + val witness = ListUtils.getWitness(s.toList, a => f(a).contains(elm)) + assert(f(witness).contains(elm)) + assert(f(witness).toList.contains(elm)) + unfold(Forall.Forall[A](a => f(a).forall(p))) + assert(f(witness).forall(p)) + assert(f(witness).toList.forall(p)) + ListSpecs.forallContained(f(witness).toList, p, elm) + }.ensuring (_ => p(elm)) + + @inlineOnce @opaque + @ghost + def lemmaFlatMapForallToList[A, B](s: Set[A], f: A => Set[B], p: B => Boolean, l: List[B]): Unit = { + require(Forall.Forall[A](a => f(a).forall(p))) + require(ListSpecs.subseq(l, s.flatMap(f).toList)) + decreases(l) + l match + case Cons(hd, tl) => + ListSpecs.subseqContains(l, s.flatMap(f).toList, hd) + assert(s.flatMap(f).contains(hd)) + unfold(s.flatMapPost(f)(hd)) + lemmaFlatMapForallElem(s, f, p, hd) + ListSpecs.subseqTail(l, s.flatMap(f).toList) + lemmaFlatMapForallToList(s, f, p, tl) + case Nil() => () + + }.ensuring (_ => l.forall(p)) + @inlineOnce - def lemmaConcatPreservesForall[A](l1: Set[A], l2: Set[A], p: A => Boolean): Unit = { - require(l1.forall(p)) - require(l2.forall(p)) - ListUtils.lemmaConcatPreservesForall(l1.toList, l2.toList, p) - assert((l1.toList ++ l2.toList).forall(p)) - ListUtils.lemmaForallThenOnContent(l1.toList ++ l2.toList, p) - assert((l1.toList ++ l2.toList).content.forall(p)) - }.ensuring (_ => (l1 ++ l2).forall(p)) + @opaque + @ghost + def lemmaFlatMapForall[A, B](s: Set[A], f: A => Set[B], p: B => Boolean): Unit = { + require(Forall.Forall[A](a => f(a).forall(p))) + ListUtils.lemmaSubseqRefl(s.flatMap(f).toList) + lemmaFlatMapForallToList(s, f, p, s.flatMap(f).toList) + + }.ensuring (_ => s.flatMap(f).forall(p)) + } object ListUtils { @@ -90,9 +138,19 @@ object ListUtils { } } + @ghost + def getWitness[B](l: List[B], p: B => Boolean): B = { + require(l.exists(p)) + decreases(l) + l match { + case Cons(hd, tl) if p(hd) => hd + case Cons(hd, tl) => getWitness(tl, p) + case Nil() => check(false); l.head + } + }.ensuring(res => p(res) && l.contains(res)) + @inlineOnce @opaque - @ghost def lemmaForallThenOnContent[B](l: List[B], p: B => Boolean): Unit = { require(l.forall(p)) decreases(l) @@ -603,7 +661,6 @@ object ListUtils { // @inlineOnce @opaque - @ghost def lemmaConcatPreservesForall[B](l1: List[B], l2: List[B], p: B => Boolean): Unit = { require(l1.forall(p)) require(l2.forall(p)) @@ -616,7 +673,6 @@ object ListUtils { // @inlineOnce @opaque - @ghost def lemmaContentSubsetPreservesForall[B](l1: List[B], l2: List[B], p: B => Boolean): Unit = { require(l1.forall(p)) require(l2.content.subsetOf(l1.content)) From d51738e7cfd3512d4b8af2167e7e6f8c16fef9f5 Mon Sep 17 00:00:00 2001 From: Samuel Chassot Date: Mon, 4 Nov 2024 10:09:32 +0100 Subject: [PATCH 36/78] add getwitness for sets --- .../src/main/scala/ch/epfl/lexer/Utils.scala | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/Utils.scala b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/Utils.scala index 66a7ca61..63ae6e0d 100644 --- a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/Utils.scala +++ b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/Utils.scala @@ -11,6 +11,15 @@ import scala.annotation.tailrec import stainless.lang.StaticChecks._ object SetUtils { + + @ghost + def getWitness[A](s: Set[A], p: A => Boolean): A = { + require(s.exists(p)) + ListUtils.getWitness(s.toList, p) + }.ensuring(res => p(res) && s.contains(res)) + + + @ghost @opaque @inlineOnce def lemmaConcatPreservesForall[A](s1: Set[A], s2: Set[A], p: A => Boolean): Unit = { From e1e828c02e3166a9426c03d8654a71bd107a2dc6 Mon Sep 17 00:00:00 2001 From: Samuel Chassot Date: Mon, 4 Nov 2024 17:16:01 +0100 Subject: [PATCH 37/78] working on zppier --- .../scala/ch/epfl/lexer/VerifiedRegex.scala | 35 +++++++++++++++++-- 1 file changed, 32 insertions(+), 3 deletions(-) diff --git a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedRegex.scala b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedRegex.scala index 824778c1..eba0cc85 100644 --- a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedRegex.scala +++ b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedRegex.scala @@ -279,7 +279,9 @@ object ZipperRegex { require(validContext(context)) decreases(context) context match { - case Cons(right, parent) if nullable(right) => derivationStepZipperDown(right, parent, a) ++ derivationStepZipperUp(parent, a) + case Cons(right, parent) if nullable(right) => + ghostExpr(SetUtils.lemmaConcatPreservesForall(derivationStepZipperDown(right, parent, a), derivationStepZipperUp(parent, a), validContext)) + derivationStepZipperDown(right, parent, a) ++ derivationStepZipperUp(parent, a) case Cons(right, parent) => derivationStepZipperDown(right, parent, a) case Nil() => Set() } @@ -292,8 +294,12 @@ object ZipperRegex { decreases(regexDepth(expr)) expr match { case ElementMatch(c) if c == a => Set(context) - case Union(rOne, rTwo) => derivationStepZipperDown(rOne, context, a) ++ derivationStepZipperDown(rTwo, context, a) - case Concat(rOne, rTwo) if nullable(rOne) => derivationStepZipperDown(rOne, rTwo :: context, a) ++ derivationStepZipperDown(rTwo, context, a) + case Union(rOne, rTwo) => + ghostExpr(SetUtils.lemmaConcatPreservesForall(derivationStepZipperDown(rOne, context, a), derivationStepZipperDown(rTwo, context, a), validContext)) + derivationStepZipperDown(rOne, context, a) ++ derivationStepZipperDown(rTwo, context, a) + case Concat(rOne, rTwo) if nullable(rOne) => + ghostExpr(SetUtils.lemmaConcatPreservesForall(derivationStepZipperDown(rOne, rTwo :: context, a), derivationStepZipperDown(rTwo, context, a), validContext)) + derivationStepZipperDown(rOne, rTwo :: context, a) ++ derivationStepZipperDown(rTwo, context, a) case Concat(rOne, rTwo) => derivationStepZipperDown(rOne, rTwo :: context, a) case Star(rInner) => derivationStepZipperDown(rInner, Star(rInner) :: context, a) case _ => Set() @@ -303,6 +309,7 @@ object ZipperRegex { // @inlineOnce def derivationStepZipper[C](z: Zipper[C], a: C): Zipper[C] = { require(validZipper(z)) + ghostExpr(SetUtils.lemmaFlatMapForall(z, (c: Context[C]) => derivationStepZipperUp(c, a), validContext)) z.flatMap(c => derivationStepZipperUp(c, a)) }.ensuring(res => validZipper(res)) @@ -331,6 +338,28 @@ object ZipperRegex { }.ensuring(_ => matchR(r, s) == matchZipper(focus(r), s)) + // LEMMAS ----------------------------------------------------------------------------------------------------- + + @ghost + @inlineOnce + @opaque + def lemmaFlatMapDerivationUpForallValidToList[C](z: Zipper[C], a:C, l: List[Context[C]]): Unit = { + require(validZipper(z)) + require(ListSpecs.subseq(l, z.flatMap(c => derivationStepZipperUp(c, a)).toList)) + decreases(l) + l match + case Cons(hd, tl) => + ListSpecs.subseqContains(l, z.flatMap(c => derivationStepZipperUp(c, a)).toList, hd) + assert(z.flatMap(c => derivationStepZipperUp(c, a)).contains(hd)) + unfold(z.flatMapPost(c => derivationStepZipperUp(c, a))(hd)) + // lemmaFlatMapForallElem(z, c => derivationStepZipperUp(c, a), validContext, hd) + ListSpecs.subseqTail(l, z.flatMap(c => derivationStepZipperUp(c, a)).toList) + lemmaFlatMapDerivationUpForallValidToList(z, a, tl) + case Nil() => () + + }.ensuring (_ => l.forall(validContext)) + + } object VerifiedRegexMatcher { From bb3c2f801a05f08daead0cf162988e38bf94188b Mon Sep 17 00:00:00 2001 From: Samuel Chassot Date: Tue, 5 Nov 2024 14:23:07 +0100 Subject: [PATCH 38/78] working --- .../src/main/scala/ch/epfl/lexer/VerifiedRegex.scala | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedRegex.scala b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedRegex.scala index 824778c1..38069604 100644 --- a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedRegex.scala +++ b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedRegex.scala @@ -279,7 +279,9 @@ object ZipperRegex { require(validContext(context)) decreases(context) context match { - case Cons(right, parent) if nullable(right) => derivationStepZipperDown(right, parent, a) ++ derivationStepZipperUp(parent, a) + case Cons(right, parent) if nullable(right) => + SetUtils.lemmaConcatPreservesForall(derivationStepZipperDown(right, parent, a), derivationStepZipperUp(parent, a), validContext) + derivationStepZipperDown(right, parent, a) ++ derivationStepZipperUp(parent, a) case Cons(right, parent) => derivationStepZipperDown(right, parent, a) case Nil() => Set() } @@ -292,8 +294,12 @@ object ZipperRegex { decreases(regexDepth(expr)) expr match { case ElementMatch(c) if c == a => Set(context) - case Union(rOne, rTwo) => derivationStepZipperDown(rOne, context, a) ++ derivationStepZipperDown(rTwo, context, a) - case Concat(rOne, rTwo) if nullable(rOne) => derivationStepZipperDown(rOne, rTwo :: context, a) ++ derivationStepZipperDown(rTwo, context, a) + case Union(rOne, rTwo) => + SetUtils.lemmaConcatPreservesForall(derivationStepZipperDown(rOne, context, a), derivationStepZipperDown(rTwo, context, a), validContext) + derivationStepZipperDown(rOne, context, a) ++ derivationStepZipperDown(rTwo, context, a) + case Concat(rOne, rTwo) if nullable(rOne) => + SetUtils.lemmaConcatPreservesForall(derivationStepZipperDown(rOne, rTwo :: context, a), derivationStepZipperDown(rTwo, context, a), validContext) + derivationStepZipperDown(rOne, rTwo :: context, a) ++ derivationStepZipperDown(rTwo, context, a) case Concat(rOne, rTwo) => derivationStepZipperDown(rOne, rTwo :: context, a) case Star(rInner) => derivationStepZipperDown(rInner, Star(rInner) :: context, a) case _ => Set() From 2f538fb7ebd12314beb4026aa3cc354fdc204456 Mon Sep 17 00:00:00 2001 From: Samuel Chassot Date: Tue, 5 Nov 2024 14:30:21 +0100 Subject: [PATCH 39/78] change to ghost --- .../src/main/scala/ch/epfl/lexer/Utils.scala | 40 +++++++++++++++++++ .../scala/ch/epfl/lexer/VerifiedLexer.scala | 2 +- .../scala/ch/epfl/lexer/VerifiedRegex.scala | 16 ++++---- 3 files changed, 49 insertions(+), 9 deletions(-) diff --git a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/Utils.scala b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/Utils.scala index 63ae6e0d..86883803 100644 --- a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/Utils.scala +++ b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/Utils.scala @@ -160,6 +160,7 @@ object ListUtils { @inlineOnce @opaque + @ghost def lemmaForallThenOnContent[B](l: List[B], p: B => Boolean): Unit = { require(l.forall(p)) decreases(l) @@ -178,6 +179,7 @@ object ListUtils { @inlineOnce @opaque + @ghost def lemmaConsecutiveSubseqThenSubseq[B](l1: List[B], l2: List[B]): Unit = { require(consecutiveSubseq(l1, l2)) decreases(l2) @@ -191,6 +193,7 @@ object ListUtils { @inlineOnce @opaque + @ghost def lemmaContainsAndNotHdThenTlContains[B](l: List[B], e: B): Unit = { require(l.contains(e)) require(l.head != e) @@ -199,6 +202,7 @@ object ListUtils { @inlineOnce @opaque + @ghost def lemmaGetIndexBiggerAndHeadNotEqThenTailContains[B](l: List[B], e1: B, e2: B): Unit = { require(l.contains(e1) && l.contains(e2)) require(e1 != e2) @@ -224,6 +228,7 @@ object ListUtils { @inlineOnce @opaque + @ghost def lemmaSameIndexThenSameElement[B](l: List[B], e1: B, e2: B): Unit = { require(l.contains(e1)) require(l.contains(e2)) @@ -241,6 +246,7 @@ object ListUtils { @inlineOnce @opaque + @ghost def lemmaGetIndexBiggerAndHeadEqThenTailContains[B](l: List[B], e1: B, e2: B): Unit = { require(l.contains(e1) && l.contains(e2)) require(e1 != e2) @@ -251,6 +257,7 @@ object ListUtils { @inlineOnce @opaque + @ghost def lemmaNotHeadSoGetIndexTailIsMinusOne[B](l: List[B], e: B): Unit = { require(l.contains(e)) require(l.head != e) @@ -263,6 +270,7 @@ object ListUtils { @inlineOnce @opaque + @ghost def lemmaIsPrefixRefl[B](l1: List[B], l2: List[B]): Unit = { decreases(l1) require(l1 == l2) @@ -274,6 +282,7 @@ object ListUtils { @inlineOnce @opaque + @ghost def lemmaConcatTwoListThenFirstIsPrefix[B](l1: List[B], l2: List[B]): Unit = { decreases(l1.size) l1 match { @@ -284,6 +293,7 @@ object ListUtils { @inlineOnce @opaque + @ghost def lemmaLongerPrefixContainsHeadOfSuffixOfSmallerPref[B](p1: List[B], s1: List[B], p2: List[B], l: List[B]): Unit = { require(isPrefix(p2, l)) require(p1 ++ s1 == l) @@ -301,6 +311,7 @@ object ListUtils { @inlineOnce @opaque + @ghost def lemmaConcatAssociativity[B](l1: List[B], elmt: B, l2: List[B], tot: List[B]): Unit = { require((l1 ++ List(elmt)) ++ l2 == tot) decreases(l1) @@ -313,6 +324,7 @@ object ListUtils { @inlineOnce @opaque + @ghost def lemmaTwoListsConcatAssociativity[B]( l1: List[B], l2: List[B], @@ -330,6 +342,7 @@ object ListUtils { @inlineOnce @opaque + @ghost def lemmaRemoveLastConcatenatedPrefixStillPrefix[B](l: List[B], elmt: B, tot: List[B]): Unit = { require(isPrefix(l ++ List(elmt), tot)) decreases(l) @@ -341,6 +354,7 @@ object ListUtils { @inlineOnce @opaque + @ghost def lemmaRemoveLastPrefixStillPrefix[B](p: List[B], l: List[B]): Unit = { require(!l.isEmpty) require(isPrefix(p, l)) @@ -355,6 +369,7 @@ object ListUtils { @inlineOnce @opaque + @ghost def lemmaPrefixStaysPrefixWhenAddingToSuffix[B](p: List[B], l: List[B], suffix: List[B]): Unit = { require(isPrefix(p, l)) decreases(p) @@ -366,12 +381,14 @@ object ListUtils { @inlineOnce @opaque + @ghost def lemmaRemoveLastPrefixDecreasesSize[B](l: List[B]): Unit = { require(l.size > 0) }.ensuring (_ => removeLast(l).size < l.size) @inlineOnce @opaque + @ghost def lemmaIsPrefixSameLengthThenSameList[B](p1: List[B], p2: List[B], l: List[B]): Unit = { require(isPrefix(p1, l)) require(isPrefix(p2, l)) @@ -387,6 +404,7 @@ object ListUtils { @inlineOnce @opaque + @ghost def lemmaRemoveLastFromBothSidePreservesEq[B](p: List[B], s: List[B], l: List[B]): Unit = { require(p ++ s == l) require(!s.isEmpty) @@ -399,6 +417,7 @@ object ListUtils { @inlineOnce @opaque + @ghost def lemmaRemoveLastFromLMakesItPrefix[B](l: List[B]): Unit = { require(!l.isEmpty) decreases(l.size) @@ -411,6 +430,7 @@ object ListUtils { @inlineOnce @opaque + @ghost def lemmaSamePrefixThenSameSuffix[B](p1: List[B], s1: List[B], p2: List[B], s2: List[B], l: List[B]): Unit = { require(isPrefix(p1, l)) require(isPrefix(p2, l)) @@ -426,6 +446,7 @@ object ListUtils { @inlineOnce @opaque + @ghost def lemmaIsPrefixThenSmallerEqSize[B](p: List[B], l: List[B]): Unit = { require(isPrefix(p, l)) decreases(p) @@ -438,6 +459,7 @@ object ListUtils { @inlineOnce @opaque + @ghost def lemmaAddHeadSuffixToPrefixStillPrefix[B](p: List[B], l: List[B]): Unit = { require(isPrefix(p, l)) require(p.size < l.size) @@ -450,6 +472,7 @@ object ListUtils { @inlineOnce @opaque + @ghost def lemmaGetSuffixOnListWithItSelfIsEmpty[B](l: List[B]): Unit = { decreases(l.size) lemmaIsPrefixRefl(l, l) @@ -461,6 +484,7 @@ object ListUtils { @inlineOnce @opaque + @ghost def lemmaMoveElementToOtherListKeepsConcatEq[B](s1: List[B], hd2: B, tl2: List[B], tot: List[B]): Unit = { require(s1 ++ Cons(hd2, tl2) == tot) decreases(s1) @@ -473,6 +497,7 @@ object ListUtils { @inlineOnce @opaque + @ghost def lemmaPrefixFromSameListAndStrictlySmallerThenPrefixFromEachOther[B](s1: List[B], s2: List[B], l: List[B]): Unit = { require(isPrefix(s1, l)) require(isPrefix(s2, l)) @@ -544,6 +569,7 @@ object ListUtils { @inlineOnce @opaque + @ghost def lemmaAppendNewElementElementPreserves[B](l: List[B], elmt: B): Unit = { require(ListSpecs.noDuplicate(l)) require(!l.contains(elmt)) @@ -556,6 +582,7 @@ object ListUtils { @inlineOnce @opaque + @ghost def lemmaBiggerSndListPreservesSubSeq[B](sub: List[B], l1: List[B], l2: List[B], l3: List[B]): Unit = { require(ListSpecs.subseq(sub, l1 ++ l2)) decreases((l1 ++ l2).size) @@ -571,6 +598,7 @@ object ListUtils { @inlineOnce @opaque + @ghost def lemmaConcatNewListPreservesSubSeq[B](l1: List[B], l2: List[B], l3: List[B]): Unit = { require(ListSpecs.subseq(l1, l3)) decreases(l2) @@ -582,6 +610,7 @@ object ListUtils { @inlineOnce @opaque + @ghost def lemmaTailOfConcatIsTailConcat[B](l1: List[B], l2: List[B]): Unit = { require(!l1.isEmpty) decreases(l1) @@ -605,6 +634,7 @@ object ListUtils { @inlineOnce @opaque + @ghost def lemmaSubseqRefl[B](l: List[B]): Unit = { decreases(l.size) l match { @@ -615,6 +645,7 @@ object ListUtils { @inlineOnce @opaque + @ghost def lemmaTailIsSubseqOfList[B](elmt: B, l: List[B]): Unit = { l match { @@ -630,6 +661,7 @@ object ListUtils { @inlineOnce @opaque + @ghost def lemmaSubSeqTransitive[B](l1: List[B], l2: List[B], l3: List[B]): Unit = { require(ListSpecs.subseq(l1, l2)) require(ListSpecs.subseq(l2, l3)) @@ -670,6 +702,7 @@ object ListUtils { // @inlineOnce @opaque + @ghost def lemmaConcatPreservesForall[B](l1: List[B], l2: List[B], p: B => Boolean): Unit = { require(l1.forall(p)) require(l2.forall(p)) @@ -682,6 +715,7 @@ object ListUtils { // @inlineOnce @opaque + @ghost def lemmaContentSubsetPreservesForall[B](l1: List[B], l2: List[B], p: B => Boolean): Unit = { require(l1.forall(p)) require(l2.content.subsetOf(l1.content)) @@ -699,6 +733,7 @@ object ListUtils { @inlineOnce @opaque + @ghost def lemmaConcatThenFirstSubseqOfTot[B](l1: List[B], l2: List[B]): Unit = { decreases(l1) l1 match { @@ -709,6 +744,7 @@ object ListUtils { @inlineOnce @opaque + @ghost def lemmaConcatThenSecondSubseqOfTot[B](l1: List[B], l2: List[B]): Unit = { decreases(l1) l1 match { @@ -719,6 +755,7 @@ object ListUtils { @inlineOnce @opaque + @ghost def lemmaConcatTwoListsWhichNotContainThenTotNotContain[B](l1: List[B], l2: List[B], b: B): Unit = { require(!l1.contains(b)) require(!l2.contains(b)) @@ -733,6 +770,7 @@ object ListUtils { @inlineOnce @opaque + @ghost def lemmaForallContainsThenForEqualLists[B](l1: List[B], l2: List[B], l1Bis: List[B], l2Bis: List[B]): Unit = { require(l1.forall(b => l2.contains(b))) require(l1 == l1Bis) @@ -742,6 +780,7 @@ object ListUtils { @inlineOnce @opaque + @ghost def lemmaForallContainsAndNoDuplicateThenSmallerList[B](l: List[B], lIn: List[B]): Unit = { require(lIn.forall(e => l.contains(e))) require(ListOps.noDuplicate(lIn)) @@ -770,6 +809,7 @@ object ListUtils { @inlineOnce @opaque + @ghost def lemmaRemoveElmtContainedSizeSmaller[B](l: List[B], e: B): Unit = { require(l.contains(e)) decreases(l) diff --git a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedLexer.scala b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedLexer.scala index 90eca502..2b9380e0 100644 --- a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedLexer.scala +++ b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedLexer.scala @@ -247,7 +247,7 @@ object VerifiedLexer { require(!rulesArg.isEmpty) decreases(rulesArg.size) - ListUtils.lemmaIsPrefixRefl(input, input) + ghostExpr(ListUtils.lemmaIsPrefixRefl(input, input)) val ret: Option[(Token[C], List[C])] = rulesArg match { case Cons(hd, Nil()) => maxPrefixOneRule(hd, input) case Cons(hd, tl) => { diff --git a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedRegex.scala b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedRegex.scala index eba0cc85..695c8d22 100644 --- a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedRegex.scala +++ b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedRegex.scala @@ -624,16 +624,16 @@ object VerifiedRegexMatcher { (Nil[C](), totalInput) } } else { - ListUtils.lemmaIsPrefixThenSmallerEqSize(testedP, totalInput) + ghostExpr(ListUtils.lemmaIsPrefixThenSmallerEqSize(testedP, totalInput)) if (testedP.size == totalInput.size) { - ListUtils.lemmaIsPrefixRefl(totalInput, totalInput) - ListUtils.lemmaIsPrefixSameLengthThenSameList(totalInput, testedP, totalInput) + ghostExpr(ListUtils.lemmaIsPrefixRefl(totalInput, totalInput)) + ghostExpr(ListUtils.lemmaIsPrefixSameLengthThenSameList(totalInput, testedP, totalInput)) check(false) } assert(testedP.size < totalInput.size) val suffix = ListUtils.getSuffix(totalInput, testedP) val newP = testedP ++ List(suffix.head) - lemmaAddHeadSuffixToPrefixStillPrefix(testedP, totalInput) + ghostExpr(lemmaAddHeadSuffixToPrefixStillPrefix(testedP, totalInput)) if (nullable(r)) { val recursive = findLongestMatchInner(derivativeStep(r, suffix.head), newP, totalInput) if (recursive._1.isEmpty) { @@ -666,16 +666,16 @@ object VerifiedRegexMatcher { (Nil[C](), totalInput) } } else { - ListUtils.lemmaIsPrefixThenSmallerEqSize(testedP, totalInput) + ghostExpr(ListUtils.lemmaIsPrefixThenSmallerEqSize(testedP, totalInput)) if (testedP.size == totalInput.size) { - ListUtils.lemmaIsPrefixRefl(totalInput, totalInput) - ListUtils.lemmaIsPrefixSameLengthThenSameList(totalInput, testedP, totalInput) + ghostExpr(ListUtils.lemmaIsPrefixRefl(totalInput, totalInput)) + ghostExpr(ListUtils.lemmaIsPrefixSameLengthThenSameList(totalInput, testedP, totalInput)) check(false) } assert(testedP.size < totalInput.size) val suffix = ListUtils.getSuffix(totalInput, testedP) val newP = testedP ++ List(suffix.head) - lemmaAddHeadSuffixToPrefixStillPrefix(testedP, totalInput) + ghostExpr(lemmaAddHeadSuffixToPrefixStillPrefix(testedP, totalInput)) check(newP.size > testedP.size) if (nullable(r)) { val recursive = findLongestMatchInnerMem(derivativeStepMem(r, suffix.head), newP, totalInput) From 7088f44049460013da3e11a96e94d42f191184c1 Mon Sep 17 00:00:00 2001 From: Samuel Chassot Date: Wed, 6 Nov 2024 12:05:18 +0100 Subject: [PATCH 40/78] change Context to be a class with invariant to emulate refinement types --- .../scala/ch/epfl/lexer/VerifiedRegex.scala | 123 ++++++++---------- 1 file changed, 57 insertions(+), 66 deletions(-) diff --git a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedRegex.scala b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedRegex.scala index 695c8d22..1ce3ee37 100644 --- a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedRegex.scala +++ b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedRegex.scala @@ -98,6 +98,18 @@ object Memoisation { object VerifiedRegex { abstract sealed class Regex[C] {} + case class ElementMatch[C](c: C) extends Regex[C] + case class Star[C](reg: Regex[C]) extends Regex[C] + case class Union[C](regOne: Regex[C], regTwo: Regex[C]) extends Regex[C] + case class Concat[C](regOne: Regex[C], regTwo: Regex[C]) extends Regex[C] + + /** Regex that accepts only the empty string: represents the language {""} + */ + case class EmptyExpr[C]() extends Regex[C] + + /** Regex that accepts nothing: represents the empty language + */ + case class EmptyLang[C]() extends Regex[C] // @ghost def validRegex[C](r: Regex[C]): Boolean = r match { @@ -129,19 +141,6 @@ object VerifiedRegex { }) ) - case class ElementMatch[C](c: C) extends Regex[C] - case class Star[C](reg: Regex[C]) extends Regex[C] - case class Union[C](regOne: Regex[C], regTwo: Regex[C]) extends Regex[C] - case class Concat[C](regOne: Regex[C], regTwo: Regex[C]) extends Regex[C] - - /** Regex that accepts only the empty string: represents the language {""} - */ - case class EmptyExpr[C]() extends Regex[C] - - /** Regex that accepts nothing: represents the empty language - */ - case class EmptyLang[C]() extends Regex[C] - def usedCharacters[C](r: Regex[C]): List[C] = { r match { case EmptyExpr() => Nil[C]() @@ -245,23 +244,27 @@ object ZipperRegex { * Context[C] represent sequences of expressions * Zipper[C] are sets of Context[C], and they represent disjunctions of expressions */ - type Context[C] = List[Regex[C]] + case class Context[C](exprs: List[Regex[C]]){ + require(exprs.forall(validRegex)) + inline def prepend(r: Regex[C]): Context[C] = { + require(validRegex(r)) + Context(r :: exprs) + } + inline def forall(p: Regex[C] => Boolean): Boolean = exprs.forall(p) + inline def isEmpty: Boolean = exprs.isEmpty + } type Zipper[C] = Set[Context[C]] - @ghost inline def validContext[C](c: Context[C]): Boolean = c.forall(validRegex) - @ghost inline def validZipper[C](z: Zipper[C]): Boolean = z.forall(c => validContext(c)) - def unfocusContext[C](c: Context[C]): Regex[C] = { - require(validContext(c)) - c match { + c.exprs match { case Cons(hd, tl) if tl.isEmpty => hd - case Cons(hd, tl) => Concat(hd, unfocusContext(tl)) + case Cons(hd, tl) => Concat(hd, unfocusContext(Context(tl))) case Nil() => EmptyExpr() } }.ensuring(res => validRegex(res)) // def unfocusZipper[C](z: Zipper[C]): Regex[C] = { - // require(validZipper(z)) + // // // z match { // // case Cons(hd, tl) if tl.isEmpty => unfocusContext(hd) // // case Cons(hd, tl) => Union(unfocusContext(hd), unfocusZipper(tl)) @@ -272,58 +275,46 @@ object ZipperRegex { def focus[C](r: Regex[C]): Zipper[C] = { require(validRegex(r)) - Set(List(r)) - }.ensuring(res => validZipper(res)) + Set(Context(List(r))) + } def derivationStepZipperUp[C](context: Context[C], a: C): Zipper[C] = { - require(validContext(context)) - decreases(context) - context match { - case Cons(right, parent) if nullable(right) => - ghostExpr(SetUtils.lemmaConcatPreservesForall(derivationStepZipperDown(right, parent, a), derivationStepZipperUp(parent, a), validContext)) - derivationStepZipperDown(right, parent, a) ++ derivationStepZipperUp(parent, a) - case Cons(right, parent) => derivationStepZipperDown(right, parent, a) + decreases(context.exprs.size) + context.exprs match { + case Cons(right, parent) if nullable(right) => derivationStepZipperDown(right, Context(parent), a) ++ derivationStepZipperUp(Context(parent), a) + case Cons(right, parent) => derivationStepZipperDown(right, Context(parent), a) case Nil() => Set() } - }.ensuring(res => validZipper(res)) + } def derivationStepZipperDown[C](expr: Regex[C], context: Context[C], a: C): Zipper[C] = { - require(validContext(context)) require(validRegex(expr)) decreases(regexDepth(expr)) expr match { case ElementMatch(c) if c == a => Set(context) - case Union(rOne, rTwo) => - ghostExpr(SetUtils.lemmaConcatPreservesForall(derivationStepZipperDown(rOne, context, a), derivationStepZipperDown(rTwo, context, a), validContext)) - derivationStepZipperDown(rOne, context, a) ++ derivationStepZipperDown(rTwo, context, a) - case Concat(rOne, rTwo) if nullable(rOne) => - ghostExpr(SetUtils.lemmaConcatPreservesForall(derivationStepZipperDown(rOne, rTwo :: context, a), derivationStepZipperDown(rTwo, context, a), validContext)) - derivationStepZipperDown(rOne, rTwo :: context, a) ++ derivationStepZipperDown(rTwo, context, a) - case Concat(rOne, rTwo) => derivationStepZipperDown(rOne, rTwo :: context, a) - case Star(rInner) => derivationStepZipperDown(rInner, Star(rInner) :: context, a) + case Union(rOne, rTwo) => derivationStepZipperDown(rOne, context, a) ++ derivationStepZipperDown(rTwo, context, a) + case Concat(rOne, rTwo) if nullable(rOne) => derivationStepZipperDown(rOne, context.prepend(rTwo), a) ++ derivationStepZipperDown(rTwo, context, a) + case Concat(rOne, rTwo) => derivationStepZipperDown(rOne, context.prepend(rTwo), a) + case Star(rInner) => derivationStepZipperDown(rInner, context.prepend(Star(rInner)), a) case _ => Set() } - }.ensuring(res => validZipper(res)) + } // @inlineOnce def derivationStepZipper[C](z: Zipper[C], a: C): Zipper[C] = { - require(validZipper(z)) - ghostExpr(SetUtils.lemmaFlatMapForall(z, (c: Context[C]) => derivationStepZipperUp(c, a), validContext)) z.flatMap(c => derivationStepZipperUp(c, a)) - }.ensuring(res => validZipper(res)) + } def nullableContext[C](c: Context[C]): Boolean = { - require(validContext(c)) c.forall(r => nullable(r)) } def nullableZipper[C](z: Zipper[C]): Boolean = { - require(validZipper(z)) + z.exists(c => nullableContext(c)) } def matchZipper[C](z: Zipper[C], input: List[C]): Boolean = { - require(validZipper(z)) decreases(input.size) if (input.isEmpty) nullableZipper(z) else matchZipper(derivationStepZipper(z, input.head), input.tail) } @@ -333,31 +324,31 @@ object ZipperRegex { def theoremZipperRegexEquiv[C](r: Regex[C], z: Zipper[C], s: List[C]): Unit = { require(validRegex(r)) - require(validZipper(z)) + }.ensuring(_ => matchR(r, s) == matchZipper(focus(r), s)) // LEMMAS ----------------------------------------------------------------------------------------------------- - @ghost - @inlineOnce - @opaque - def lemmaFlatMapDerivationUpForallValidToList[C](z: Zipper[C], a:C, l: List[Context[C]]): Unit = { - require(validZipper(z)) - require(ListSpecs.subseq(l, z.flatMap(c => derivationStepZipperUp(c, a)).toList)) - decreases(l) - l match - case Cons(hd, tl) => - ListSpecs.subseqContains(l, z.flatMap(c => derivationStepZipperUp(c, a)).toList, hd) - assert(z.flatMap(c => derivationStepZipperUp(c, a)).contains(hd)) - unfold(z.flatMapPost(c => derivationStepZipperUp(c, a))(hd)) - // lemmaFlatMapForallElem(z, c => derivationStepZipperUp(c, a), validContext, hd) - ListSpecs.subseqTail(l, z.flatMap(c => derivationStepZipperUp(c, a)).toList) - lemmaFlatMapDerivationUpForallValidToList(z, a, tl) - case Nil() => () - - }.ensuring (_ => l.forall(validContext)) + // @ghost + // @inlineOnce + // @opaque + // def lemmaFlatMapDerivationUpForallValidToList[C](z: Zipper[C], a:C, l: List[Context[C]]): Unit = { + // + // require(ListSpecs.subseq(l, z.flatMap(c => derivationStepZipperUp(c, a)).toList)) + // decreases(l) + // l match + // case Cons(hd, tl) => + // ListSpecs.subseqContains(l, z.flatMap(c => derivationStepZipperUp(c, a)).toList, hd) + // assert(z.flatMap(c => derivationStepZipperUp(c, a)).contains(hd)) + // unfold(z.flatMapPost(c => derivationStepZipperUp(c, a))(hd)) + // // lemmaFlatMapForallElem(z, c => derivationStepZipperUp(c, a), validContext, hd) + // ListSpecs.subseqTail(l, z.flatMap(c => derivationStepZipperUp(c, a)).toList) + // lemmaFlatMapDerivationUpForallValidToList(z, a, tl) + // case Nil() => () + + // }.ensuring (_ => l.forall(validContext)) } From 5543793935aaca61fe11e8df2d017b47a7d0e00c Mon Sep 17 00:00:00 2001 From: Samuel Chassot Date: Mon, 11 Nov 2024 09:29:04 +0100 Subject: [PATCH 41/78] Prove that the derivation of zippers is associative --- .../src/main/scala/ch/epfl/lexer/Utils.scala | 166 +++++++++++++++++- .../scala/ch/epfl/lexer/VerifiedRegex.scala | 56 ++++-- 2 files changed, 201 insertions(+), 21 deletions(-) diff --git a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/Utils.scala b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/Utils.scala index 86883803..3d8bf15a 100644 --- a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/Utils.scala +++ b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/Utils.scala @@ -12,12 +12,160 @@ import stainless.lang.StaticChecks._ object SetUtils { + @opaque + @ghost + @inlineOnce + def lemmaFlatMapAssociativeElem[A, B](s1: Set[A], s2: Set[A], f: A => Set[B], b: B): Unit = { + unfold(s2.flatMapPost(f)) + if(s1.flatMap(f).contains(b)) { + unfold(s1.flatMapPost(f)(b)) + assert(s1.exists(a => f(a).contains(b))) + val witness = getWitness(s1, a => f(a).contains(b)) + assert(f(witness).contains(b)) + assert((s1 ++ s2).contains(witness)) + assert((s1.flatMap(f) ++ s2.flatMap(f)).contains(b)) + lemmaContainsThenExist(s1 ++ s2, witness, a => f(a).contains(b)) + assert((s1 ++ s2).exists(a => f(a).contains(b))) + + unfold((s1 ++ s2).flatMapPost(f)(b)) + + assert((s1 ++ s2).flatMap(f).contains(b)) + assert((s1.flatMap(f) ++ s2.flatMap(f)).contains(b) == (s1++s2).flatMap(f).contains(b)) + } else if (s2.flatMap(f).contains(b)) { + unfold(s2.flatMapPost(f)(b)) + assert(s2.exists(a => f(a).contains(b))) + val witness = getWitness(s2, a => f(a).contains(b)) + assert(f(witness).contains(b)) + assert((s1 ++ s2).contains(witness)) + assert((s1.flatMap(f) ++ s2.flatMap(f)).contains(b)) + lemmaContainsThenExist(s1 ++ s2, witness, a => f(a).contains(b)) + assert((s1 ++ s2).exists(a => f(a).contains(b))) + + unfold((s1 ++ s2).flatMapPost(f)(b)) + + assert((s1 ++ s2).flatMap(f).contains(b)) + assert((s1.flatMap(f) ++ s2.flatMap(f)).contains(b) == (s1++s2).flatMap(f).contains(b)) + } else{ + assert(!(s1.flatMap(f) ++ s2.flatMap(f)).contains(b)) + assert(!s1.flatMap(f).contains(b)) + assert(!s2.flatMap(f).contains(b)) + unfold(s1.flatMapPost(f)(b)) + unfold(s2.flatMapPost(f)(b)) + assert(!s1.exists(a => f(a).contains(b))) + assert(!s2.exists(a => f(a).contains(b))) + if((s1++s2).flatMap(f).contains(b)){ + unfold((s1 ++ s2).flatMapPost(f)(b)) + assert((s1 ++ s2).exists(a => f(a).contains(b))) + val witness = getWitness(s1 ++ s2, a => f(a).contains(b)) + assert(s1.contains(witness) || s2.contains(witness)) + if(s1.contains(witness)) { + lemmaContainsThenExist(s1, witness, a => f(a).contains(b)) + assert(s1.exists(a => f(a).contains(b))) + check(false) + } else { + lemmaContainsThenExist(s2, witness, a => f(a).contains(b)) + check(false) + } + check(false) + } + check(!(s1++s2).flatMap(f).contains(b)) + } + }.ensuring(_ => (s1.flatMap(f) ++ s2.flatMap(f)).contains(b) == (s1++s2).flatMap(f).contains(b)) + + @opaque + @ghost + @inlineOnce + def lemmaFlatMapAssociative[A, B](s1: Set[A], s2: Set[A], f: A => Set[B]): Unit = { + + val l1 = (s1.flatMap(f) ++ s2.flatMap(f)).toList + val l2 = (s1++s2).flatMap(f).toList + + ListUtils.lemmaSubseqRefl(l1) + ListUtils.lemmaSubseqRefl(l2) + + lemmaFlatMapAssociativeToList2(s1, s2, f, l1, l2) + lemmaFlatMapAssociativeToList1(s1, s2, f, l1, l2) + check(l1.forall(l2.contains)) + check(l2.forall(l1.contains)) + ListSpecs.forallContainsSubset(l1, l2) + ListSpecs.forallContainsSubset(l2, l1) + + }.ensuring(_ => s1.flatMap(f) ++ s2.flatMap(f) == (s1++s2).flatMap(f)) + + + @opaque + @ghost + @inlineOnce + def lemmaFlatMapAssociativeToList1[A, B](s1: Set[A], s2: Set[A], f: A => Set[B], l1: List[B], l2: List[B]): Unit = { + require(ListSpecs.subseq(l1, (s1.flatMap(f) ++ s2.flatMap(f)).toList)) + require(l2 == (s1++s2).flatMap(f).toList) + decreases(l1.size) + l1 match { + case Cons(hd, tl) => { + lemmaFlatMapAssociativeElem(s1, s2, f, hd) + ListSpecs.subseqTail(l1, (s1.flatMap(f) ++ s2.flatMap(f)).toList) + lemmaFlatMapAssociativeToList1(s1, s2, f, tl, l2) + ListSpecs.subseqContains(l1, (s1.flatMap(f) ++ s2.flatMap(f)).toList, hd) + } + case Nil() => () + } + + }.ensuring(_ => l1.forall(l2.contains)) + + @opaque + @ghost + @inlineOnce + def lemmaFlatMapAssociativeToList2[A, B](s1: Set[A], s2: Set[A], f: A => Set[B], l1: List[B], l2: List[B]): Unit = { + require(l1 == (s1.flatMap(f) ++ s2.flatMap(f)).toList) + require(ListSpecs.subseq(l2, (s1++s2).flatMap(f).toList)) + decreases(l2.size) + l2 match { + case Cons(hd, tl) => { + lemmaFlatMapAssociativeElem(s1, s2, f, hd) + ListSpecs.subseqTail(l2, (s1++s2).flatMap(f).toList) + lemmaFlatMapAssociativeToList2(s1, s2, f, l1, tl) + ListSpecs.subseqContains(l2, (s1++s2).flatMap(f).toList, hd) + } + case Nil() => () + } + + }.ensuring(_ => l2.forall(l1.contains)) + + @ghost def getWitness[A](s: Set[A], p: A => Boolean): A = { require(s.exists(p)) ListUtils.getWitness(s.toList, p) - }.ensuring(res => p(res) && s.contains(res)) + }.ensuring(res => p(res) && s.contains(res) && s.exists(p)) + @opaque + @ghost + @inlineOnce + def lemmaContainsThenExist[A](s: Set[A], e: A, p: A => Boolean): Unit = { + require(s.contains(e)) + require(p(e)) + ListUtils.lemmaContainsThenExist(s.toList, e, p) + assert(s.exists(p)) + }.ensuring(_ => s.exists(p)) + + @ghost + @inlineOnce + @opaque + def lemmaConcatNotExistThenBothNotExist[A](s1: Set[A], s2: Set[A], p: A => Boolean): Unit = { + require(!(s1 ++ s2).exists(p)) + if(s1.exists(p)){ + val witness = getWitness(s1, p) + assert(s1.contains(witness)) + assert((s1 ++ s2).contains(witness)) + lemmaContainsThenExist(s1 ++ s2, witness, p) + } + if (s2.exists(p)){ + val witness = getWitness(s2, p) + assert(s2.contains(witness)) + assert((s1 ++ s2).contains(witness)) + lemmaContainsThenExist(s1 ++ s2, witness, p) + } + }.ensuring(_ => !s1.exists(p) && !s2.exists(p)) @ghost @opaque @@ -156,7 +304,21 @@ object ListUtils { case Cons(hd, tl) => getWitness(tl, p) case Nil() => check(false); l.head } - }.ensuring(res => p(res) && l.contains(res)) + }.ensuring(res => p(res) && l.contains(res) && l.exists(p)) + + @ghost + @opaque + @inlineOnce + def lemmaContainsThenExist[B](l: List[B], e: B, p: B => Boolean): Unit = { + require(l.contains(e)) + require(p(e)) + decreases(l) + l match { + case Cons(hd, tl) if hd == e => () + case Cons(hd, tl) => lemmaContainsThenExist(tl, e, p) + case Nil() => check(false) + } + }.ensuring(_ => l.exists(p)) @inlineOnce @opaque diff --git a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedRegex.scala b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedRegex.scala index 1ce3ee37..e6e30511 100644 --- a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedRegex.scala +++ b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedRegex.scala @@ -310,7 +310,6 @@ object ZipperRegex { c.forall(r => nullable(r)) } def nullableZipper[C](z: Zipper[C]): Boolean = { - z.exists(c => nullableContext(c)) } @@ -328,28 +327,47 @@ object ZipperRegex { }.ensuring(_ => matchR(r, s) == matchZipper(focus(r), s)) + def lemmaZipperConcatMatchesSameAsBothZippers[C](z1:Zipper[C], z2: Zipper[C], s: List[C]): Unit = { + + }.ensuring(_ => matchZipper(z1 ++ z2, s) == matchZipper(z1, s) || matchZipper(z2, s)) + // LEMMAS ----------------------------------------------------------------------------------------------------- - // @ghost - // @inlineOnce - // @opaque - // def lemmaFlatMapDerivationUpForallValidToList[C](z: Zipper[C], a:C, l: List[Context[C]]): Unit = { - // - // require(ListSpecs.subseq(l, z.flatMap(c => derivationStepZipperUp(c, a)).toList)) - // decreases(l) - // l match - // case Cons(hd, tl) => - // ListSpecs.subseqContains(l, z.flatMap(c => derivationStepZipperUp(c, a)).toList, hd) - // assert(z.flatMap(c => derivationStepZipperUp(c, a)).contains(hd)) - // unfold(z.flatMapPost(c => derivationStepZipperUp(c, a))(hd)) - // // lemmaFlatMapForallElem(z, c => derivationStepZipperUp(c, a), validContext, hd) - // ListSpecs.subseqTail(l, z.flatMap(c => derivationStepZipperUp(c, a)).toList) - // lemmaFlatMapDerivationUpForallValidToList(z, a, tl) - // case Nil() => () - - // }.ensuring (_ => l.forall(validContext)) + @ghost + @opaque + @inlineOnce + def lemmaDerivativeStepZipperAssociative[C](z: Zipper[C], z1: Zipper[C], z2: Zipper[C], a: C): Unit = { + require(z == z1 ++ z2) + SetUtils.lemmaFlatMapAssociative(z1, z2, (c: Context[C]) => derivationStepZipperUp(c, a)) + }.ensuring(_ => derivationStepZipper(z, a) == derivationStepZipper(z1, a) ++ derivationStepZipper(z2, a)) + + @ghost + @opaque + @inlineOnce + def lemmaZipperOfEmptyLangMatchesNothing[C](z: Zipper[C], s: List[C]): Unit = { + require(z == focus(EmptyLang[C]())) + if(s.isEmpty){ + check(!nullableContext(Context(List(EmptyLang[C]())))) + }else{ + () + } + }.ensuring(_ => !matchZipper(z, s)) + + + @ghost + @opaque + @inlineOnce + def lemmaZipperOfEmptyExprMatchesOnlyEmptyString[C](z: Zipper[C], s: List[C]): Unit = { + require(z == focus(EmptyExpr[C]())) + check(nullableContext(Context(List(EmptyExpr[C]())))) + if (s.isEmpty) { + check(nullableContext(Context(List(EmptyExpr[C]())))) + } else { + check(matchZipper(z, s) == false) + } + }.ensuring(_ => matchZipper(z, s) == s.isEmpty) } From b2c0f9d71b86395816b62005669af1debd2a7093 Mon Sep 17 00:00:00 2001 From: Samuel Chassot Date: Mon, 11 Nov 2024 14:57:46 +0100 Subject: [PATCH 42/78] working on zipper proof --- .../src/main/scala/ch/epfl/lexer/Utils.scala | 36 +++-- .../scala/ch/epfl/lexer/VerifiedRegex.scala | 136 +++++++++++++++++- 2 files changed, 156 insertions(+), 16 deletions(-) diff --git a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/Utils.scala b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/Utils.scala index 3d8bf15a..26e67c27 100644 --- a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/Utils.scala +++ b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/Utils.scala @@ -12,6 +12,22 @@ import stainless.lang.StaticChecks._ object SetUtils { + @opaque + @ghost + @inlineOnce + def lemmaFlatMapOnEmptySetIsEmpty[A, B](s: Set[A], f: A => Set[B]): Unit = { + require(s.isEmpty) + val ftmap = s.flatMap(f) + if(!ftmap.isEmpty) { + val hd = ftmap.toList.head + assert(ftmap.contains(hd)) + unfold(s.flatMapPost(f)(hd)) + assert(s.exists(a => f(a).contains(hd))) + val witness = getWitness(s, a => f(a).contains(hd)) + check(false) + } + }.ensuring(_ => s.flatMap(f).isEmpty) + @opaque @ghost @inlineOnce @@ -24,7 +40,7 @@ object SetUtils { assert(f(witness).contains(b)) assert((s1 ++ s2).contains(witness)) assert((s1.flatMap(f) ++ s2.flatMap(f)).contains(b)) - lemmaContainsThenExist(s1 ++ s2, witness, a => f(a).contains(b)) + lemmaContainsThenExists(s1 ++ s2, witness, a => f(a).contains(b)) assert((s1 ++ s2).exists(a => f(a).contains(b))) unfold((s1 ++ s2).flatMapPost(f)(b)) @@ -38,7 +54,7 @@ object SetUtils { assert(f(witness).contains(b)) assert((s1 ++ s2).contains(witness)) assert((s1.flatMap(f) ++ s2.flatMap(f)).contains(b)) - lemmaContainsThenExist(s1 ++ s2, witness, a => f(a).contains(b)) + lemmaContainsThenExists(s1 ++ s2, witness, a => f(a).contains(b)) assert((s1 ++ s2).exists(a => f(a).contains(b))) unfold((s1 ++ s2).flatMapPost(f)(b)) @@ -59,11 +75,11 @@ object SetUtils { val witness = getWitness(s1 ++ s2, a => f(a).contains(b)) assert(s1.contains(witness) || s2.contains(witness)) if(s1.contains(witness)) { - lemmaContainsThenExist(s1, witness, a => f(a).contains(b)) + lemmaContainsThenExists(s1, witness, a => f(a).contains(b)) assert(s1.exists(a => f(a).contains(b))) check(false) } else { - lemmaContainsThenExist(s2, witness, a => f(a).contains(b)) + lemmaContainsThenExists(s2, witness, a => f(a).contains(b)) check(false) } check(false) @@ -141,10 +157,10 @@ object SetUtils { @opaque @ghost @inlineOnce - def lemmaContainsThenExist[A](s: Set[A], e: A, p: A => Boolean): Unit = { + def lemmaContainsThenExists[A](s: Set[A], e: A, p: A => Boolean): Unit = { require(s.contains(e)) require(p(e)) - ListUtils.lemmaContainsThenExist(s.toList, e, p) + ListUtils.lemmaContainsThenExists(s.toList, e, p) assert(s.exists(p)) }.ensuring(_ => s.exists(p)) @@ -157,13 +173,13 @@ object SetUtils { val witness = getWitness(s1, p) assert(s1.contains(witness)) assert((s1 ++ s2).contains(witness)) - lemmaContainsThenExist(s1 ++ s2, witness, p) + lemmaContainsThenExists(s1 ++ s2, witness, p) } if (s2.exists(p)){ val witness = getWitness(s2, p) assert(s2.contains(witness)) assert((s1 ++ s2).contains(witness)) - lemmaContainsThenExist(s1 ++ s2, witness, p) + lemmaContainsThenExists(s1 ++ s2, witness, p) } }.ensuring(_ => !s1.exists(p) && !s2.exists(p)) @@ -309,13 +325,13 @@ object ListUtils { @ghost @opaque @inlineOnce - def lemmaContainsThenExist[B](l: List[B], e: B, p: B => Boolean): Unit = { + def lemmaContainsThenExists[B](l: List[B], e: B, p: B => Boolean): Unit = { require(l.contains(e)) require(p(e)) decreases(l) l match { case Cons(hd, tl) if hd == e => () - case Cons(hd, tl) => lemmaContainsThenExist(tl, e, p) + case Cons(hd, tl) => lemmaContainsThenExists(tl, e, p) case Nil() => check(false) } }.ensuring(_ => l.exists(p)) diff --git a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedRegex.scala b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedRegex.scala index e6e30511..3b1296c3 100644 --- a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedRegex.scala +++ b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedRegex.scala @@ -237,7 +237,6 @@ object VerifiedRegex { object ZipperRegex { import VerifiedRegex.* import VerifiedRegexMatcher.* - import ListUtils.* import stainless.lang.Set /** @@ -252,10 +251,13 @@ object ZipperRegex { } inline def forall(p: Regex[C] => Boolean): Boolean = exprs.forall(p) inline def isEmpty: Boolean = exprs.isEmpty + inline def head: Regex[C] = exprs.head + inline def tail: Context[C] = Context(exprs.tail) } type Zipper[C] = Set[Context[C]] def unfocusContext[C](c: Context[C]): Regex[C] = { + decreases(c.exprs.size) c.exprs match { case Cons(hd, tl) if tl.isEmpty => hd case Cons(hd, tl) => Concat(hd, unfocusContext(Context(tl))) @@ -301,6 +303,14 @@ object ZipperRegex { } } + def derivationStepZipperAllInOne[C](context: Context[C], a: C): Zipper[C] = { + context.exprs match { + case Cons(right, parent) if nullable(right) => derivationStepZipperDown(right, Context(parent), a) ++ derivationStepZipperUp(Context(parent), a) + case Cons(right, parent) => derivationStepZipperDown(right, Context(parent), a) + case Nil() => Set() + } + } + // @inlineOnce def derivationStepZipper[C](z: Zipper[C], a: C): Zipper[C] = { z.flatMap(c => derivationStepZipperUp(c, a)) @@ -321,20 +331,104 @@ object ZipperRegex { // PROOFS ----------------------------------------------------------------------------------------------------- + @ghost + @opaque + @inlineOnce def theoremZipperRegexEquiv[C](r: Regex[C], z: Zipper[C], s: List[C]): Unit = { require(validRegex(r)) + require(focus(r) == z) + r match { + case EmptyExpr() => () + case EmptyLang() => lemmaZipperStartingWithEmptyLangMatchesNothing(z, Context(List(r)), s) + case ElementMatch(c) => () + case Union(r1, r2) => () + case Star(rInner) => () + case Concat(r1, r2) => () + } }.ensuring(_ => matchR(r, s) == matchZipper(focus(r), s)) + @ghost + @opaque + @inlineOnce def lemmaZipperConcatMatchesSameAsBothZippers[C](z1:Zipper[C], z2: Zipper[C], s: List[C]): Unit = { + decreases(s.size) + s match{ + case Cons(hd, tl) => { + val z1Deriv = derivationStepZipper(z1, hd) + val z2Deriv = derivationStepZipper(z2, hd) + lemmaDerivativeStepZipperAssociative(z1 ++ z2, z1, z2, hd) + lemmaZipperConcatMatchesSameAsBothZippers(z1Deriv, z2Deriv, tl) + } + case Nil() => { + if(nullableZipper(z1 ++ z2)){ + assert((z1 ++ z2).exists(c => nullableContext(c))) + val witness = SetUtils.getWitness(z1 ++ z2, (c: Context[C]) => nullableContext(c)) + assert((z1 ++ z2).contains(witness)) + if(z1.contains(witness)){ + SetUtils.lemmaContainsThenExists(z1, witness, (c: Context[C]) => nullableContext(c)) + assert(nullableZipper(z1)) + } else { + SetUtils.lemmaContainsThenExists(z2, witness, (c: Context[C]) => nullableContext(c)) + assert(nullableZipper(z2)) + } + check(nullableZipper(z1) || nullableZipper(z2)) + } else{ + assert(!(z1 ++ z2).exists(c => nullableContext(c))) + if(nullableZipper(z1)) { + val witness = SetUtils.getWitness(z1, (c: Context[C]) => nullableContext(c)) + SetUtils.lemmaContainsThenExists(z1 ++ z2, witness, (c: Context[C]) => nullableContext(c)) + assert(nullableZipper(z1 ++ z2)) + check(false) + } + if(nullableZipper(z2)) { + val witness = SetUtils.getWitness(z2, (c: Context[C]) => nullableContext(c)) + SetUtils.lemmaContainsThenExists(z1 ++ z2, witness, (c: Context[C]) => nullableContext(c)) + assert(nullableZipper(z1 ++ z2)) + check(false) + } + check(!nullableZipper(z1) && !nullableZipper(z2)) + } + } + } }.ensuring(_ => matchZipper(z1 ++ z2, s) == matchZipper(z1, s) || matchZipper(z2, s)) // LEMMAS ----------------------------------------------------------------------------------------------------- - @ghost + @ghost + @opaque + @inlineOnce + def lemmaDerivationStepDownCorrect[C](r: Regex[C], c: Context[C], a: C, s: List[C]): Unit = { + require(validRegex(r)) + decreases(s.size) + val unfocusRegex = Concat(r, unfocusContext(c)) + VerifiedRegexMatcher.mainMatchTheorem(unfocusRegex, a :: s) + + r match { + // case ElementMatch(c) if c == a => Set(context) + // case Union(rOne, rTwo) => derivationStepZipperDown(rOne, context, a) ++ derivationStepZipperDown(rTwo, context, a) + // case Concat(rOne, rTwo) if nullable(rOne) => derivationStepZipperDown(rOne, context.prepend(rTwo), a) ++ derivationStepZipperDown(rTwo, context, a) + // case Concat(rOne, rTwo) => derivationStepZipperDown(rOne, context.prepend(rTwo), a) + // case Star(rInner) => derivationStepZipperDown(rInner, context.prepend(Star(rInner)), a) + case ElementMatch(cc) if cc == a => + assert(derivationStepZipperDown(r, c, a) == Set(c)) + if(!s.isEmpty && !c.isEmpty){ + lemmaDerivationStepDownCorrect(c.head, c.tail, s.head, s.tail) + } + case Union(rOne, rTwo) => () + case Concat(rOne, rTwo) if nullable(rOne) => () + case Concat(rOne, rTwo) => () + case Star(rInner) => () + case _ => () + } + + }.ensuring(_ => matchR(Concat(r, unfocusContext(c)), a :: s) == matchZipper(derivationStepZipperDown(r, c, a), s)) + + + @ghost @opaque @inlineOnce def lemmaDerivativeStepZipperAssociative[C](z: Zipper[C], z1: Zipper[C], z2: Zipper[C], a: C): Unit = { @@ -346,12 +440,42 @@ object ZipperRegex { @ghost @opaque @inlineOnce - def lemmaZipperOfEmptyLangMatchesNothing[C](z: Zipper[C], s: List[C]): Unit = { - require(z == focus(EmptyLang[C]())) + def lemmaEmptyZipperMatchesNothing[C](z: Zipper[C], s: List[C]): Unit = { + require(z.isEmpty) + decreases(s.size) + s match { + case Cons(hd, tl) => + SetUtils.lemmaFlatMapOnEmptySetIsEmpty(z, (c: Context[C]) => derivationStepZipperUp(c, hd)) + lemmaEmptyZipperMatchesNothing(z, tl) + case Nil() => () + } + }.ensuring(_ => !matchZipper(z, s)) + + @ghost + @opaque + @inlineOnce + def lemmaZipperStartingWithEmptyLangMatchesNothing[C](z: Zipper[C], c: Context[C], s: List[C]): Unit = { + require(z == Set(c)) + require(!c.isEmpty) + require(c.head == EmptyLang[C]()) if(s.isEmpty){ - check(!nullableContext(Context(List(EmptyLang[C]())))) + check(!nullableContext(c)) }else{ - () + val deriv: Zipper[C]= derivationStepZipper(z, s.head) + val derivUp = derivationStepZipperUp(c, s.head) + assert(derivUp == Set()) + if(!deriv.isEmpty){ + val hd = deriv.toList.head + val f: Context[C] => Zipper[C] = ct => derivationStepZipperUp(ct, s.head) + assert(z.flatMap(f).contains(hd)) + assert(deriv.contains(hd)) + unfold(z.flatMapPost(f)(hd)) + assert(z.exists(context => derivationStepZipperUp(context, s.head).contains(hd))) + assert(z == Set(c)) + check(false) + } + assert(deriv.isEmpty) + lemmaEmptyZipperMatchesNothing(deriv, s.tail) } }.ensuring(_ => !matchZipper(z, s)) From 9ec15fb8c05f00e6b01d7f35b85f5bdcc7785232 Mon Sep 17 00:00:00 2001 From: Samuel Chassot Date: Tue, 12 Nov 2024 09:34:49 +0100 Subject: [PATCH 43/78] proving stuff --- .../scala/ch/epfl/lexer/VerifiedRegex.scala | 71 ++++++++++++++++++- 1 file changed, 70 insertions(+), 1 deletion(-) diff --git a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedRegex.scala b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedRegex.scala index 3b1296c3..85ab04e3 100644 --- a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedRegex.scala +++ b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedRegex.scala @@ -338,7 +338,7 @@ object ZipperRegex { require(validRegex(r)) require(focus(r) == z) r match { - case EmptyExpr() => () + case EmptyExpr() => lemmaEmptyExprZipperMatchesOnlyEmptyString(z, Context(List(r)), s) case EmptyLang() => lemmaZipperStartingWithEmptyLangMatchesNothing(z, Context(List(r)), s) case ElementMatch(c) => () case Union(r1, r2) => () @@ -437,6 +437,75 @@ object ZipperRegex { }.ensuring(_ => derivationStepZipper(z, a) == derivationStepZipper(z1, a) ++ derivationStepZipper(z2, a)) + @ghost + @opaque + @inlineOnce + def lemmaElementMatchZipperAcceptsOnlyThisChar[C](z: Zipper[C], c: Context[C], a: C, s: List[C]): Unit = { + require(z == Set(c)) + require(!c.isEmpty) + require(c.head == ElementMatch[C](a)) + require(c.tail.isEmpty) + s match { + case Cons(hd, tl) if hd == a => () + case Cons(hd, tl) if hd != a => { + val deriv: Zipper[C]= derivationStepZipper(z, s.head) + val derivUp = derivationStepZipperUp(c, s.head) + assert(derivUp == Set()) + if(!deriv.isEmpty){ + val hd = deriv.toList.head + val f: Context[C] => Zipper[C] = ct => derivationStepZipperUp(ct, s.head) + assert(z.flatMap(f).contains(hd)) + assert(deriv.contains(hd)) + unfold(z.flatMapPost(f)(hd)) + assert(z.exists(context => derivationStepZipperUp(context, s.head).contains(hd))) + assert(z == Set(c)) + check(false) + } + assert(deriv.isEmpty) + lemmaEmptyZipperMatchesNothing(deriv, s.tail) + } + case Nil() => () + } + }.ensuring(_ => !matchZipper(z, s)) + + + @ghost + @opaque + @inlineOnce + def lemmaEmptyExprZipperMatchesOnlyEmptyString[C](z: Zipper[C], c: Context[C], s: List[C]): Unit = { + require(z == Set(Context(List(EmptyExpr[C]())))) + require(!c.isEmpty) + require(c.head == EmptyExpr[C]()) + require(c.tail.isEmpty) + + s match { + case Cons(hd, tl) => { + val deriv: Zipper[C]= derivationStepZipper(z, hd) + val derivUp = derivationStepZipperUp(c, hd) + assert(derivUp == derivationStepZipperDown(c.head, Context(List()), hd) ++ derivationStepZipperUp(Context(List()), hd)) + if(!deriv.isEmpty){ + val hd = deriv.toList.head + val f: Context[C] => Zipper[C] = ct => derivationStepZipperUp(ct, s.head) + assert(z.flatMap(f).contains(hd)) + assert(deriv.contains(hd)) + unfold(z.flatMapPost(f)(hd)) + assert(z.exists(context => derivationStepZipperUp(context, s.head).contains(hd))) + assert(z == Set(c)) + check(false) + } + assert(deriv.isEmpty) + lemmaEmptyZipperMatchesNothing(deriv, s.tail) + check(!s.isEmpty) + check(matchZipper(z, s) == false) + } + case Nil() => + check(nullableContext(c)) + check(matchZipper(z, s)) + } + + }.ensuring(_ => matchZipper(z, s) == s.isEmpty) + + @ghost @opaque @inlineOnce From 38ad285cb513b2db62b9d6945b99cdb031073486 Mon Sep 17 00:00:00 2001 From: Samuel Chassot Date: Tue, 12 Nov 2024 15:41:37 +0100 Subject: [PATCH 44/78] proving equivalence of base cases --- .../scala/ch/epfl/lexer/VerifiedRegex.scala | 195 ++++++++++++------ 1 file changed, 136 insertions(+), 59 deletions(-) diff --git a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedRegex.scala b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedRegex.scala index 85ab04e3..2ed25a33 100644 --- a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedRegex.scala +++ b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedRegex.scala @@ -251,8 +251,14 @@ object ZipperRegex { } inline def forall(p: Regex[C] => Boolean): Boolean = exprs.forall(p) inline def isEmpty: Boolean = exprs.isEmpty - inline def head: Regex[C] = exprs.head - inline def tail: Context[C] = Context(exprs.tail) + inline def head: Regex[C] = { + require(!isEmpty) + exprs.head + } + inline def tail: Context[C] = { + require(!isEmpty) + Context(exprs.tail) + } } type Zipper[C] = Set[Context[C]] @@ -338,9 +344,9 @@ object ZipperRegex { require(validRegex(r)) require(focus(r) == z) r match { - case EmptyExpr() => lemmaEmptyExprZipperMatchesOnlyEmptyString(z, Context(List(r)), s) + case EmptyExpr() => lemmaZipperOfEmptyExprMatchesOnlyEmptyString(z, s) case EmptyLang() => lemmaZipperStartingWithEmptyLangMatchesNothing(z, Context(List(r)), s) - case ElementMatch(c) => () + case ElementMatch(c) => lemmaElementMatchZipperAcceptsOnlyThisChar(z, Context(List(ElementMatch(c))), c, s) case Union(r1, r2) => () case Star(rInner) => () case Concat(r1, r2) => () @@ -398,34 +404,34 @@ object ZipperRegex { // LEMMAS ----------------------------------------------------------------------------------------------------- - @ghost - @opaque - @inlineOnce - def lemmaDerivationStepDownCorrect[C](r: Regex[C], c: Context[C], a: C, s: List[C]): Unit = { - require(validRegex(r)) - decreases(s.size) - val unfocusRegex = Concat(r, unfocusContext(c)) - VerifiedRegexMatcher.mainMatchTheorem(unfocusRegex, a :: s) - - r match { - // case ElementMatch(c) if c == a => Set(context) - // case Union(rOne, rTwo) => derivationStepZipperDown(rOne, context, a) ++ derivationStepZipperDown(rTwo, context, a) - // case Concat(rOne, rTwo) if nullable(rOne) => derivationStepZipperDown(rOne, context.prepend(rTwo), a) ++ derivationStepZipperDown(rTwo, context, a) - // case Concat(rOne, rTwo) => derivationStepZipperDown(rOne, context.prepend(rTwo), a) - // case Star(rInner) => derivationStepZipperDown(rInner, context.prepend(Star(rInner)), a) - case ElementMatch(cc) if cc == a => - assert(derivationStepZipperDown(r, c, a) == Set(c)) - if(!s.isEmpty && !c.isEmpty){ - lemmaDerivationStepDownCorrect(c.head, c.tail, s.head, s.tail) - } - case Union(rOne, rTwo) => () - case Concat(rOne, rTwo) if nullable(rOne) => () - case Concat(rOne, rTwo) => () - case Star(rInner) => () - case _ => () - } + // @ghost + // @opaque + // @inlineOnce + // def lemmaDerivationStepDownCorrect[C](r: Regex[C], c: Context[C], a: C, s: List[C]): Unit = { + // require(validRegex(r)) + // decreases(s.size) + // val unfocusRegex = Concat(r, unfocusContext(c)) + // VerifiedRegexMatcher.mainMatchTheorem(unfocusRegex, a :: s) + + // r match { + // // case ElementMatch(c) if c == a => Set(context) + // // case Union(rOne, rTwo) => derivationStepZipperDown(rOne, context, a) ++ derivationStepZipperDown(rTwo, context, a) + // // case Concat(rOne, rTwo) if nullable(rOne) => derivationStepZipperDown(rOne, context.prepend(rTwo), a) ++ derivationStepZipperDown(rTwo, context, a) + // // case Concat(rOne, rTwo) => derivationStepZipperDown(rOne, context.prepend(rTwo), a) + // // case Star(rInner) => derivationStepZipperDown(rInner, context.prepend(Star(rInner)), a) + // case ElementMatch(cc) if cc == a => + // assert(derivationStepZipperDown(r, c, a) == Set(c)) + // if(!s.isEmpty && !c.isEmpty){ + // lemmaDerivationStepDownCorrect(c.head, c.tail, s.head, s.tail) + // } + // case Union(rOne, rTwo) => () + // case Concat(rOne, rTwo) if nullable(rOne) => () + // case Concat(rOne, rTwo) => () + // case Star(rInner) => () + // case _ => () + // } - }.ensuring(_ => matchR(Concat(r, unfocusContext(c)), a :: s) == matchZipper(derivationStepZipperDown(r, c, a), s)) + // }.ensuring(_ => matchR(Concat(r, unfocusContext(c)), a :: s) == matchZipper(derivationStepZipperDown(r, c, a), s)) @ghost @@ -446,14 +452,39 @@ object ZipperRegex { require(c.head == ElementMatch[C](a)) require(c.tail.isEmpty) s match { - case Cons(hd, tl) if hd == a => () - case Cons(hd, tl) if hd != a => { - val deriv: Zipper[C]= derivationStepZipper(z, s.head) + case Cons(hd, tl) if hd == a => { + val deriv: Zipper[C] = derivationStepZipper(z, s.head) val derivUp = derivationStepZipperUp(c, s.head) + assert(derivUp == Set(Context(List()))) + unfold(z.flatMapPost(c => derivationStepZipperUp(c, a))(Context[C](List()))) + assert(deriv.contains(Context[C](List()))) + if(deriv != Set(Context[C](List()))) { + assert(deriv.exists(c => c != Context[C](List()))) + val witness = SetUtils.getWitness(deriv, (c: Context[C]) => c != Context[C](List())) + unfold(z.flatMapPost(c => derivationStepZipperUp(c, a))(witness)) + assert(deriv.contains(witness)) + assert(z.exists(c => derivationStepZipperUp(c, s.head).contains(witness))) + val witnessContext = SetUtils.getWitness(z, (c: Context[C]) => derivationStepZipperUp(c, s.head).contains(witness)) + assert(z.contains(witnessContext)) + check(false) + } + check(deriv == Set(Context[C](List()))) + if(tl.isEmpty) { + lemmaZipperOfEmptyContextMatchesEmptyString(Set(Context(List())), tl) + check(matchZipper(z, s)) + + } else { + lemmaZipperOfEmptyContextMatchesEmptyString(deriv, tl) + check(!matchZipper(z, s)) + } + } + case Cons(shd, tl) if shd != a => { + val deriv: Zipper[C] = derivationStepZipper(z, shd) + val derivUp = derivationStepZipperUp(c, shd) assert(derivUp == Set()) if(!deriv.isEmpty){ val hd = deriv.toList.head - val f: Context[C] => Zipper[C] = ct => derivationStepZipperUp(ct, s.head) + val f: Context[C] => Zipper[C] = ct => derivationStepZipperUp(ct, shd) assert(z.flatMap(f).contains(hd)) assert(deriv.contains(hd)) unfold(z.flatMapPost(f)(hd)) @@ -462,47 +493,78 @@ object ZipperRegex { check(false) } assert(deriv.isEmpty) - lemmaEmptyZipperMatchesNothing(deriv, s.tail) + lemmaEmptyZipperMatchesNothing(deriv, tl) } - case Nil() => () + case Nil() => + check(!nullableContext(c)) } - }.ensuring(_ => !matchZipper(z, s)) + }.ensuring(_ => matchZipper(z, s) == (s == List(a))) + + + // @ghost + // @opaque + // @inlineOnce + // def lemmaEmptyExprZipperMatchesOnlyEmptyString[C](z: Zipper[C], c: Context[C], s: List[C]): Unit = { + // require(z == Set(Context(List(EmptyExpr[C]())))) + // require(!c.isEmpty) + // require(c.head == EmptyExpr[C]()) + // require(c.tail.isEmpty) + + // s match { + // case Cons(hd, tl) => { + // val deriv: Zipper[C]= derivationStepZipper(z, hd) + // val derivUp = derivationStepZipperUp(c, hd) + // assert(derivUp == derivationStepZipperDown(c.head, Context(List()), hd) ++ derivationStepZipperUp(Context(List()), hd)) + // if(!deriv.isEmpty){ + // val hd = deriv.toList.head + // val f: Context[C] => Zipper[C] = ct => derivationStepZipperUp(ct, s.head) + // assert(z.flatMap(f).contains(hd)) + // assert(deriv.contains(hd)) + // unfold(z.flatMapPost(f)(hd)) + // assert(z.exists(context => derivationStepZipperUp(context, s.head).contains(hd))) + // assert(z == Set(c)) + // check(false) + // } + // assert(deriv.isEmpty) + // lemmaEmptyZipperMatchesNothing(deriv, s.tail) + // check(!s.isEmpty) + // check(matchZipper(z, s) == false) + // } + // case Nil() => + // check(nullableContext(c)) + // check(matchZipper(z, s)) + // } + // }.ensuring(_ => matchZipper(z, s) == s.isEmpty) @ghost @opaque @inlineOnce - def lemmaEmptyExprZipperMatchesOnlyEmptyString[C](z: Zipper[C], c: Context[C], s: List[C]): Unit = { - require(z == Set(Context(List(EmptyExpr[C]())))) - require(!c.isEmpty) - require(c.head == EmptyExpr[C]()) - require(c.tail.isEmpty) - + def lemmaZipperOfEmptyContextMatchesEmptyString[C](z: Zipper[C], s: List[C]): Unit = { + require(z == Set(Context(List()))) + decreases(s.size) s match { - case Cons(hd, tl) => { - val deriv: Zipper[C]= derivationStepZipper(z, hd) - val derivUp = derivationStepZipperUp(c, hd) - assert(derivUp == derivationStepZipperDown(c.head, Context(List()), hd) ++ derivationStepZipperUp(Context(List()), hd)) + case Cons(shd, tl) => + val deriv: Zipper[C] = derivationStepZipper(z, shd) + val derivUp = derivationStepZipperUp(Context(List()), shd) + assert(derivUp == Set()) + if(!deriv.isEmpty){ val hd = deriv.toList.head - val f: Context[C] => Zipper[C] = ct => derivationStepZipperUp(ct, s.head) + val f: Context[C] => Zipper[C] = ct => derivationStepZipperUp(ct, shd) assert(z.flatMap(f).contains(hd)) assert(deriv.contains(hd)) unfold(z.flatMapPost(f)(hd)) assert(z.exists(context => derivationStepZipperUp(context, s.head).contains(hd))) - assert(z == Set(c)) + assert(z == Set(Context(List()))) check(false) } - assert(deriv.isEmpty) - lemmaEmptyZipperMatchesNothing(deriv, s.tail) - check(!s.isEmpty) - check(matchZipper(z, s) == false) - } + check((deriv.isEmpty)) + + lemmaEmptyZipperMatchesNothing(derivUp, tl) case Nil() => - check(nullableContext(c)) - check(matchZipper(z, s)) + check(nullableContext(Context[C](List()))) } - }.ensuring(_ => matchZipper(z, s) == s.isEmpty) @@ -555,9 +617,24 @@ object ZipperRegex { def lemmaZipperOfEmptyExprMatchesOnlyEmptyString[C](z: Zipper[C], s: List[C]): Unit = { require(z == focus(EmptyExpr[C]())) check(nullableContext(Context(List(EmptyExpr[C]())))) + val c = Context(List(EmptyExpr[C]())) if (s.isEmpty) { - check(nullableContext(Context(List(EmptyExpr[C]())))) + check(nullableContext(c)) } else { + val deriv: Zipper[C]= derivationStepZipper(z, s.head) + val derivUp = derivationStepZipperUp(c, s.head) + assert(derivUp == Set()) + if(!deriv.isEmpty){ + val hd = deriv.toList.head + val f: Context[C] => Zipper[C] = ct => derivationStepZipperUp(ct, s.head) + assert(z.flatMap(f).contains(hd)) + assert(deriv.contains(hd)) + unfold(z.flatMapPost(f)(hd)) + assert(z.exists(context => derivationStepZipperUp(context, s.head).contains(hd))) + assert(z == Set(c)) + check(false) + } + lemmaEmptyZipperMatchesNothing(deriv, s.tail) check(matchZipper(z, s) == false) } }.ensuring(_ => matchZipper(z, s) == s.isEmpty) From 3f588bda998407d64665f946eabeeddfcee3f86f Mon Sep 17 00:00:00 2001 From: Samuel Chassot Date: Wed, 13 Nov 2024 11:39:20 +0100 Subject: [PATCH 45/78] add generalisations of union and concat to base regex --- .../scala/ch/epfl/lexer/VerifiedRegex.scala | 107 ++++++++++++------ 1 file changed, 71 insertions(+), 36 deletions(-) diff --git a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedRegex.scala b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedRegex.scala index 2ed25a33..45bfe4b7 100644 --- a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedRegex.scala +++ b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedRegex.scala @@ -102,7 +102,6 @@ object VerifiedRegex { case class Star[C](reg: Regex[C]) extends Regex[C] case class Union[C](regOne: Regex[C], regTwo: Regex[C]) extends Regex[C] case class Concat[C](regOne: Regex[C], regTwo: Regex[C]) extends Regex[C] - /** Regex that accepts only the empty string: represents the language {""} */ case class EmptyExpr[C]() extends Regex[C] @@ -111,10 +110,13 @@ object VerifiedRegex { */ case class EmptyLang[C]() extends Regex[C] + case class GenUnion[C](s: Set[Regex[C]]) extends Regex[C] + case class GenConcat[C](l: List[Regex[C]]) extends Regex[C] + // @ghost def validRegex[C](r: Regex[C]): Boolean = r match { case ElementMatch(c) => true - case Star(r) => !nullable(r) && validRegex(r) // && !isEmptyLang(r) + case Star(r) => !nullable(r) && validRegex(r) case Union(rOne, rTwo) => validRegex(rOne) && validRegex(rTwo) case Concat(rOne, rTwo) => validRegex(rOne) && validRegex(rTwo) case EmptyExpr() => true @@ -337,23 +339,69 @@ object ZipperRegex { // PROOFS ----------------------------------------------------------------------------------------------------- + @extern + @ghost + @opaque + def assume(c: Boolean): Unit = { + () + }.ensuring(_ => c) + @ghost @opaque @inlineOnce def theoremZipperRegexEquiv[C](r: Regex[C], z: Zipper[C], s: List[C]): Unit = { require(validRegex(r)) require(focus(r) == z) + require(Concat(r, unfocus(context))) + decreases(regexDepth(r)) + VerifiedRegexMatcher.mainMatchTheorem(r, s) r match { case EmptyExpr() => lemmaZipperOfEmptyExprMatchesOnlyEmptyString(z, s) case EmptyLang() => lemmaZipperStartingWithEmptyLangMatchesNothing(z, Context(List(r)), s) - case ElementMatch(c) => lemmaElementMatchZipperAcceptsOnlyThisChar(z, Context(List(ElementMatch(c))), c, s) - case Union(r1, r2) => () - case Star(rInner) => () - case Concat(r1, r2) => () + case ElementMatch(a) => lemmaElementMatchZipperAcceptsOnlyThisChar(z, Context(List(ElementMatch(a))), a, s) + case Union(r1, r2) => { + VerifiedRegexMatcher.mainMatchTheorem(r1, s) + VerifiedRegexMatcher.mainMatchTheorem(r2, s) + assert(matchR(r, s) == (matchR(r1, s) || matchR(r2, s))) + s match { + case Nil() => { + lemmaFocusPreservesNullability(r, z) + assert(nullableZipper(z) == nullable(r)) + check(matchZipper(z, s) == matchR(r, s)) + + } + case Cons(shd, stl) => { + val c = Context(List(r)) + val deriv: Zipper[C] = derivationStepZipper(z, shd) + val derivUp = derivationStepZipperUp(c, shd) + if(nullable(r)) { + assert(derivUp == derivationStepZipperDown(r, Context(List()), s.head) ++ derivationStepZipperUp(Context(List()), shd)) + val z1 = derivationStepZipperDown(r, Context(List()), shd) + val z2 = derivationStepZipperUp(Context(List()), shd) + assert(z2 == Set()) + assert(deriv == z1 ++ z2) + lemmaEmptyZipperMatchesNothing(z2, stl) + lemmaZipperConcatMatchesSameAsBothZippers(z1, z2, stl) + // assert(matchZipper(z, s) == matchZipper(z1, s) || matchZipper(z2, s)) + // assert(matchZipper(z, s) == matchZipper(z1, s)) + + } else { + assert(deriv == derivUp) + // assert(derivUp == derivationStepZipperDown(r, Context(List()), s.head)) + // assert(matchZipper(z, s) == matchZipper(derivUp, stl)) + } + } + } + + + + } + case Star(rInner) => assume(matchR(r, s) == matchZipper(focus(r), s)) + case Concat(r1, r2) => assume(matchR(r, s) == matchZipper(focus(r), s)) } - }.ensuring(_ => matchR(r, s) == matchZipper(focus(r), s)) + }.ensuring(_ => matchR(r, s) == matchZipper(z, s)) @ghost @opaque @@ -404,35 +452,18 @@ object ZipperRegex { // LEMMAS ----------------------------------------------------------------------------------------------------- - // @ghost - // @opaque - // @inlineOnce - // def lemmaDerivationStepDownCorrect[C](r: Regex[C], c: Context[C], a: C, s: List[C]): Unit = { - // require(validRegex(r)) - // decreases(s.size) - // val unfocusRegex = Concat(r, unfocusContext(c)) - // VerifiedRegexMatcher.mainMatchTheorem(unfocusRegex, a :: s) - - // r match { - // // case ElementMatch(c) if c == a => Set(context) - // // case Union(rOne, rTwo) => derivationStepZipperDown(rOne, context, a) ++ derivationStepZipperDown(rTwo, context, a) - // // case Concat(rOne, rTwo) if nullable(rOne) => derivationStepZipperDown(rOne, context.prepend(rTwo), a) ++ derivationStepZipperDown(rTwo, context, a) - // // case Concat(rOne, rTwo) => derivationStepZipperDown(rOne, context.prepend(rTwo), a) - // // case Star(rInner) => derivationStepZipperDown(rInner, context.prepend(Star(rInner)), a) - // case ElementMatch(cc) if cc == a => - // assert(derivationStepZipperDown(r, c, a) == Set(c)) - // if(!s.isEmpty && !c.isEmpty){ - // lemmaDerivationStepDownCorrect(c.head, c.tail, s.head, s.tail) - // } - // case Union(rOne, rTwo) => () - // case Concat(rOne, rTwo) if nullable(rOne) => () - // case Concat(rOne, rTwo) => () - // case Star(rInner) => () - // case _ => () - // } - - // }.ensuring(_ => matchR(Concat(r, unfocusContext(c)), a :: s) == matchZipper(derivationStepZipperDown(r, c, a), s)) + @ghost + @opaque + @inlineOnce + def lemmaFocusPreservesNullability[C](r: Regex[C], z: Zipper[C]): Unit = { + require(validRegex(r)) + require(focus(r) == z) + + assert(z == Set(Context(List(r)))) + assert(nullableZipper(z) == z.exists(c => nullableContext(c))) + assert(nullableContext(Context(List(r))) == nullable(r)) + }.ensuring(_ => nullable(r) == nullableZipper(z)) @ghost @opaque @@ -786,9 +817,13 @@ object VerifiedRegexMatcher { case EmptyLang() => false case ElementMatch(c) => s == List(c) case Union(r1, r2) => matchRSpec(r1, s) || matchRSpec(r2, s) + case GenUnion(s) => s.exists(r => matchRSpec(r, s)) case Star(rInner) => s.isEmpty || findConcatSeparation(rInner, Star(rInner), Nil(), s, s).isDefined case Concat(r1, r2) => findConcatSeparation(r1, r2, Nil(), s, s).isDefined - } + case GenConcat(l) => l match { + case Nil() => s.isEmpty + case Cons(rHd, rTl) => findConcatSeparation(rHd, GenConcat(rTl), Nil(), s, s).isDefined + } } @ghost From 747ec07bcfb8f66ef4e29f4064b7e417a7852c0e Mon Sep 17 00:00:00 2001 From: Samuel Chassot Date: Wed, 13 Nov 2024 13:57:00 +0100 Subject: [PATCH 46/78] work on regex --- .../src/main/scala/ch/epfl/lexer/VerifiedRegex.scala | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedRegex.scala b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedRegex.scala index 45bfe4b7..42f6ea90 100644 --- a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedRegex.scala +++ b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedRegex.scala @@ -97,7 +97,7 @@ object Memoisation { } object VerifiedRegex { - abstract sealed class Regex[C] {} + sealed trait Regex[C] case class ElementMatch[C](c: C) extends Regex[C] case class Star[C](reg: Regex[C]) extends Regex[C] case class Union[C](regOne: Regex[C], regTwo: Regex[C]) extends Regex[C] @@ -352,7 +352,6 @@ object ZipperRegex { def theoremZipperRegexEquiv[C](r: Regex[C], z: Zipper[C], s: List[C]): Unit = { require(validRegex(r)) require(focus(r) == z) - require(Concat(r, unfocus(context))) decreases(regexDepth(r)) VerifiedRegexMatcher.mainMatchTheorem(r, s) r match { @@ -817,13 +816,14 @@ object VerifiedRegexMatcher { case EmptyLang() => false case ElementMatch(c) => s == List(c) case Union(r1, r2) => matchRSpec(r1, s) || matchRSpec(r2, s) - case GenUnion(s) => s.exists(r => matchRSpec(r, s)) + case GenUnion(rSet) => rSet.exists(rr => matchRSpec(rr, s)) case Star(rInner) => s.isEmpty || findConcatSeparation(rInner, Star(rInner), Nil(), s, s).isDefined case Concat(r1, r2) => findConcatSeparation(r1, r2, Nil(), s, s).isDefined case GenConcat(l) => l match { case Nil() => s.isEmpty case Cons(rHd, rTl) => findConcatSeparation(rHd, GenConcat(rTl), Nil(), s, s).isDefined } + } } @ghost From 55d1a49423fd7bcaca5c4772e0fa030fb63f09cd Mon Sep 17 00:00:00 2001 From: Samuel Chassot Date: Wed, 13 Nov 2024 17:54:46 +0100 Subject: [PATCH 47/78] revert the generalised regex --- .../main/scala/ch/epfl/lexer/VerifiedRegex.scala | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedRegex.scala b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedRegex.scala index 42f6ea90..9e1cc5da 100644 --- a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedRegex.scala +++ b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedRegex.scala @@ -110,9 +110,6 @@ object VerifiedRegex { */ case class EmptyLang[C]() extends Regex[C] - case class GenUnion[C](s: Set[Regex[C]]) extends Regex[C] - case class GenConcat[C](l: List[Regex[C]]) extends Regex[C] - // @ghost def validRegex[C](r: Regex[C]): Boolean = r match { case ElementMatch(c) => true @@ -816,13 +813,13 @@ object VerifiedRegexMatcher { case EmptyLang() => false case ElementMatch(c) => s == List(c) case Union(r1, r2) => matchRSpec(r1, s) || matchRSpec(r2, s) - case GenUnion(rSet) => rSet.exists(rr => matchRSpec(rr, s)) case Star(rInner) => s.isEmpty || findConcatSeparation(rInner, Star(rInner), Nil(), s, s).isDefined case Concat(r1, r2) => findConcatSeparation(r1, r2, Nil(), s, s).isDefined - case GenConcat(l) => l match { - case Nil() => s.isEmpty - case Cons(rHd, rTl) => findConcatSeparation(rHd, GenConcat(rTl), Nil(), s, s).isDefined - } + // case GenUnion(rSet) => rSet.exists(rr => matchRSpec(rr, s)) + // case GenConcat(l) => l match { + // case Nil() => s.isEmpty + // case Cons(rHd, rTl) => findConcatSeparation(rHd, GenConcat(rTl), Nil(), s, s).isDefined + // } } } From 371f8831582d1f3ae8d9f4efb46e8502032e9c16 Mon Sep 17 00:00:00 2001 From: Samuel Chassot Date: Thu, 14 Nov 2024 10:03:56 +0100 Subject: [PATCH 48/78] add generalised union and concat as method --- .../scala/ch/epfl/lexer/VerifiedLexer.scala | 5 +- .../scala/ch/epfl/lexer/VerifiedRegex.scala | 60 ++++++++++++++++--- 2 files changed, 56 insertions(+), 9 deletions(-) diff --git a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedLexer.scala b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedLexer.scala index 2b9380e0..4650f783 100644 --- a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedLexer.scala +++ b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedLexer.scala @@ -83,7 +83,7 @@ object VerifiedLexer { object Lexer { - @inline + @ghost def ruleValid[C](r: Rule[C]): Boolean = { validRegex(r.regex) && !nullable(r.regex) && r.tag != "" } @@ -94,6 +94,7 @@ object VerifiedLexer { case Cons(hd, tl) => !acc.contains(hd.tag) && noDuplicateTag(tl, Cons(hd.tag, acc)) } } + @ghost def rulesValid[C](rs: List[Rule[C]]): Boolean = { rs match { case Cons(hd, tl) => ruleValid(hd) && rulesValid(tl) @@ -139,7 +140,7 @@ object VerifiedLexer { usedCharacters(r1.regex).forall(c => !usedCharacters(r2.regex).contains(c)) } - @inline + @inline @ghost def rulesInvariant[C](rules: List[Rule[C]]): Boolean = rulesValid(rules) && noDuplicateTag(rules, Nil()) diff --git a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedRegex.scala b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedRegex.scala index 9e1cc5da..26c3684a 100644 --- a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedRegex.scala +++ b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedRegex.scala @@ -110,7 +110,23 @@ object VerifiedRegex { */ case class EmptyLang[C]() extends Regex[C] - // @ghost + def generalisedUnion[C](l: List[Regex[C]]): Regex[C] = { + require(l.forall(validRegex)) + l match { + case Cons(hd, tl) => Union(hd, generalisedUnion(tl)) + case Nil() => EmptyLang() + } + }.ensuring(res => validRegex(res)) + + def generalisedConcat[C](l: List[Regex[C]]): Regex[C] = { + require(l.forall(validRegex)) + l match { + case Cons(hd, tl) => Concat(hd, generalisedConcat(tl)) + case Nil() => EmptyExpr() + } + }.ensuring(res => validRegex(res)) + + @ghost def validRegex[C](r: Regex[C]): Boolean = r match { case ElementMatch(c) => true case Star(r) => !nullable(r) && validRegex(r) @@ -120,7 +136,7 @@ object VerifiedRegex { case EmptyLang() => true } - // @ghost + @ghost def regexDepth[C](r: Regex[C]): BigInt = { decreases(r) r match { @@ -804,6 +820,41 @@ object VerifiedRegexMatcher { // if (input.isEmpty) nullable(rr) else matchRMemSimp(derivativeStepMem(rr, input.head)(cache: Cache[C]), input.tail) // }.ensuring (res => res == matchR(r, input)) + + @ghost + def matchRGenUnionSpec[C](r: Regex[C], l: List[Regex[C]], s: List[C]): Unit = { + require(l.forall(validRegex)) + require(r == generalisedUnion(l)) + mainMatchTheorem(r, s) + r match { + case Union(hd, unionTl) => + assert(matchR(r, s) == (matchRSpec(hd, s) || matchRSpec(unionTl, s))) + mainMatchTheorem(hd, s) + mainMatchTheorem(unionTl, s) + matchRGenUnionSpec(unionTl, l.tail, s) + case _ => () + } + }.ensuring(_ => matchR(r, s) == l.exists(rr => { + require(validRegex(rr)) + matchR(rr, s) + } + )) + + @ghost + def matchRGenConcatSpec[C](r: Regex[C], l: List[Regex[C]], s: List[C]): Unit = { + require(l.forall(validRegex)) + require(r == generalisedConcat(l)) + mainMatchTheorem(r, s) + r match { + case Concat(hd, concatTl) => + assert(matchRSpec(r,s) == findConcatSeparation(hd, concatTl, Nil(), s, s).isDefined) + case _ => () + } + }.ensuring(_ => l match { + case Cons(hd, tl) => matchR(r, s) == findConcatSeparation(hd, generalisedConcat(tl), Nil(), s, s).isDefined + case Nil() => matchR(r, s) == s.isEmpty + }) + @ghost def matchRSpec[C](r: Regex[C], s: List[C]): Boolean = { require(validRegex(r)) @@ -815,11 +866,6 @@ object VerifiedRegexMatcher { case Union(r1, r2) => matchRSpec(r1, s) || matchRSpec(r2, s) case Star(rInner) => s.isEmpty || findConcatSeparation(rInner, Star(rInner), Nil(), s, s).isDefined case Concat(r1, r2) => findConcatSeparation(r1, r2, Nil(), s, s).isDefined - // case GenUnion(rSet) => rSet.exists(rr => matchRSpec(rr, s)) - // case GenConcat(l) => l match { - // case Nil() => s.isEmpty - // case Cons(rHd, rTl) => findConcatSeparation(rHd, GenConcat(rTl), Nil(), s, s).isDefined - // } } } From 26ef6c9c4e5785a37a728b6b05b09eaf955759e4 Mon Sep 17 00:00:00 2001 From: Samuel Chassot Date: Thu, 14 Nov 2024 16:20:41 +0100 Subject: [PATCH 49/78] matchRGenUnionSpec and matchRGenConcatSpec --- lexers/regex/verifiedlexer/build.sbt | 2 +- .../main/scala/ch/epfl/benchmark/Utils.scala | 2 +- .../scala/ch/epfl/lexer/VerifiedRegex.scala | 116 ++++++++++++++---- 3 files changed, 93 insertions(+), 27 deletions(-) diff --git a/lexers/regex/verifiedlexer/build.sbt b/lexers/regex/verifiedlexer/build.sbt index 7c99167e..16c3bf2e 100644 --- a/lexers/regex/verifiedlexer/build.sbt +++ b/lexers/regex/verifiedlexer/build.sbt @@ -1,6 +1,6 @@ name := "VerifiedLexer" version := "0.1.0-SNAPSHOT" -scalaVersion :="3.5.0" +scalaVersion :="3.5.2" run / fork := true diff --git a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/benchmark/Utils.scala b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/benchmark/Utils.scala index 7084ceec..385f1a64 100644 --- a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/benchmark/Utils.scala +++ b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/benchmark/Utils.scala @@ -37,7 +37,7 @@ object RegexUtils { case stainless.collection.Nil() => "" case stainless.collection.Cons(h, t) => h.toString + (if t.isEmpty then "" else inter + t.mkString(inter)) } - extension (c: Context[Char]) def asStringContext(): String = s"Sequence(${c.map(regex => regex.asString()).mkString(", ")})" + extension (c: Context[Char]) def asStringContext(): String = s"Sequence(${c.exprs.map(regex => regex.asString()).mkString(", ")})" extension (z: Zipper[Char]) def asStringZipper(): String = s"Set(${z.map(c => c.asStringContext()).mkString(", ")})" diff --git a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedRegex.scala b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedRegex.scala index 26c3684a..879ea152 100644 --- a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedRegex.scala +++ b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedRegex.scala @@ -113,6 +113,7 @@ object VerifiedRegex { def generalisedUnion[C](l: List[Regex[C]]): Regex[C] = { require(l.forall(validRegex)) l match { + case Cons(hd, tl) if tl.isEmpty => hd case Cons(hd, tl) => Union(hd, generalisedUnion(tl)) case Nil() => EmptyLang() } @@ -121,6 +122,7 @@ object VerifiedRegex { def generalisedConcat[C](l: List[Regex[C]]): Regex[C] = { require(l.forall(validRegex)) l match { + case Cons(hd, tl) if tl.isEmpty => hd case Cons(hd, tl) => Concat(hd, generalisedConcat(tl)) case Nil() => EmptyExpr() } @@ -277,29 +279,23 @@ object ZipperRegex { } type Zipper[C] = Set[Context[C]] - def unfocusContext[C](c: Context[C]): Regex[C] = { - decreases(c.exprs.size) - c.exprs match { - case Cons(hd, tl) if tl.isEmpty => hd - case Cons(hd, tl) => Concat(hd, unfocusContext(Context(tl))) - case Nil() => EmptyExpr() - } + @ghost + def unfocusZipper[C](z: Zipper[C]): Regex[C] = { + VerifiedRegex.generalisedUnion(unfocusZipperList(z.toList)) }.ensuring(res => validRegex(res)) - // def unfocusZipper[C](z: Zipper[C]): Regex[C] = { - // - // // z match { - // // case Cons(hd, tl) if tl.isEmpty => unfocusContext(hd) - // // case Cons(hd, tl) => Union(unfocusContext(hd), unfocusZipper(tl)) - // // case Nil() => EmptyLang() - // // } - // z.flatMap(c =>) - // }.ensuring(res => validRegex(res)) + @ghost + def unfocusZipperList[C](zl: List[Context[C]]): List[Regex[C]] = { + zl match { + case Cons(hd, tl) => Cons(VerifiedRegex.generalisedConcat(hd.exprs), unfocusZipperList(tl)) + case Nil() => Nil() + } + }.ensuring(res => res.forall(validRegex)) def focus[C](r: Regex[C]): Zipper[C] = { require(validRegex(r)) Set(Context(List(r))) - } + }.ensuring(res => unfocusZipper(res) == r) def derivationStepZipperUp[C](context: Context[C], a: C): Zipper[C] = { decreases(context.exprs.size) @@ -362,9 +358,9 @@ object ZipperRegex { @ghost @opaque @inlineOnce - def theoremZipperRegexEquiv[C](r: Regex[C], z: Zipper[C], s: List[C]): Unit = { + def theoremZipperRegexEquiv[C](z: Zipper[C], r: Regex[C], s: List[C]): Unit = { require(validRegex(r)) - require(focus(r) == z) + require(r == unfocusZipper(z)) decreases(regexDepth(r)) VerifiedRegexMatcher.mainMatchTheorem(r, s) r match { @@ -828,10 +824,25 @@ object VerifiedRegexMatcher { mainMatchTheorem(r, s) r match { case Union(hd, unionTl) => - assert(matchR(r, s) == (matchRSpec(hd, s) || matchRSpec(unionTl, s))) - mainMatchTheorem(hd, s) - mainMatchTheorem(unionTl, s) - matchRGenUnionSpec(unionTl, l.tail, s) + if(l.isEmpty){ + check(false) + } else if(l.tail.isEmpty){ + // It means that the head of list (i.e., one of the regex of the generalised union) + // is a union itself, and so this Union is not one of the chain + assert(generalisedUnion(l.tail) == EmptyLang[C]()) + check(matchR(r, s) == l.exists(rr => { + require(validRegex(rr)) + matchR(rr, s) + } + )) + + } else { + // Here the Union we are matching on is a part of the chain built by generalisedUnion + assert(matchR(r, s) == (matchRSpec(hd, s) || matchRSpec(unionTl, s))) + mainMatchTheorem(hd, s) + mainMatchTheorem(unionTl, s) + matchRGenUnionSpec(unionTl, l.tail, s) + } case _ => () } }.ensuring(_ => matchR(r, s) == l.exists(rr => { @@ -845,10 +856,65 @@ object VerifiedRegexMatcher { require(l.forall(validRegex)) require(r == generalisedConcat(l)) mainMatchTheorem(r, s) - r match { + r match { case Concat(hd, concatTl) => assert(matchRSpec(r,s) == findConcatSeparation(hd, concatTl, Nil(), s, s).isDefined) - case _ => () + if(l.isEmpty) { + check(false) + } else if(l.tail.isEmpty){ + // Here the Concat we are matching on is NOT a part of the chain built by generalisedConcat + // it means that the head of the list is a Concat itself + assert(generalisedConcat(l.tail) == EmptyExpr[C]()) + if(matchR(l.head, s)) { + lemmaTwoRegexMatchThenConcatMatchesConcatString(l.head, EmptyExpr[C](), s, Nil()) + check(matchR(Concat(l.head, EmptyExpr[C]()), s)) + lemmaConcatAcceptsStringThenFindSeparationIsDefined(l.head, EmptyExpr[C](), s) + check(findConcatSeparation(l.head, generalisedConcat(l.tail), Nil(), s, s).isDefined) + } else { + val cut = findConcatSeparation(l.head, EmptyExpr[C](), Nil(), s, s) + if(cut.isDefined) { + lemmaFindSeparationIsDefinedThenConcatMatches(l.head, EmptyExpr[C](), cut.get._1, cut.get._2, s) + check(false) + } + check(!cut.isDefined) + check(!findConcatSeparation(l.head, generalisedConcat(l.tail), Nil(), s, s).isDefined) + } + check(matchR(r, s) == findConcatSeparation(l.head, generalisedConcat(l.tail), Nil(), s, s).isDefined ) + } else { + // Here the Concat we are matching on is a part of the chain built by generalisedConcat + check(matchR(r, s) == findConcatSeparation(l.head, generalisedConcat(l.tail), Nil(), s, s).isDefined ) + } + check(l match { + case Cons(hd, tl) => matchR(r, s) == findConcatSeparation(hd, generalisedConcat(tl), Nil(), s, s).isDefined + case Nil() => matchR(r, s) == s.isEmpty + }) + case EmptyExpr() => + check(l match { + case Cons(hd, tl) => matchR(r, s) == findConcatSeparation(hd, generalisedConcat(tl), Nil(), s, s).isDefined + case Nil() => matchR(r, s) == s.isEmpty + }) + case _ => + assert(!l.isEmpty) + assert(l.tail.isEmpty) + assert(generalisedConcat(l.tail) == EmptyExpr[C]()) + if(matchR(l.head, s)) { + lemmaTwoRegexMatchThenConcatMatchesConcatString(l.head, EmptyExpr[C](), s, Nil()) + check(matchR(Concat(l.head, EmptyExpr[C]()), s)) + lemmaConcatAcceptsStringThenFindSeparationIsDefined(l.head, EmptyExpr[C](), s) + check(findConcatSeparation(l.head, generalisedConcat(l.tail), Nil(), s, s).isDefined) + } else { + val cut = findConcatSeparation(l.head, EmptyExpr[C](), Nil(), s, s) + if(cut.isDefined) { + lemmaFindSeparationIsDefinedThenConcatMatches(l.head, EmptyExpr[C](), cut.get._1, cut.get._2, s) + check(false) + } + check(!cut.isDefined) + check(!findConcatSeparation(l.head, generalisedConcat(l.tail), Nil(), s, s).isDefined) + } + check(l match { + case Cons(hd, tl) => matchR(r, s) == findConcatSeparation(hd, generalisedConcat(tl), Nil(), s, s).isDefined + case Nil() => matchR(r, s) == s.isEmpty + }) } }.ensuring(_ => l match { case Cons(hd, tl) => matchR(r, s) == findConcatSeparation(hd, generalisedConcat(tl), Nil(), s, s).isDefined From 902b7f48f393af85c5f47d97c2c0d616edce451b Mon Sep 17 00:00:00 2001 From: Samuel Chassot Date: Thu, 14 Nov 2024 19:28:05 +0100 Subject: [PATCH 50/78] working on zipper proof --- .../scala/ch/epfl/lexer/VerifiedRegex.scala | 253 +++++++++++++----- 1 file changed, 182 insertions(+), 71 deletions(-) diff --git a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedRegex.scala b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedRegex.scala index 879ea152..9934b1f3 100644 --- a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedRegex.scala +++ b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedRegex.scala @@ -320,14 +320,6 @@ object ZipperRegex { } } - def derivationStepZipperAllInOne[C](context: Context[C], a: C): Zipper[C] = { - context.exprs match { - case Cons(right, parent) if nullable(right) => derivationStepZipperDown(right, Context(parent), a) ++ derivationStepZipperUp(Context(parent), a) - case Cons(right, parent) => derivationStepZipperDown(right, Context(parent), a) - case Nil() => Set() - } - } - // @inlineOnce def derivationStepZipper[C](z: Zipper[C], a: C): Zipper[C] = { z.flatMap(c => derivationStepZipperUp(c, a)) @@ -355,6 +347,37 @@ object ZipperRegex { () }.ensuring(_ => c) + @ghost + @opaque + @inlineOnce + def lemmaZipperContainsContextThenUnfocusZipperListContains[C](zl: List[Context[C]], c: Context[C]): Unit = { + require(zl.contains(c)) + decreases(zl.size) + zl match { + case Cons(hd, tl) => + if(hd != c){ + lemmaZipperContainsContextThenUnfocusZipperListContains(tl, c) + } + case Nil() => () + } + }.ensuring(_ => unfocusZipperList(zl).contains(VerifiedRegex.generalisedConcat(c.exprs))) + + @ghost + @opaque + @inlineOnce + def lemmaUnfocusZipperListContainsRegexFromContextThenZipperContains[C](zl: List[Context[C]], r: Regex[C]): Unit = { + require(unfocusZipperList(zl).contains(r)) + decreases(zl.size) + zl match { + case Cons(hd, tl) => + if(VerifiedRegex.generalisedConcat(hd.exprs) != r){ + lemmaUnfocusZipperListContainsRegexFromContextThenZipperContains(tl, r) + } + case Nil() => () + } + }.ensuring(_ => zl.exists(c => VerifiedRegex.generalisedConcat(c.exprs) == r)) + + @ghost @opaque @inlineOnce @@ -365,47 +388,34 @@ object ZipperRegex { VerifiedRegexMatcher.mainMatchTheorem(r, s) r match { case EmptyExpr() => lemmaZipperOfEmptyExprMatchesOnlyEmptyString(z, s) - case EmptyLang() => lemmaZipperStartingWithEmptyLangMatchesNothing(z, Context(List(r)), s) + case EmptyLang() => + if(z.isEmpty){ + lemmaEmptyZipperMatchesNothing(z, s) + } else { + lemmaZipperStartingWithEmptyLangMatchesNothing(z, Context(List(r)), s) + } case ElementMatch(a) => lemmaElementMatchZipperAcceptsOnlyThisChar(z, Context(List(ElementMatch(a))), a, s) - case Union(r1, r2) => { + case Union(r1, r2) => + { VerifiedRegexMatcher.mainMatchTheorem(r1, s) VerifiedRegexMatcher.mainMatchTheorem(r2, s) assert(matchR(r, s) == (matchR(r1, s) || matchR(r2, s))) s match { case Nil() => { - lemmaFocusPreservesNullability(r, z) + lemmaUnfocusPreservesNullability(r, z) assert(nullableZipper(z) == nullable(r)) check(matchZipper(z, s) == matchR(r, s)) - } case Cons(shd, stl) => { - val c = Context(List(r)) - val deriv: Zipper[C] = derivationStepZipper(z, shd) - val derivUp = derivationStepZipperUp(c, shd) - if(nullable(r)) { - assert(derivUp == derivationStepZipperDown(r, Context(List()), s.head) ++ derivationStepZipperUp(Context(List()), shd)) - val z1 = derivationStepZipperDown(r, Context(List()), shd) - val z2 = derivationStepZipperUp(Context(List()), shd) - assert(z2 == Set()) - assert(deriv == z1 ++ z2) - lemmaEmptyZipperMatchesNothing(z2, stl) - lemmaZipperConcatMatchesSameAsBothZippers(z1, z2, stl) - // assert(matchZipper(z, s) == matchZipper(z1, s) || matchZipper(z2, s)) - // assert(matchZipper(z, s) == matchZipper(z1, s)) - - } else { - assert(deriv == derivUp) - // assert(derivUp == derivationStepZipperDown(r, Context(List()), s.head)) - // assert(matchZipper(z, s) == matchZipper(derivUp, stl)) - } + assert(matchZipper(z, s) == matchZipper(derivationStepZipper(z, shd), stl)) + assert(r == VerifiedRegex.generalisedUnion(unfocusZipperList(z.toList))) + matchRGenUnionSpec(r, unfocusZipperList(z.toList), s) + assert(matchR(r, s) == unfocusZipperList(z.toList).exists(rr => validRegex(rr) && matchR(rr, s))) } } - - - } - case Star(rInner) => assume(matchR(r, s) == matchZipper(focus(r), s)) - case Concat(r1, r2) => assume(matchR(r, s) == matchZipper(focus(r), s)) + case Star(rInner) => assume(matchR(r, s) == matchZipper(z, s)) + case Concat(r1, r2) => assume(matchR(r, s) == matchZipper(z, s)) } @@ -463,13 +473,50 @@ object ZipperRegex { @ghost @opaque @inlineOnce - def lemmaFocusPreservesNullability[C](r: Regex[C], z: Zipper[C]): Unit = { + def lemmaUnfocusPreservesNullability[C](r: Regex[C], z: Zipper[C]): Unit = { require(validRegex(r)) - require(focus(r) == z) + require(r == unfocusZipper(z)) + decreases(regexDepth(r)) + + val reg = VerifiedRegex.generalisedUnion(unfocusZipperList(z.toList)) + assert(r == reg) + nullableGenUnionSpec(reg, unfocusZipperList(z.toList)) + assert(nullable(reg) == unfocusZipperList(z.toList).exists(rr => nullable(rr))) + if(nullable(reg)){ + assert( unfocusZipperList(z.toList).exists(rr => nullable(rr))) + val witnessNullableReg = ListUtils.getWitness(unfocusZipperList(z.toList), (rr: Regex[C]) => nullable(rr)) + assert(nullable(witnessNullableReg)) + assert(unfocusZipperList(z.toList).contains(witnessNullableReg)) + lemmaUnfocusZipperListContainsRegexFromContextThenZipperContains(z.toList, witnessNullableReg) + assert(z.exists(c => VerifiedRegex.generalisedConcat(c.exprs) == witnessNullableReg)) + val witnessContext = SetUtils.getWitness(z, (c: Context[C]) => VerifiedRegex.generalisedConcat(c.exprs) == witnessNullableReg) + assert(z.contains(witnessContext)) + assert(VerifiedRegex.generalisedConcat(witnessContext.exprs) == witnessNullableReg) + assert(nullable(VerifiedRegex.generalisedConcat(witnessContext.exprs))) + nullableGenConcatSpec(witnessNullableReg, witnessContext.exprs) + assert(nullableContext(witnessContext)) + SetUtils.lemmaContainsThenExists(z, witnessContext, a => nullableContext(a)) + assert(nullableZipper(z)) + } else { + assert(!unfocusZipperList(z.toList).exists(rr => nullable(rr))) + if(z.exists(c => nullableContext(c))){ + val witnessContext = SetUtils.getWitness(z, (c: Context[C]) => nullableContext(c)) + assert(z.contains(witnessContext)) + assert(nullableContext(witnessContext)) + lemmaZipperContainsContextThenUnfocusZipperListContains(z.toList, witnessContext) + assert(unfocusZipperList(z.toList).contains(VerifiedRegex.generalisedConcat(witnessContext.exprs))) + nullableGenConcatSpec(VerifiedRegex.generalisedConcat(witnessContext.exprs), witnessContext.exprs) + assert(nullable(VerifiedRegex.generalisedConcat(witnessContext.exprs))) + ListUtils.lemmaContainsThenExists(unfocusZipperList(z.toList), VerifiedRegex.generalisedConcat(witnessContext.exprs), rr => nullable(rr)) + assert(unfocusZipperList(z.toList).exists(rr => nullable(rr))) + check(false) + } + + assert(!z.exists(c => nullableContext(c))) + assert(!nullableZipper(z)) + } + - assert(z == Set(Context(List(r)))) - assert(nullableZipper(z) == z.exists(c => nullableContext(c))) - assert(nullableContext(Context(List(r))) == nullable(r)) }.ensuring(_ => nullable(r) == nullableZipper(z)) @@ -654,27 +701,52 @@ object ZipperRegex { @opaque @inlineOnce def lemmaZipperOfEmptyExprMatchesOnlyEmptyString[C](z: Zipper[C], s: List[C]): Unit = { - require(z == focus(EmptyExpr[C]())) - check(nullableContext(Context(List(EmptyExpr[C]())))) - val c = Context(List(EmptyExpr[C]())) - if (s.isEmpty) { - check(nullableContext(c)) - } else { - val deriv: Zipper[C]= derivationStepZipper(z, s.head) - val derivUp = derivationStepZipperUp(c, s.head) - assert(derivUp == Set()) - if(!deriv.isEmpty){ - val hd = deriv.toList.head - val f: Context[C] => Zipper[C] = ct => derivationStepZipperUp(ct, s.head) - assert(z.flatMap(f).contains(hd)) - assert(deriv.contains(hd)) - unfold(z.flatMapPost(f)(hd)) - assert(z.exists(context => derivationStepZipperUp(context, s.head).contains(hd))) - assert(z == Set(c)) - check(false) + require(unfocusZipper(z) == EmptyExpr[C]()) + if(z == focus(EmptyExpr[C]())){ + check(nullableContext(Context(List(EmptyExpr[C]())))) + val c = Context(List(EmptyExpr[C]())) + if (s.isEmpty) { + check(nullableContext(c)) + } else { + val deriv: Zipper[C]= derivationStepZipper(z, s.head) + val derivUp = derivationStepZipperUp(c, s.head) + assert(derivUp == Set()) + if(!deriv.isEmpty){ + val hd = deriv.toList.head + val f: Context[C] => Zipper[C] = ct => derivationStepZipperUp(ct, s.head) + assert(z.flatMap(f).contains(hd)) + assert(deriv.contains(hd)) + unfold(z.flatMapPost(f)(hd)) + assert(z.exists(context => derivationStepZipperUp(context, s.head).contains(hd))) + assert(z == Set(c)) + check(false) + } + lemmaEmptyZipperMatchesNothing(deriv, s.tail) + check(matchZipper(z, s) == false) + } + } + else{ + val c: Context[C] = Context(List()) + assert(z == Set(c)) + if (s.isEmpty) { + check(nullableContext(c)) + } else { + val deriv: Zipper[C]= derivationStepZipper(z, s.head) + val derivUp = derivationStepZipperUp(c, s.head) + assert(derivUp == Set()) + if(!deriv.isEmpty){ + val hd = deriv.toList.head + val f: Context[C] => Zipper[C] = ct => derivationStepZipperUp(ct, s.head) + assert(z.flatMap(f).contains(hd)) + assert(deriv.contains(hd)) + unfold(z.flatMapPost(f)(hd)) + assert(z.exists(context => derivationStepZipperUp(context, s.head).contains(hd))) + assert(z == Set(c)) + check(false) + } + lemmaEmptyZipperMatchesNothing(deriv, s.tail) + check(matchZipper(z, s) == false) } - lemmaEmptyZipperMatchesNothing(deriv, s.tail) - check(matchZipper(z, s) == false) } }.ensuring(_ => matchZipper(z, s) == s.isEmpty) @@ -816,11 +888,34 @@ object VerifiedRegexMatcher { // if (input.isEmpty) nullable(rr) else matchRMemSimp(derivativeStepMem(rr, input.head)(cache: Cache[C]), input.tail) // }.ensuring (res => res == matchR(r, input)) + @ghost + @opaque + @inlineOnce + def nullableGenUnionSpec[C](r: Regex[C], l: List[Regex[C]]): Unit = { + require(l.forall(validRegex)) + require(r == generalisedUnion(l)) + decreases(l.size) + l match { + case Cons(hd, tl) if tl.isEmpty => () + case Cons(hd, tl) => { + r match { + case Union(rHd, rTl) => + nullableGenUnionSpec(rTl, tl) + case _ => () + } + } + case Nil() => () + } + }.ensuring(_ => nullable(r) == l.exists(rr => nullable(rr))) @ghost + @opaque + @inlineOnce def matchRGenUnionSpec[C](r: Regex[C], l: List[Regex[C]], s: List[C]): Unit = { require(l.forall(validRegex)) require(r == generalisedUnion(l)) + decreases(l.size) + mainMatchTheorem(r, s) r match { case Union(hd, unionTl) => @@ -830,11 +925,7 @@ object VerifiedRegexMatcher { // It means that the head of list (i.e., one of the regex of the generalised union) // is a union itself, and so this Union is not one of the chain assert(generalisedUnion(l.tail) == EmptyLang[C]()) - check(matchR(r, s) == l.exists(rr => { - require(validRegex(rr)) - matchR(rr, s) - } - )) + check(matchR(r, s) == l.exists(rr => validRegex(rr) && matchR(rr, s))) } else { // Here the Union we are matching on is a part of the chain built by generalisedUnion @@ -845,13 +936,33 @@ object VerifiedRegexMatcher { } case _ => () } - }.ensuring(_ => matchR(r, s) == l.exists(rr => { - require(validRegex(rr)) - matchR(rr, s) - } - )) + }.ensuring(_ => matchR(r, s) == l.exists(rr => validRegex(rr) && matchR(rr, s))) + + + @ghost + @opaque + @inlineOnce + def nullableGenConcatSpec[C](r: Regex[C], l: List[Regex[C]]): Unit = { + require(l.forall(validRegex)) + require(r == generalisedConcat(l)) + decreases(l.size) + l match { + case Cons(hd, tl) if tl.isEmpty => () + case Cons(hd, tl) => { + r match { + case Concat(rHd, rTl) => + nullableGenConcatSpec(rTl, tl) + case _ => () + } + } + case Nil() => () + } + }.ensuring(_ => nullable(r) == l.forall(rr => nullable(rr))) + @ghost + @opaque + @inlineOnce def matchRGenConcatSpec[C](r: Regex[C], l: List[Regex[C]], s: List[C]): Unit = { require(l.forall(validRegex)) require(r == generalisedConcat(l)) From 592a260230f109a1c9bcf3d2734323d1c71d81f4 Mon Sep 17 00:00:00 2001 From: Samuel Chassot Date: Mon, 18 Nov 2024 21:23:02 +0100 Subject: [PATCH 51/78] working on Zipper theorem --- .../src/main/scala/ch/epfl/lexer/Utils.scala | 64 +++ .../scala/ch/epfl/lexer/VerifiedRegex.scala | 380 ++++++++++++++++-- lexers/regex/verifiedlexer/stainless.conf | 2 +- 3 files changed, 421 insertions(+), 25 deletions(-) diff --git a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/Utils.scala b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/Utils.scala index 26e67c27..1b421045 100644 --- a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/Utils.scala +++ b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/Utils.scala @@ -9,6 +9,7 @@ import stainless.lang.{ghost => ghostExpr, _} import stainless.proof.check import scala.annotation.tailrec import stainless.lang.StaticChecks._ +import ch.epfl.lexer.ListUtils.lemmaSubseqRefl object SetUtils { @@ -148,6 +149,69 @@ object SetUtils { }.ensuring(_ => l2.forall(l1.contains)) + @opaque + @ghost + @inlineOnce + def lemmaFlatMapOnSingletonSet[A, B](s: Set[A], elmt: A, f: A => Set[B]): Unit = { + require(s == Set(elmt)) + val ftmap = s.flatMap(f) + lemmaSubseqRefl(ftmap.toList) + lemmaSubseqRefl(f(elmt).toList) + lemmaFlatMapOnSingletonSetList1(s, elmt, f, ftmap.toList) + lemmaFlatMapOnSingletonSetList2(s, elmt, f, f(elmt).toList) + ListSpecs.forallContainsSubset(ftmap.toList, f(elmt).toList) + ListSpecs.forallContainsSubset(f(elmt).toList, ftmap.toList) + }.ensuring(_ => s.flatMap(f) == f(elmt)) + + @opaque + @ghost + @inlineOnce + def lemmaFlatMapOnSingletonSetList1[A, B](s: Set[A], elmt: A, f: A => Set[B], lRes: List[B]): Unit = { + require(s == Set(elmt)) + require(ListSpecs.subseq(lRes, s.flatMap(f).toList)) + decreases(lRes) + lRes match { + case Cons(hd, tl) => { + ListSpecs.subseqTail(lRes, s.flatMap(f).toList) + lemmaFlatMapOnSingletonSetList1(s, elmt, f, tl) + check(tl.forall(f(elmt).toList.contains)) + ListSpecs.subseqContains(lRes, s.flatMap(f).toList, hd) + assert(s.flatMap(f).contains(hd)) + unfold(s.flatMapPost(f)(hd)) + assert(s.exists(a => f(a).contains(hd))) + val witness = getWitness(s, a => f(a).contains(hd)) + assert(s.toList.contains(witness)) + assert(witness == elmt) + check(f(elmt).toList.contains(hd)) + check(lRes.forall(f(elmt).toList.contains)) + } + case Nil() => () + } + }.ensuring(_ => lRes.forall(f(elmt).toList.contains)) + + @opaque + @ghost + @inlineOnce + def lemmaFlatMapOnSingletonSetList2[A, B](s: Set[A], elmt: A, f: A => Set[B], lRes: List[B]): Unit = { + require(s == Set(elmt)) + require(ListSpecs.subseq(lRes, f(elmt).toList)) + decreases(lRes) + lRes match { + case Cons(hd, tl) => { + ListSpecs.subseqTail(lRes, f(elmt).toList) + lemmaFlatMapOnSingletonSetList2(s, elmt, f, tl) + check(tl.forall(s.flatMap(f).toList.contains)) + ListSpecs.subseqContains(lRes, f(elmt).toList, hd) + check(f(elmt).toList.contains(hd)) + unfold(s.flatMapPost(f)(hd)) + check(s.flatMap(f).toList.contains(hd)) + check(lRes.forall(s.flatMap(f).toList.contains)) + } + case Nil() => () + } + }.ensuring(_ => lRes.forall(s.flatMap(f).toList.contains)) + + @ghost def getWitness[A](s: Set[A], p: A => Boolean): A = { require(s.exists(p)) diff --git a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedRegex.scala b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedRegex.scala index 9934b1f3..6bb97156 100644 --- a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedRegex.scala +++ b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedRegex.scala @@ -117,7 +117,7 @@ object VerifiedRegex { case Cons(hd, tl) => Union(hd, generalisedUnion(tl)) case Nil() => EmptyLang() } - }.ensuring(res => validRegex(res)) + }.ensuring(res => validRegex(res) && (if(l.isEmpty) isEmptyLang(res) else if(l.tail.isEmpty) res == l.head else isUnion(res))) def generalisedConcat[C](l: List[Regex[C]]): Regex[C] = { require(l.forall(validRegex)) @@ -126,7 +126,7 @@ object VerifiedRegex { case Cons(hd, tl) => Concat(hd, generalisedConcat(tl)) case Nil() => EmptyExpr() } - }.ensuring(res => validRegex(res)) + }.ensuring(res => validRegex(res) && (if(l.isEmpty) isEmptyExpr(res) else if(l.tail.isEmpty) res == l.head else isConcat(res))) @ghost def validRegex[C](r: Regex[C]): Boolean = r match { @@ -280,14 +280,14 @@ object ZipperRegex { type Zipper[C] = Set[Context[C]] @ghost - def unfocusZipper[C](z: Zipper[C]): Regex[C] = { - VerifiedRegex.generalisedUnion(unfocusZipperList(z.toList)) + def unfocusZipper[C](zl: List[Context[C]]): Regex[C] = { + generalisedUnion(unfocusZipperList(zl)) }.ensuring(res => validRegex(res)) @ghost def unfocusZipperList[C](zl: List[Context[C]]): List[Regex[C]] = { zl match { - case Cons(hd, tl) => Cons(VerifiedRegex.generalisedConcat(hd.exprs), unfocusZipperList(tl)) + case Cons(hd, tl) => Cons(generalisedConcat(hd.exprs), unfocusZipperList(tl)) case Nil() => Nil() } }.ensuring(res => res.forall(validRegex)) @@ -295,7 +295,52 @@ object ZipperRegex { def focus[C](r: Regex[C]): Zipper[C] = { require(validRegex(r)) Set(Context(List(r))) - }.ensuring(res => unfocusZipper(res) == r) + }.ensuring(res => unfocusZipper(res.toList) == r) + + @inlineOnce + @ghost + @opaque + def lemmaForallRegexDepthBiggerThanTransitive[C](@induct l: List[Regex[C]], a: BigInt, b: BigInt): Unit = { + require(a >= b) + require(l.forall(r => b >= regexDepth(r))) + + }.ensuring(_ => l.forall(r => a >= regexDepth(r))) + + @inlineOnce + @ghost + @opaque + def lemmaForallContextDepthBiggerThanTransitive[C](@induct l: List[Context[C]], a: BigInt, b: BigInt, f: Context[C] => BigInt): Unit = { + require(a >= b) + require(l.forall(r => b >= f(r))) + + }.ensuring(_ => l.forall(c => a >= f(c))) + + @ghost + @pure + def contextDepth[C](c: Context[C]): BigInt = { + decreases(c.exprs.size) + c.exprs match { + case Cons(hd, tl) => + val res = Utils.maxBigInt(regexDepth(hd), contextDepth(Context(tl))) + lemmaForallRegexDepthBiggerThanTransitive(tl, res, contextDepth(Context(tl))) + res + case Nil() => BigInt(0) + } + }.ensuring(res => res >= 0 && c.exprs.forall(r => res >= regexDepth(r))) + + @ghost + @pure + def zipperDepth[C](zl: List[Context[C]]): BigInt = { + decreases(zl.size) + zl match { + case Cons(hd, tl) => + val res = Utils.maxBigInt(contextDepth(hd), zipperDepth(tl)) + lemmaForallContextDepthBiggerThanTransitive(tl, res, zipperDepth(tl), contextDepth) + res + case Nil() => BigInt(0) + } + }.ensuring(res => res >= 0 && zl.forall(c => res >= contextDepth(c))) + def derivationStepZipperUp[C](context: Context[C], a: C): Zipper[C] = { decreases(context.exprs.size) @@ -381,11 +426,12 @@ object ZipperRegex { @ghost @opaque @inlineOnce - def theoremZipperRegexEquiv[C](z: Zipper[C], r: Regex[C], s: List[C]): Unit = { + def theoremZipperRegexEquiv[C](z: Zipper[C], zl: List[Context[C]], r: Regex[C], s: List[C]): Unit = { require(validRegex(r)) - require(r == unfocusZipper(z)) + require(z.toList == zl) + require(r == unfocusZipper(zl)) decreases(regexDepth(r)) - VerifiedRegexMatcher.mainMatchTheorem(r, s) + mainMatchTheorem(r, s) r match { case EmptyExpr() => lemmaZipperOfEmptyExprMatchesOnlyEmptyString(z, s) case EmptyLang() => @@ -397,8 +443,8 @@ object ZipperRegex { case ElementMatch(a) => lemmaElementMatchZipperAcceptsOnlyThisChar(z, Context(List(ElementMatch(a))), a, s) case Union(r1, r2) => { - VerifiedRegexMatcher.mainMatchTheorem(r1, s) - VerifiedRegexMatcher.mainMatchTheorem(r2, s) + mainMatchTheorem(r1, s) + mainMatchTheorem(r2, s) assert(matchR(r, s) == (matchR(r1, s) || matchR(r2, s))) s match { case Nil() => { @@ -407,10 +453,37 @@ object ZipperRegex { check(matchZipper(z, s) == matchR(r, s)) } case Cons(shd, stl) => { + assert(!z.toList.isEmpty) + val zHd: Context[C] = z.toList.head + val zTl: List[Context[C]] = z.toList.tail + assert(matchZipper(z, s) == matchZipper(derivationStepZipper(z, shd), stl)) - assert(r == VerifiedRegex.generalisedUnion(unfocusZipperList(z.toList))) + assert(r == generalisedUnion(unfocusZipperList(z.toList))) matchRGenUnionSpec(r, unfocusZipperList(z.toList), s) assert(matchR(r, s) == unfocusZipperList(z.toList).exists(rr => validRegex(rr) && matchR(rr, s))) + + if(zTl.isEmpty) { + assert(r == generalisedConcat(zHd.exprs)) + assert(zHd.exprs == List(r)) + // Now let's dive in the derivative computation + val deriv = derivationStepZipper(z, shd) + val derivUp = derivationStepZipperUp(Context(List(r)), shd) + zHd.exprs match { + case Cons(right, parent) if nullable(right) => assert(derivationStepZipperDown(right, Context(parent), shd) ++ derivationStepZipperUp(Context(parent),shd) == derivationStepZipperUp(Context(List(r)), shd)) + case Cons(right, parent) => assert(derivationStepZipperDown(right, Context(parent), shd) == derivationStepZipperUp(Context(List(r)), shd)) + case Nil() => assert(Set[Context[C]]() == derivationStepZipperUp(Context(List(r)), shd)) + } + + } else { + unfold(generalisedUnion(unfocusZipperList(z.toList))) + assert(r2 == unfocusZipper(zTl)) + } + + + + + + } } } @@ -419,6 +492,265 @@ object ZipperRegex { } + }.ensuring(_ => matchR(r, s) == matchZipper(z, s)) + + @ghost + @opaque + @inlineOnce + def theoremZipperRegexEquivInductZ[C](z: Zipper[C], zl: List[Context[C]], r: Regex[C], s: List[C]): Unit = { + require(validRegex(r)) + require(z.toList == zl) + require(r == unfocusZipper(zl)) + decreases(regexDepth(r) + zipperDepth(zl) + (if zl.isEmpty || zl.head.isEmpty then BigInt(0) else regexDepth(zl.head.exprs.head)) * 2) // Pretty insane but so beautiful haha + + mainMatchTheorem(r, s) + zl match { + case Cons(hd, tl) if tl.isEmpty => { + assert(r == generalisedConcat(hd.exprs)) + hd.exprs match { + case Cons(hExp, tlExp) if tlExp.isEmpty => { + assert(r == hExp) + r match { + case EmptyExpr() => lemmaZipperOfEmptyExprMatchesOnlyEmptyString(z, s) + case EmptyLang() => lemmaZipperStartingWithEmptyLangMatchesNothing(z, Context(List(r)), s) + case ElementMatch(a) => lemmaElementMatchZipperAcceptsOnlyThisChar(z, Context(List(ElementMatch(a))), a, s) + case Union(r1, r2) => { + mainMatchTheorem(r1, s) + mainMatchTheorem(r2, s) + assert(matchR(r, s) == (matchR(r1, s) || matchR(r2, s))) + s match { + case Nil() => { + lemmaUnfocusPreservesNullability(r, z) + assert(nullableZipper(z) == nullable(r)) + check(matchZipper(z, s) == matchR(r, s)) + } + case Cons(shd, stl) => { + val deriv = derivationStepZipper(z, shd) + val derivUp = derivationStepZipperUp(Context(List(r)), shd) + val derivDown = derivationStepZipperDown(r, Context(List()), shd) + assert(derivUp == derivDown) + assert(derivDown == derivationStepZipperDown(r1, Context(List()), shd) ++ derivationStepZipperDown(r2, Context(List()), shd)) + val z1 = derivationStepZipperDown(r1, Context(List()), shd) + val z2 = derivationStepZipperDown(r2, Context(List()), shd) + SetUtils.lemmaFlatMapOnSingletonSet(z, hd, (c: Context[C]) => derivationStepZipperUp(c, shd)) + assert(deriv == z1 ++ z2) + assert(matchZipper(z1 ++ z2, stl) == matchZipper(z, s)) + lemmaZipperConcatMatchesSameAsBothZippers(z1, z2, stl) + assert((matchZipper(z1, stl) || matchZipper(z2, stl)) == matchZipper(z1 ++ z2, stl)) + assert((matchZipper(z1, stl) || matchZipper(z2, stl)) == matchZipper(z, s)) + + val zR1 = Set(Context(List(r1))) + val zR2 = Set(Context(List(r2))) + val derivZR1 = derivationStepZipper(zR1, shd) + val derivZR2 = derivationStepZipper(zR2, shd) + + val derivUpZR1 = derivationStepZipperUp(Context(List(r1)), shd) + val derivUpZR2 = derivationStepZipperUp(Context(List(r2)), shd) + SetUtils.lemmaFlatMapOnSingletonSet(zR1, Context(List(r1)), (c: Context[C]) => derivationStepZipperUp(c, shd)) + SetUtils.lemmaFlatMapOnSingletonSet(zR2, Context(List(r2)), (c: Context[C]) => derivationStepZipperUp(c, shd)) + + val zR1Deriv = derivationStepZipperDown(r1, Context(List()), shd) + val zR2Deriv = derivationStepZipperDown(r2, Context(List()), shd) + assert(zR1Deriv == z1) + assert(zR2Deriv == z2) + assert(matchZipper(zR1, s) == matchZipper(zR1Deriv, stl)) + assert(matchZipper(zR2, s) == matchZipper(zR2Deriv, stl)) + assert((matchZipper(zR1, s) || matchZipper(zR2, s)) == matchZipper(z, s)) + theoremZipperRegexEquivInductZ(zR1, List(Context(List(r1))), r1, s) + theoremZipperRegexEquivInductZ(zR2, List(Context(List(r2))), r2, s) + } + } + } + case Concat(r1, r2) => { + assert(matchR(r, s) == findConcatSeparation(r1, r2, Nil(), s, s).isDefined) + s match { + case Nil() => { + lemmaUnfocusPreservesNullability(r, z) + assert(nullableZipper(z) == nullable(r)) + check(matchZipper(z, s) == matchR(r, s)) + } + case Cons(shd, stl) => { + val zDeriv = derivationStepZipper(z, shd) + val zDerivUp = derivationStepZipperUp(Context(List(r)), shd) + val zDerivDown = derivationStepZipperDown(r, Context(List()), shd) + assert(zDerivUp == zDerivDown) + SetUtils.lemmaFlatMapOnSingletonSet(z, hd, (c: Context[C]) => derivationStepZipperUp(c, shd)) + assert(zDeriv == zDerivDown) + val zR1 = Set(Context(List(r1, r2))) + val zR2 = Set(Context(List(r2))) + val derivZR1 = derivationStepZipper(zR1, shd) + val derivZR2 = derivationStepZipper(zR2, shd) + + val derivUpZR1 = derivationStepZipperUp(Context(List(r1, r2)), shd) + val derivUpZR2 = derivationStepZipperUp(Context(List(r2)), shd) + + SetUtils.lemmaFlatMapOnSingletonSet(zR1, Context(List(r1, r2)), (c: Context[C]) => derivationStepZipperUp(c, shd)) + SetUtils.lemmaFlatMapOnSingletonSet(zR2, Context(List(r2)), (c: Context[C]) => derivationStepZipperUp(c, shd)) + + val derivDownZR1 = derivationStepZipperDown(r1, Context(List(r2)), shd) + val derivDownZR2 = derivationStepZipperDown(r2, Context(List()), shd) + + if(nullable(r1)){ + assert(zDeriv == derivDownZR1 ++ derivDownZR2) + lemmaZipperConcatMatchesSameAsBothZippers(derivDownZR1, derivDownZR2, stl) + assert((matchZipper(derivDownZR1, stl) || matchZipper(derivDownZR2, stl)) == matchZipper(zDeriv, stl)) + + assert(derivUpZR1 == derivDownZR1 ++ derivUpZR2) + assert(derivUpZR1 == derivDownZR1 ++ derivDownZR2) + assert(derivUpZR1 == zDeriv) + + assert(matchZipper(zR1, s) == matchZipper(derivationStepZipper(zR1, shd), stl)) + + assert(matchZipper(zR1, s) == matchZipper(derivZR1, stl)) + assert(matchZipper(zR2, s) == matchZipper(derivZR2, stl)) + + assert(regexDepth(r1) < regexDepth(r)) + assert(contextDepth(Context(List(r1, r2))) < contextDepth(Context(List(r)))) + assert(regexDepth(r) == regexDepth(Concat(r1, r2))) + theoremZipperRegexEquivInductZ(zR1, List(Context(List(r1, r2))), Concat(r1, r2), s) + theoremZipperRegexEquivInductZ(zR2, List(Context(List(r2))), r2, s) + + + } else { + assert(zDeriv == derivDownZR1) + + theoremZipperRegexEquivInductZ(zR1, List(Context(List(r1, r2))), Concat(r1, r2), s) + theoremZipperRegexEquivInductZ(zR2, List(Context(List(r2))), r2, s) + } + } + } + } + case Star(rInner) => { + assert(matchR(r, s) == s.isEmpty || findConcatSeparation(rInner, Star(rInner), Nil(), s, s).isDefined) + s match { + case Nil() => { + lemmaUnfocusPreservesNullability(r, z) + assert(nullableZipper(z) == nullable(r)) + check(matchZipper(z, s) == matchR(r, s)) + } + case Cons(shd, stl) => { + val zDeriv = derivationStepZipper(z, shd) + val zDerivUp = derivationStepZipperUp(Context(List(r)), shd) + val zDerivDown = derivationStepZipperDown(r, Context(List()), shd) + assert(zDerivUp == zDerivDown) + SetUtils.lemmaFlatMapOnSingletonSet(z, hd, (c: Context[C]) => derivationStepZipperUp(c, shd)) + assert(zDeriv == zDerivDown) + + val zR1 = Set(Context(List(rInner, Star(rInner)))) + val derivZR1 = derivationStepZipper(zR1, shd) + + val derivUpZR1 = derivationStepZipperUp(Context(List(rInner, Star(rInner))), shd) + + SetUtils.lemmaFlatMapOnSingletonSet(zR1, Context(List(rInner, Star(rInner))), (c: Context[C]) => derivationStepZipperUp(c, shd)) + val derivDownZR1 = derivationStepZipperDown(rInner, Context(List(Star(rInner))), shd) + + assert(zDeriv == derivDownZR1) + assert(matchZipper(zR1, s) == matchZipper(derivationStepZipper(zR1, shd), stl)) + assert(matchZipper(zR1, s) == matchZipper(derivZR1, stl)) + + assert(regexDepth(rInner) < regexDepth(r)) + assert(contextDepth(Context(List(rInner, Star(rInner)))) == contextDepth(Context(List(r)))) + assert(regexDepth(r) + 1 == regexDepth(Concat(rInner, Star(rInner)))) + theoremZipperRegexEquivInductZ(zR1, List(Context(List(rInner, Star(rInner)))), Concat(rInner, Star(rInner)), s) + assert(matchR(Concat(rInner, Star(rInner)), s) == matchZipper(zR1, s)) + mainMatchTheorem(Concat(rInner, Star(rInner)), s) + } + } + } + } + } + case Cons(hExp, tlExp) => { + s match { + case Nil() => { + lemmaUnfocusPreservesNullability(r, z) + assert(nullableZipper(z) == nullable(r)) + check(matchZipper(z, s) == matchR(r, s)) + } + case Cons(shd, stl) => { + val zDeriv = derivationStepZipper(z, shd) + val zDerivUp = derivationStepZipperUp(Context(Cons(hExp, tlExp)), shd) + val zDerivDown = derivationStepZipperDown(hExp, Context(tlExp), shd) + val zDerivUpUp = derivationStepZipperUp(Context(tlExp), shd) + SetUtils.lemmaFlatMapOnSingletonSet(z, hd, (c: Context[C]) => derivationStepZipperUp(c, shd)) + if(nullable(hExp)){ + assert(zDeriv == zDerivDown ++ zDerivUpUp) + } else { + assert(zDeriv == zDerivDown) + } + assert(r == generalisedConcat(hd.exprs)) + assert(isConcat(r)) + r match { + case Concat(r1, r2) => { + assert(matchR(r, s) == findConcatSeparation(r1, r2, Nil(), s, s).isDefined) + assert(r1 == hExp) + assert(r2 == generalisedConcat(tlExp)) + + assert(matchZipper(z, s) == matchZipper(zDeriv, stl)) + if(nullable(r1)){ + assert(zDeriv == zDerivDown ++ zDerivUpUp) + lemmaZipperConcatMatchesSameAsBothZippers(zDerivDown, zDerivUpUp, stl) + assert((matchZipper(zDerivDown, stl) || matchZipper(zDerivUpUp, stl)) == matchZipper(zDeriv, stl)) + assert((matchZipper(zDerivDown, stl) || matchZipper(zDerivUpUp, stl)) == matchZipper(z, s)) + + assert(matchZipper(zDerivDown, stl) == matchZipper(derivationStepZipperDown(r1, Context(tlExp), shd), stl)) + + val zR1 = Set(Context(Cons(r1, tlExp))) + val zR2 = Set(Context(tlExp)) + val derivZR1 = derivationStepZipper(zR1, shd) + val derivZR2 = derivationStepZipper(zR2, shd) + + val derivUpZR1 = derivationStepZipperUp(Context(Cons(r1, tlExp)), shd) + val derivUpZR2 = derivationStepZipperUp(Context(tlExp), shd) + + SetUtils.lemmaFlatMapOnSingletonSet(zR1, Context(Cons(r1, tlExp)), (c: Context[C]) => derivationStepZipperUp(c, shd)) + SetUtils.lemmaFlatMapOnSingletonSet(zR2, Context(tlExp), (c: Context[C]) => derivationStepZipperUp(c, shd)) + + // val derivDownZR1 = derivationStepZipperDown(r1, Context(tlExp), shd) + // val derivDownZR2 = derivationStepZipperDown(r2, Context(List()), shd) + + theoremZipperRegexEquivInductZ(zR1, List(Context(Cons(r1, tlExp))), r, s) + theoremZipperRegexEquivInductZ(zR2, List(Context(tlExp)), r2, s) + + + } else { + assert(zDeriv == zDerivDown) + } + + + + + assume(matchR(r, s) == matchZipper(z, s)) + } + } + + } + } + + + } + case Nil() => { + assert(r == EmptyExpr[C]()) + lemmaZipperOfEmptyContextMatchesEmptyString(z, s) + } + } + } + case Cons(hd, tl) => { + s match { + case Nil() => { + lemmaUnfocusPreservesNullability(r, z) + assert(nullableZipper(z) == nullable(r)) + check(matchZipper(z, s) == matchR(r, s)) + } + case Cons(shd, stl) => assume(matchR(r, s) == matchZipper(z, s)) + } + } + case Nil() => { + assert(isEmptyLang(r)) + lemmaEmptyZipperMatchesNothing(z, s) + } + } + + }.ensuring(_ => matchR(r, s) == matchZipper(z, s)) @ghost @@ -465,7 +797,7 @@ object ZipperRegex { } } - }.ensuring(_ => matchZipper(z1 ++ z2, s) == matchZipper(z1, s) || matchZipper(z2, s)) + }.ensuring(_ => matchZipper(z1 ++ z2, s) == (matchZipper(z1, s) || matchZipper(z2, s))) // LEMMAS ----------------------------------------------------------------------------------------------------- @@ -475,10 +807,10 @@ object ZipperRegex { @inlineOnce def lemmaUnfocusPreservesNullability[C](r: Regex[C], z: Zipper[C]): Unit = { require(validRegex(r)) - require(r == unfocusZipper(z)) + require(r == unfocusZipper(z.toList)) decreases(regexDepth(r)) - val reg = VerifiedRegex.generalisedUnion(unfocusZipperList(z.toList)) + val reg = generalisedUnion(unfocusZipperList(z.toList)) assert(r == reg) nullableGenUnionSpec(reg, unfocusZipperList(z.toList)) assert(nullable(reg) == unfocusZipperList(z.toList).exists(rr => nullable(rr))) @@ -488,11 +820,11 @@ object ZipperRegex { assert(nullable(witnessNullableReg)) assert(unfocusZipperList(z.toList).contains(witnessNullableReg)) lemmaUnfocusZipperListContainsRegexFromContextThenZipperContains(z.toList, witnessNullableReg) - assert(z.exists(c => VerifiedRegex.generalisedConcat(c.exprs) == witnessNullableReg)) - val witnessContext = SetUtils.getWitness(z, (c: Context[C]) => VerifiedRegex.generalisedConcat(c.exprs) == witnessNullableReg) + assert(z.exists(c => generalisedConcat(c.exprs) == witnessNullableReg)) + val witnessContext = SetUtils.getWitness(z, (c: Context[C]) => generalisedConcat(c.exprs) == witnessNullableReg) assert(z.contains(witnessContext)) - assert(VerifiedRegex.generalisedConcat(witnessContext.exprs) == witnessNullableReg) - assert(nullable(VerifiedRegex.generalisedConcat(witnessContext.exprs))) + assert(generalisedConcat(witnessContext.exprs) == witnessNullableReg) + assert(nullable(generalisedConcat(witnessContext.exprs))) nullableGenConcatSpec(witnessNullableReg, witnessContext.exprs) assert(nullableContext(witnessContext)) SetUtils.lemmaContainsThenExists(z, witnessContext, a => nullableContext(a)) @@ -504,10 +836,10 @@ object ZipperRegex { assert(z.contains(witnessContext)) assert(nullableContext(witnessContext)) lemmaZipperContainsContextThenUnfocusZipperListContains(z.toList, witnessContext) - assert(unfocusZipperList(z.toList).contains(VerifiedRegex.generalisedConcat(witnessContext.exprs))) - nullableGenConcatSpec(VerifiedRegex.generalisedConcat(witnessContext.exprs), witnessContext.exprs) - assert(nullable(VerifiedRegex.generalisedConcat(witnessContext.exprs))) - ListUtils.lemmaContainsThenExists(unfocusZipperList(z.toList), VerifiedRegex.generalisedConcat(witnessContext.exprs), rr => nullable(rr)) + assert(unfocusZipperList(z.toList).contains(generalisedConcat(witnessContext.exprs))) + nullableGenConcatSpec(generalisedConcat(witnessContext.exprs), witnessContext.exprs) + assert(nullable(generalisedConcat(witnessContext.exprs))) + ListUtils.lemmaContainsThenExists(unfocusZipperList(z.toList), generalisedConcat(witnessContext.exprs), rr => nullable(rr)) assert(unfocusZipperList(z.toList).exists(rr => nullable(rr))) check(false) } @@ -701,7 +1033,7 @@ object ZipperRegex { @opaque @inlineOnce def lemmaZipperOfEmptyExprMatchesOnlyEmptyString[C](z: Zipper[C], s: List[C]): Unit = { - require(unfocusZipper(z) == EmptyExpr[C]()) + require(unfocusZipper(z.toList) == EmptyExpr[C]()) if(z == focus(EmptyExpr[C]())){ check(nullableContext(Context(List(EmptyExpr[C]())))) val c = Context(List(EmptyExpr[C]())) diff --git a/lexers/regex/verifiedlexer/stainless.conf b/lexers/regex/verifiedlexer/stainless.conf index 8f1a2aa7..960e9600 100644 --- a/lexers/regex/verifiedlexer/stainless.conf +++ b/lexers/regex/verifiedlexer/stainless.conf @@ -3,7 +3,7 @@ vc-cache = true # debug = ["verification", "smt"] -timeout = 10 +timeout = 20 check-models = false print-ids = false print-types = false From 2e5f53c03f68747960a31f87f5210aa32f15a654 Mon Sep 17 00:00:00 2001 From: Samuel Chassot Date: Tue, 19 Nov 2024 17:51:54 +0100 Subject: [PATCH 52/78] working on zipper proof --- .../scala/ch/epfl/lexer/VerifiedRegex.scala | 625 +++++++++++++++--- 1 file changed, 515 insertions(+), 110 deletions(-) diff --git a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedRegex.scala b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedRegex.scala index 6bb97156..a195866f 100644 --- a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedRegex.scala +++ b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedRegex.scala @@ -158,6 +158,19 @@ object VerifiedRegex { }) ) + @ghost + def regexDepthTotal[C](r: Regex[C]): BigInt = { + decreases(r) + r match { + case ElementMatch(c) => BigInt(1) + case Star(r) => BigInt(1) + regexDepthTotal(r) + case Union(rOne, rTwo) => BigInt(1) + regexDepthTotal(rOne) + regexDepthTotal(rTwo) + case Concat(rOne, rTwo) => BigInt(1) + regexDepthTotal(rOne) + regexDepthTotal(rTwo) + case EmptyExpr() => BigInt(1) + case EmptyLang() => BigInt(1) + } + }.ensuring (res => res > 0) + def usedCharacters[C](r: Regex[C]): List[C] = { r match { case EmptyExpr() => Nil[C]() @@ -328,6 +341,17 @@ object ZipperRegex { } }.ensuring(res => res >= 0 && c.exprs.forall(r => res >= regexDepth(r))) + @ghost + @pure + def contextDepthTotal[C](c: Context[C]): BigInt = { + decreases(c.exprs.size) + c.exprs match { + case Cons(hd, tl) => + regexDepthTotal(hd) + contextDepthTotal(Context(tl)) + case Nil() => BigInt(0) + } + }.ensuring(res => res >= 0) + @ghost @pure def zipperDepth[C](zl: List[Context[C]]): BigInt = { @@ -341,6 +365,17 @@ object ZipperRegex { } }.ensuring(res => res >= 0 && zl.forall(c => res >= contextDepth(c))) + @ghost + @pure + def zipperDepthTotal[C](zl: List[Context[C]]): BigInt = { + decreases(zl.size) + zl match { + case Cons(hd, tl) => + contextDepthTotal(hd) + zipperDepthTotal(tl) + case Nil() => BigInt(0) + } + }.ensuring(res => res >= 0) + def derivationStepZipperUp[C](context: Context[C], a: C): Zipper[C] = { decreases(context.exprs.size) @@ -501,15 +536,174 @@ object ZipperRegex { require(validRegex(r)) require(z.toList == zl) require(r == unfocusZipper(zl)) - decreases(regexDepth(r) + zipperDepth(zl) + (if zl.isEmpty || zl.head.isEmpty then BigInt(0) else regexDepth(zl.head.exprs.head)) * 2) // Pretty insane but so beautiful haha + decreases( + // regexDepth(r) + zipperDepth(zl) * 2 + + zipperDepthTotal(zl) + // + (if zl.isEmpty || zl.head.isEmpty then BigInt(0) else zl.head.exprs.size) + + zl.size + + s.size + // + (if zl.isEmpty || zl.head.isEmpty then BigInt(0) else regexDepth(zl.head.exprs.head)) + ) // Pretty insane but so beautiful haha mainMatchTheorem(r, s) zl match { case Cons(hd, tl) if tl.isEmpty => { assert(r == generalisedConcat(hd.exprs)) hd.exprs match { - case Cons(hExp, tlExp) if tlExp.isEmpty => { - assert(r == hExp) + // case Cons(hExp, tlExp) if tlExp.isEmpty => { + // assert(r == hExp) + // r match { + // case EmptyExpr() => lemmaZipperOfEmptyExprMatchesOnlyEmptyString(z, s) + // case EmptyLang() => lemmaZipperStartingWithEmptyLangMatchesNothing(z, Context(List(r)), s) + // case ElementMatch(a) => lemmaElementMatchZipperAcceptsOnlyThisChar(z, Context(List(ElementMatch(a))), a, s) + // case Union(r1, r2) => { + // mainMatchTheorem(r1, s) + // mainMatchTheorem(r2, s) + // assert(matchR(r, s) == (matchR(r1, s) || matchR(r2, s))) + // s match { + // case Nil() => { + // lemmaUnfocusPreservesNullability(r, z) + // assert(nullableZipper(z) == nullable(r)) + // check(matchZipper(z, s) == matchR(r, s)) + // } + // case Cons(shd, stl) => { + // val deriv = derivationStepZipper(z, shd) + // val derivUp = derivationStepZipperUp(Context(List(r)), shd) + // val derivDown = derivationStepZipperDown(r, Context(List()), shd) + // assert(derivUp == derivDown) + // assert(derivDown == derivationStepZipperDown(r1, Context(List()), shd) ++ derivationStepZipperDown(r2, Context(List()), shd)) + // val z1 = derivationStepZipperDown(r1, Context(List()), shd) + // val z2 = derivationStepZipperDown(r2, Context(List()), shd) + // SetUtils.lemmaFlatMapOnSingletonSet(z, hd, (c: Context[C]) => derivationStepZipperUp(c, shd)) + // assert(deriv == z1 ++ z2) + // assert(matchZipper(z1 ++ z2, stl) == matchZipper(z, s)) + // lemmaZipperConcatMatchesSameAsBothZippers(z1, z2, stl) + // assert((matchZipper(z1, stl) || matchZipper(z2, stl)) == matchZipper(z1 ++ z2, stl)) + // assert((matchZipper(z1, stl) || matchZipper(z2, stl)) == matchZipper(z, s)) + + // val zR1 = Set(Context(List(r1))) + // val zR2 = Set(Context(List(r2))) + // val derivZR1 = derivationStepZipper(zR1, shd) + // val derivZR2 = derivationStepZipper(zR2, shd) + + // val derivUpZR1 = derivationStepZipperUp(Context(List(r1)), shd) + // val derivUpZR2 = derivationStepZipperUp(Context(List(r2)), shd) + // SetUtils.lemmaFlatMapOnSingletonSet(zR1, Context(List(r1)), (c: Context[C]) => derivationStepZipperUp(c, shd)) + // SetUtils.lemmaFlatMapOnSingletonSet(zR2, Context(List(r2)), (c: Context[C]) => derivationStepZipperUp(c, shd)) + + // val zR1Deriv = derivationStepZipperDown(r1, Context(List()), shd) + // val zR2Deriv = derivationStepZipperDown(r2, Context(List()), shd) + // assert(zR1Deriv == z1) + // assert(zR2Deriv == z2) + // assert(matchZipper(zR1, s) == matchZipper(zR1Deriv, stl)) + // assert(matchZipper(zR2, s) == matchZipper(zR2Deriv, stl)) + // assert((matchZipper(zR1, s) || matchZipper(zR2, s)) == matchZipper(z, s)) + // theoremZipperRegexEquivInductZ(zR1, List(Context(List(r1))), r1, s) + // theoremZipperRegexEquivInductZ(zR2, List(Context(List(r2))), r2, s) + // } + // } + // } + // case Concat(r1, r2) => { + // assert(matchR(r, s) == findConcatSeparation(r1, r2, Nil(), s, s).isDefined) + // s match { + // case Nil() => { + // lemmaUnfocusPreservesNullability(r, z) + // assert(nullableZipper(z) == nullable(r)) + // check(matchZipper(z, s) == matchR(r, s)) + // } + // case Cons(shd, stl) => { + // val zDeriv = derivationStepZipper(z, shd) + // val zDerivUp = derivationStepZipperUp(Context(List(r)), shd) + // val zDerivDown = derivationStepZipperDown(r, Context(List()), shd) + // assert(zDerivUp == zDerivDown) + // SetUtils.lemmaFlatMapOnSingletonSet(z, hd, (c: Context[C]) => derivationStepZipperUp(c, shd)) + // assert(zDeriv == zDerivDown) + // val zR1 = Set(Context(List(r1, r2))) + // val zR2 = Set(Context(List(r2))) + // val derivZR1 = derivationStepZipper(zR1, shd) + // val derivZR2 = derivationStepZipper(zR2, shd) + + // val derivUpZR1 = derivationStepZipperUp(Context(List(r1, r2)), shd) + // val derivUpZR2 = derivationStepZipperUp(Context(List(r2)), shd) + + // SetUtils.lemmaFlatMapOnSingletonSet(zR1, Context(List(r1, r2)), (c: Context[C]) => derivationStepZipperUp(c, shd)) + // SetUtils.lemmaFlatMapOnSingletonSet(zR2, Context(List(r2)), (c: Context[C]) => derivationStepZipperUp(c, shd)) + + // val derivDownZR1 = derivationStepZipperDown(r1, Context(List(r2)), shd) + // val derivDownZR2 = derivationStepZipperDown(r2, Context(List()), shd) + + // if(nullable(r1)){ + // assert(zDeriv == derivDownZR1 ++ derivDownZR2) + // lemmaZipperConcatMatchesSameAsBothZippers(derivDownZR1, derivDownZR2, stl) + // assert((matchZipper(derivDownZR1, stl) || matchZipper(derivDownZR2, stl)) == matchZipper(zDeriv, stl)) + + // assert(derivUpZR1 == derivDownZR1 ++ derivUpZR2) + // assert(derivUpZR1 == derivDownZR1 ++ derivDownZR2) + // assert(derivUpZR1 == zDeriv) + + // assert(matchZipper(zR1, s) == matchZipper(derivationStepZipper(zR1, shd), stl)) + + // assert(matchZipper(zR1, s) == matchZipper(derivZR1, stl)) + // assert(matchZipper(zR2, s) == matchZipper(derivZR2, stl)) + + // assert(regexDepth(r1) < regexDepth(r)) + // assert(contextDepth(Context(List(r1, r2))) < contextDepth(Context(List(r)))) + // assert(regexDepth(r) == regexDepth(Concat(r1, r2))) + // theoremZipperRegexEquivInductZ(zR1, List(Context(List(r1, r2))), Concat(r1, r2), s) + // theoremZipperRegexEquivInductZ(zR2, List(Context(List(r2))), r2, s) + + + // } else { + // assert(zDeriv == derivDownZR1) + + // theoremZipperRegexEquivInductZ(zR1, List(Context(List(r1, r2))), Concat(r1, r2), s) + // theoremZipperRegexEquivInductZ(zR2, List(Context(List(r2))), r2, s) + // } + // } + // } + // } + // case Star(rInner) => { + // assert(matchR(r, s) == s.isEmpty || findConcatSeparation(rInner, Star(rInner), Nil(), s, s).isDefined) + // s match { + // case Nil() => { + // lemmaUnfocusPreservesNullability(r, z) + // assert(nullableZipper(z) == nullable(r)) + // check(matchZipper(z, s) == matchR(r, s)) + // } + // case Cons(shd, stl) => { + // val zDeriv = derivationStepZipper(z, shd) + // val zDerivUp = derivationStepZipperUp(Context(List(r)), shd) + // val zDerivDown = derivationStepZipperDown(r, Context(List()), shd) + // assert(zDerivUp == zDerivDown) + // SetUtils.lemmaFlatMapOnSingletonSet(z, hd, (c: Context[C]) => derivationStepZipperUp(c, shd)) + // assert(zDeriv == zDerivDown) + + // val zR1 = Set(Context(List(rInner, Star(rInner)))) + // val derivZR1 = derivationStepZipper(zR1, shd) + + // val derivUpZR1 = derivationStepZipperUp(Context(List(rInner, Star(rInner))), shd) + + // SetUtils.lemmaFlatMapOnSingletonSet(zR1, Context(List(rInner, Star(rInner))), (c: Context[C]) => derivationStepZipperUp(c, shd)) + // val derivDownZR1 = derivationStepZipperDown(rInner, Context(List(Star(rInner))), shd) + + // assert(zDeriv == derivDownZR1) + // assert(matchZipper(zR1, s) == matchZipper(derivationStepZipper(zR1, shd), stl)) + // assert(matchZipper(zR1, s) == matchZipper(derivZR1, stl)) + + // assert(regexDepth(rInner) < regexDepth(r)) + // assert(contextDepth(Context(List(rInner, Star(rInner)))) == contextDepth(Context(List(r)))) + // assert(regexDepth(r) + 1 == regexDepth(Concat(rInner, Star(rInner)))) + // theoremZipperRegexEquivInductZ(zR1, List(Context(List(rInner, Star(rInner)))), Concat(rInner, Star(rInner)), s) + // assert(matchR(Concat(rInner, Star(rInner)), s) == matchZipper(zR1, s)) + // mainMatchTheorem(Concat(rInner, Star(rInner)), s) + // } + // } + // } + // } + // } + case Cons(hExp, tlExp) => { + assert(r == generalisedUnion(unfocusZipperList(zl))) r match { case EmptyExpr() => lemmaZipperOfEmptyExprMatchesOnlyEmptyString(z, s) case EmptyLang() => lemmaZipperStartingWithEmptyLangMatchesNothing(z, Context(List(r)), s) @@ -570,53 +764,273 @@ object ZipperRegex { check(matchZipper(z, s) == matchR(r, s)) } case Cons(shd, stl) => { - val zDeriv = derivationStepZipper(z, shd) - val zDerivUp = derivationStepZipperUp(Context(List(r)), shd) - val zDerivDown = derivationStepZipperDown(r, Context(List()), shd) - assert(zDerivUp == zDerivDown) - SetUtils.lemmaFlatMapOnSingletonSet(z, hd, (c: Context[C]) => derivationStepZipperUp(c, shd)) - assert(zDeriv == zDerivDown) - val zR1 = Set(Context(List(r1, r2))) - val zR2 = Set(Context(List(r2))) - val derivZR1 = derivationStepZipper(zR1, shd) - val derivZR2 = derivationStepZipper(zR2, shd) + if(tlExp.isEmpty){ + // Here, we are in the case where the Concat is a Regex from the Zipper (the only one in its context) + val zDeriv = derivationStepZipper(z, shd) + val zDerivUp = derivationStepZipperUp(Context(List(r)), shd) + val zDerivDown = derivationStepZipperDown(r, Context(List()), shd) + assert(zDerivUp == zDerivDown) + SetUtils.lemmaFlatMapOnSingletonSet(z, hd, (c: Context[C]) => derivationStepZipperUp(c, shd)) + assert(zDeriv == zDerivDown) + val zR1 = Set(Context(List(r1, r2))) + val zR2 = Set(Context(List(r2))) + val derivZR1 = derivationStepZipper(zR1, shd) + val derivZR2 = derivationStepZipper(zR2, shd) + + val derivUpZR1 = derivationStepZipperUp(Context(List(r1, r2)), shd) + val derivUpZR2 = derivationStepZipperUp(Context(List(r2)), shd) + + SetUtils.lemmaFlatMapOnSingletonSet(zR1, Context(List(r1, r2)), (c: Context[C]) => derivationStepZipperUp(c, shd)) + SetUtils.lemmaFlatMapOnSingletonSet(zR2, Context(List(r2)), (c: Context[C]) => derivationStepZipperUp(c, shd)) + + val derivDownZR1 = derivationStepZipperDown(r1, Context(List(r2)), shd) + val derivDownZR2 = derivationStepZipperDown(r2, Context(List()), shd) - val derivUpZR1 = derivationStepZipperUp(Context(List(r1, r2)), shd) - val derivUpZR2 = derivationStepZipperUp(Context(List(r2)), shd) + if(nullable(r1)){ + assert(zDeriv == derivDownZR1 ++ derivDownZR2) + lemmaZipperConcatMatchesSameAsBothZippers(derivDownZR1, derivDownZR2, stl) + assert((matchZipper(derivDownZR1, stl) || matchZipper(derivDownZR2, stl)) == matchZipper(zDeriv, stl)) - SetUtils.lemmaFlatMapOnSingletonSet(zR1, Context(List(r1, r2)), (c: Context[C]) => derivationStepZipperUp(c, shd)) - SetUtils.lemmaFlatMapOnSingletonSet(zR2, Context(List(r2)), (c: Context[C]) => derivationStepZipperUp(c, shd)) + assert(derivUpZR1 == derivDownZR1 ++ derivUpZR2) + assert(derivUpZR1 == derivDownZR1 ++ derivDownZR2) + assert(derivUpZR1 == zDeriv) + + assert(matchZipper(zR1, s) == matchZipper(derivationStepZipper(zR1, shd), stl)) + + assert(matchZipper(zR1, s) == matchZipper(derivZR1, stl)) + assert(matchZipper(zR2, s) == matchZipper(derivZR2, stl)) - val derivDownZR1 = derivationStepZipperDown(r1, Context(List(r2)), shd) - val derivDownZR2 = derivationStepZipperDown(r2, Context(List()), shd) - if(nullable(r1)){ - assert(zDeriv == derivDownZR1 ++ derivDownZR2) - lemmaZipperConcatMatchesSameAsBothZippers(derivDownZR1, derivDownZR2, stl) - assert((matchZipper(derivDownZR1, stl) || matchZipper(derivDownZR2, stl)) == matchZipper(zDeriv, stl)) + assert(contextDepth(Context(List(r1, r2))) < contextDepth(Context(List(r)))) + assert(zipperDepth(List(Context(List(r1, r2)))) < zipperDepth(List(Context(List(r))))) + + assert(contextDepth(Context(List(r2))) < contextDepth(Context(List(r)))) + assert(zipperDepth(List(Context(List(r2)))) < zipperDepth(List(Context(List(r))))) + assert(regexDepth(r) == regexDepth(Concat(r1, r2))) + theoremZipperRegexEquivInductZ(zR1, List(Context(List(r1, r2))), Concat(r1, r2), s) + theoremZipperRegexEquivInductZ(zR2, List(Context(List(r2))), r2, s) + - assert(derivUpZR1 == derivDownZR1 ++ derivUpZR2) - assert(derivUpZR1 == derivDownZR1 ++ derivDownZR2) - assert(derivUpZR1 == zDeriv) + } else { + assert(zDeriv == derivDownZR1) + assert(unfocusZipper(zl) == r) + assert(r == generalisedConcat(hd.exprs)) + assert(hd.exprs == List(r)) + assert(zipperDepth(List(Context(List(r1, r2)))) < zipperDepth(List(Context(List(r))))) // Measure + assert(zipperDepth(List(Context(List(r1, r2)))) < zipperDepth(List(Context(List(r))))) // Measure + theoremZipperRegexEquivInductZ(zR1, List(Context(List(r1, r2))), Concat(r1, r2), s) + theoremZipperRegexEquivInductZ(zR2, List(Context(List(r2))), r2, s) + } + } else + // Here, we are in the case where the Concat is the result of generalisedConcat + val zDeriv = derivationStepZipper(z, shd) + val zDerivUp = derivationStepZipperUp(Context(Cons(hExp, tlExp)), shd) + val zDerivDown = derivationStepZipperDown(hExp, Context(tlExp), shd) + val zDerivUpUp = derivationStepZipperUp(Context(tlExp), shd) + + SetUtils.lemmaFlatMapOnSingletonSet(z, hd, (c: Context[C]) => derivationStepZipperUp(c, shd)) + + if(nullable(hExp)){ + assert(zDerivUp == zDerivDown ++ zDerivUpUp) + assert(zDeriv == zDerivUp) + lemmaZipperConcatMatchesSameAsBothZippers(zDerivDown, zDerivUpUp, stl) + assert(matchZipper(zDeriv, stl) == matchZipper(z, s)) + assert(matchZipper(z, s) == (matchZipper(zDerivDown, stl) || matchZipper(zDerivUpUp, stl))) - assert(matchZipper(zR1, s) == matchZipper(derivationStepZipper(zR1, shd), stl)) + } else { + assert(zDerivUp == zDerivDown) + assert(zDeriv == zDerivDown) + assert(matchZipper(zDerivDown, stl) == matchZipper(z, s)) + } - assert(matchZipper(zR1, s) == matchZipper(derivZR1, stl)) - assert(matchZipper(zR2, s) == matchZipper(derivZR2, stl)) + r1 match { + case ElementMatch(c) if c == shd => { + assert(zDerivDown == Set(Context(tlExp))) + val zVirt = Set(Context(tlExp)) + theoremZipperRegexEquivInductZ(zVirt, List(Context(tlExp)), generalisedConcat(tlExp), stl) + assert(matchR(r, s) == matchZipper(z, s)) + } + case Union(rOne, rTwo) => { + assert(zDerivDown == derivationStepZipperDown(rOne, Context(tlExp), shd) ++ derivationStepZipperDown(rTwo, Context(tlExp), shd)) + val zDerivDown1 = derivationStepZipperDown(rOne, Context(tlExp), shd) + val zDerivDown2 = derivationStepZipperDown(rTwo, Context(tlExp), shd) + lemmaZipperConcatMatchesSameAsBothZippers(zDerivDown1, zDerivDown2, stl) + assert(matchZipper(zDerivDown, stl) == matchZipper(zDerivDown1, stl) || matchZipper(zDerivDown2, stl)) + val zVirt1 = Set(Context(Cons(rOne, tlExp))) + val zVirt2 = Set(Context(Cons(rTwo, tlExp))) + + val zVirt1Deriv = derivationStepZipper(zVirt1, shd) + val zVirt1DerivUp = derivationStepZipperUp(Context(Cons(rOne, tlExp)), shd) + SetUtils.lemmaFlatMapOnSingletonSet(zVirt1, Context(Cons(rOne, tlExp)), (c: Context[C]) => derivationStepZipperUp(c, shd)) + if (nullable(rOne)){ + assert(zVirt1DerivUp == derivationStepZipperDown(rOne, Context(tlExp), shd) ++ derivationStepZipperUp(Context(tlExp), shd)) + assert(zVirt1Deriv == derivationStepZipperDown(rOne, Context(tlExp), shd) ++ derivationStepZipperUp(Context(tlExp), shd)) + assert(matchZipper(zVirt1, s) == matchZipper(zVirt1Deriv, stl)) + lemmaZipperConcatMatchesSameAsBothZippers(derivationStepZipperDown(rOne, Context(tlExp), shd), derivationStepZipperUp(Context(tlExp), shd), stl) + assert(matchZipper(zVirt1, s) == (matchZipper(zDerivDown1, stl) || matchZipper(zDerivUpUp, stl))) + } else { + assert(zVirt1DerivUp == derivationStepZipperDown(rOne, Context(tlExp), shd)) + assert(matchZipper(zVirt1, s) == (matchZipper(zDerivDown1, stl))) + } + + val zVirt2Deriv = derivationStepZipper(zVirt2, shd) + val zVirt2DerivUp = derivationStepZipperUp(Context(Cons(rTwo, tlExp)), shd) + SetUtils.lemmaFlatMapOnSingletonSet(zVirt2, Context(Cons(rTwo, tlExp)), (c: Context[C]) => derivationStepZipperUp(c, shd)) + if (nullable(rTwo)){ + assert(zVirt2DerivUp == derivationStepZipperDown(rTwo, Context(tlExp), shd) ++ derivationStepZipperUp(Context(tlExp), shd)) + assert(zVirt2Deriv == derivationStepZipperDown(rTwo, Context(tlExp), shd) ++ derivationStepZipperUp(Context(tlExp), shd)) + assert(matchZipper(zVirt2, s) == matchZipper(zVirt2Deriv, stl)) + lemmaZipperConcatMatchesSameAsBothZippers(derivationStepZipperDown(rTwo, Context(tlExp), shd), derivationStepZipperUp(Context(tlExp), shd), stl) + assert(matchZipper(zVirt2, s) == (matchZipper(zDerivDown2, stl) || matchZipper(zDerivUpUp, stl))) + } else { + assert(zVirt2DerivUp == derivationStepZipperDown(rTwo, Context(tlExp), shd)) + assert(matchZipper(zVirt2, s) == (matchZipper(zDerivDown2, stl))) + } + if(nullable(r1)){ + // This one is really beautiful, as the matching of derivUpUp appears in the derivative of one of the 2 virtual zippers + // if they are nullable, but the same term appears before if r1 is nullable, so it cancels out and does not break + // anything + assert(nullable(rOne) || nullable(rTwo)) + assert(matchZipper(z, s) == (matchZipper(zDerivDown1, stl) || matchZipper(zDerivDown2, stl) || matchZipper(zDerivUpUp, stl))) + } else { + assert(!nullable(rOne) && !nullable(rTwo)) + assert(matchZipper(z, s) == (matchZipper(zDerivDown1, stl) || matchZipper(zDerivDown2, stl))) + } + assert(matchZipper(z, s) == (matchZipper(zVirt1, s) || matchZipper(zVirt2, s))) + + assert(unfocusZipper(zl) == r) + assert(r == generalisedConcat(hd.exprs)) + assert(hd.exprs == Cons(r1, tlExp)) + + assert(zipperDepthTotal(List(Context(Cons(rOne, tlExp)))) < zipperDepthTotal(zl)) // Measure decreases + assert(zipperDepthTotal(List(Context(Cons(rTwo, tlExp)))) < zipperDepthTotal(zl)) // Measure decreases + theoremZipperRegexEquivInductZ(zVirt1, List(Context(Cons(rOne, tlExp))), generalisedConcat(Cons(rOne, tlExp)), s) + theoremZipperRegexEquivInductZ(zVirt2, List(Context(Cons(rTwo, tlExp))), generalisedConcat(Cons(rTwo, tlExp)), s) + } + case Concat(rOne, rTwo) if nullable(rOne) => { + assert(zDerivDown == derivationStepZipperDown(rOne, Context(Cons(rTwo, tlExp)), shd) ++ derivationStepZipperDown(rTwo, Context(tlExp), shd)) + val zDerivDown1 = derivationStepZipperDown(rOne, Context(Cons(rTwo, tlExp)), shd) + val zDerivDown2 = derivationStepZipperDown(rTwo, Context(tlExp), shd) + lemmaZipperConcatMatchesSameAsBothZippers(zDerivDown1, zDerivDown2, stl) + assert(matchZipper(zDerivDown, stl) == matchZipper(zDerivDown1, stl) || matchZipper(zDerivDown2, stl)) + val zVirt1 = Set(Context(Cons(rOne, Cons(rTwo, tlExp)))) + val zVirt2 = Set(Context(Cons(rTwo, tlExp))) + + val zVirt1Deriv = derivationStepZipper(zVirt1, shd) + val zVirt1DerivUp = derivationStepZipperUp(Context(Cons(rOne, Cons(rTwo, tlExp))), shd) + SetUtils.lemmaFlatMapOnSingletonSet(zVirt1, Context(Cons(rOne, Cons(rTwo, tlExp))), (c: Context[C]) => derivationStepZipperUp(c, shd)) + assert(zVirt1Deriv == derivationStepZipperDown(rOne, Context(Cons(rTwo, tlExp)), shd) ++ derivationStepZipperUp(Context(Cons(rTwo, tlExp)), shd)) + + val zVirt2Deriv = derivationStepZipper(zVirt2, shd) + val zVirt2DerivUp = derivationStepZipperUp(Context(Cons(rTwo, tlExp)), shd) + SetUtils.lemmaFlatMapOnSingletonSet(zVirt2, Context(Cons(rTwo, tlExp)), (c: Context[C]) => derivationStepZipperUp(c, shd)) + + if(nullable(rTwo)){ + assert(zVirt2Deriv == derivationStepZipperDown(rTwo, Context(tlExp), shd) ++ derivationStepZipperUp(Context(tlExp), shd)) + } else { + assert(zVirt2Deriv == derivationStepZipperDown(rTwo, Context(tlExp), shd)) + } + + lemmaZipperConcatMatchesSameAsBothZippers(derivationStepZipperDown(rOne, Context(Cons(rTwo, tlExp)), shd), derivationStepZipperUp(Context(Cons(rTwo, tlExp)), shd), stl) + assert(matchZipper(zDerivDown, stl) == matchZipper(zVirt1Deriv, stl) || matchZipper(zVirt2Deriv, stl)) + assert(hd.exprs == Cons(Concat(rOne, rTwo), tlExp)) + assert(contextDepthTotal(Context(Cons(rOne, Cons(rTwo, tlExp)))) < contextDepthTotal(Context(hd.exprs))) + assert(zipperDepthTotal(List(Context(Cons(rOne, Cons(rTwo, tlExp))))) < zipperDepthTotal(zl)) // Measure decreases + assert(zipperDepth(List(Context(Cons(rOne, Cons(rTwo, tlExp))))) <= zipperDepth(zl)) // Measure decreases + theoremZipperRegexEquivInductZ(zVirt1, List(Context(Cons(rOne, Cons(rTwo, tlExp)))), generalisedConcat(Cons(rOne, Cons(rTwo, tlExp))), s) + theoremZipperRegexEquivInductZ(zVirt2, List(Context(Cons(rTwo, tlExp))), generalisedConcat(Cons(rTwo, tlExp)), s) + + mainMatchTheorem(generalisedConcat(Cons(rOne, Cons(rTwo, tlExp))), s) + mainMatchTheorem(generalisedConcat(Cons(rTwo, tlExp)), s) + + assert(matchZipper(z, s) == (matchZipper(zVirt1, s) || matchZipper(zVirt2, s))) + assert(matchZipper(z, s) == (matchR(generalisedConcat(Cons(rOne, Cons(rTwo, tlExp))), s) || matchR(generalisedConcat(Cons(rTwo, tlExp)), s))) + + assert(matchR(r, s) == findConcatSeparation(r1, r2, Nil(), s, s).isDefined) + assert(matchR(r, s) == matchR(Concat(r1, r2), s)) + assert(matchR(r, s) == matchR(Concat(Concat(rOne, rTwo), r2), s)) + assert(matchR(r, s) == matchR(Concat(Concat(rOne, rTwo), generalisedConcat(tlExp)), s)) + + lemmaConcatAssociative(rOne, rTwo, generalisedConcat(tlExp), s) + assert(matchR(Concat(Concat(rOne, rTwo), generalisedConcat(tlExp)), s) == matchR(Concat(rOne, Concat(rTwo, generalisedConcat(tlExp))), s)) // TODO + + assert(matchR(r, s) == matchZipper(z, s)) + } + case Concat(rOne, rTwo) => { + assert(zDerivDown == derivationStepZipperDown(rOne, Context(Cons(rTwo, tlExp)), shd)) + val zDerivDown1 = derivationStepZipperDown(rOne, Context(Cons(rTwo, tlExp)), shd) + assert(matchZipper(zDerivDown, stl) == matchZipper(zDerivDown1, stl)) + val zVirt1 = Set(Context(Cons(rOne, Cons(rTwo, tlExp)))) + + val zVirt1Deriv = derivationStepZipper(zVirt1, shd) + val zVirt1DerivUp = derivationStepZipperUp(Context(Cons(rOne, Cons(rTwo, tlExp))), shd) + SetUtils.lemmaFlatMapOnSingletonSet(zVirt1, Context(Cons(rOne, Cons(rTwo, tlExp))), (c: Context[C]) => derivationStepZipperUp(c, shd)) + assert(zVirt1Deriv == derivationStepZipperDown(rOne, Context(Cons(rTwo, tlExp)), shd)) + + assert(matchZipper(zDerivDown, stl) == matchZipper(zVirt1Deriv, stl)) + assert(hd.exprs == Cons(Concat(rOne, rTwo), tlExp)) + assert(contextDepthTotal(Context(Cons(rOne, Cons(rTwo, tlExp)))) < contextDepthTotal(Context(hd.exprs))) + assert(zipperDepthTotal(List(Context(Cons(rOne, Cons(rTwo, tlExp))))) < zipperDepthTotal(zl)) // Measure decreases + assert(zipperDepth(List(Context(Cons(rOne, Cons(rTwo, tlExp))))) <= zipperDepth(zl)) // Measure decreases + theoremZipperRegexEquivInductZ(zVirt1, List(Context(Cons(rOne, Cons(rTwo, tlExp)))), generalisedConcat(Cons(rOne, Cons(rTwo, tlExp))), s) + + mainMatchTheorem(generalisedConcat(Cons(rOne, Cons(rTwo, tlExp))), s) + mainMatchTheorem(generalisedConcat(Cons(rTwo, tlExp)), s) + + assert(matchZipper(z, s) == (matchZipper(zVirt1, s))) + assert(matchZipper(z, s) == (matchR(generalisedConcat(Cons(rOne, Cons(rTwo, tlExp))), s))) + + assert(matchR(r, s) == findConcatSeparation(r1, r2, Nil(), s, s).isDefined) + assert(matchR(r, s) == matchR(Concat(r1, r2), s)) + assert(matchR(r, s) == matchR(Concat(Concat(rOne, rTwo), r2), s)) + assert(matchR(r, s) == matchR(Concat(Concat(rOne, rTwo), generalisedConcat(tlExp)), s)) + + lemmaConcatAssociative(rOne, rTwo, generalisedConcat(tlExp), s) + assert(matchR(Concat(Concat(rOne, rTwo), generalisedConcat(tlExp)), s) == matchR(Concat(rOne, Concat(rTwo, generalisedConcat(tlExp))), s)) // TODO + + assert(matchR(r, s) == matchZipper(z, s)) + } + case Star(rInner) => { + assert(zDerivDown == derivationStepZipperDown(rInner, Context(Cons(Star(rInner), tlExp)), shd)) + + val zVirt = Set(Context(Cons(Star(rInner), tlExp))) + val zVirtDeriv = derivationStepZipper(zVirt, shd) + val zVirtDerivUp = derivationStepZipperUp(Context(Cons(Star(rInner), tlExp)), shd) + SetUtils.lemmaFlatMapOnSingletonSet(zVirt, Context(Cons(Star(rInner), tlExp)), (c: Context[C]) => derivationStepZipperUp(c, shd)) + assert(zVirtDeriv == derivationStepZipperDown(rInner, Context(Cons(Star(rInner), tlExp)), shd) ++ derivationStepZipperUp(Context(tlExp), shd)) + + assert(matchZipper(zVirt, s) == matchZipper(zVirtDeriv, stl)) + + + //TODO measures + theoremZipperRegexEquivInductZ(zVirt, List(Context(Cons(Star(rInner), tlExp))), generalisedConcat(Cons(Star(rInner), tlExp)), s) + + } + case _ => { + assume(matchR(r, s) == matchZipper(z, s)) + lemmaEmptyZipperMatchesNothing(zDerivDown, stl) + assert(zDerivDown == Set()) + assert(matchR(r, s) == matchZipper(z, s)) + } + } + + val zTail = Set(Context(tlExp)) + val zDerivTail = derivationStepZipper(zTail, shd) + SetUtils.lemmaFlatMapOnSingletonSet(zTail, Context(tlExp), (c: Context[C]) => derivationStepZipperUp(c, shd)) + assert(zDerivUpUp == zDerivTail) + + assert(r == generalisedConcat(hd.exprs)) + assert(regexDepth(r) >= regexDepth(generalisedConcat(tlExp))) + assert(zipperDepth(zl) >= zipperDepth(List(Context(tlExp)))) + + theoremZipperRegexEquivInductZ(zTail, List(Context(tlExp)), generalisedConcat(tlExp), s) + assert(matchR(generalisedConcat(tlExp), s) == matchZipper(zTail, s)) - assert(regexDepth(r1) < regexDepth(r)) - assert(contextDepth(Context(List(r1, r2))) < contextDepth(Context(List(r)))) - assert(regexDepth(r) == regexDepth(Concat(r1, r2))) - theoremZipperRegexEquivInductZ(zR1, List(Context(List(r1, r2))), Concat(r1, r2), s) - theoremZipperRegexEquivInductZ(zR2, List(Context(List(r2))), r2, s) - } else { - assert(zDeriv == derivDownZR1) - theoremZipperRegexEquivInductZ(zR1, List(Context(List(r1, r2))), Concat(r1, r2), s) - theoremZipperRegexEquivInductZ(zR2, List(Context(List(r2))), r2, s) - } + assume(matchR(r, s) == matchZipper(z, s)) + } } } @@ -651,82 +1065,14 @@ object ZipperRegex { assert(regexDepth(rInner) < regexDepth(r)) assert(contextDepth(Context(List(rInner, Star(rInner)))) == contextDepth(Context(List(r)))) assert(regexDepth(r) + 1 == regexDepth(Concat(rInner, Star(rInner)))) - theoremZipperRegexEquivInductZ(zR1, List(Context(List(rInner, Star(rInner)))), Concat(rInner, Star(rInner)), s) - assert(matchR(Concat(rInner, Star(rInner)), s) == matchZipper(zR1, s)) + assume(matchR(r, s) == matchZipper(z, s)) + // theoremZipperRegexEquivInductZ(zR1, List(Context(List(rInner, Star(rInner)))), Concat(rInner, Star(rInner)), s) // TODO measure + // assert(matchR(Concat(rInner, Star(rInner)), s) == matchZipper(zR1, s)) mainMatchTheorem(Concat(rInner, Star(rInner)), s) } } } } - } - case Cons(hExp, tlExp) => { - s match { - case Nil() => { - lemmaUnfocusPreservesNullability(r, z) - assert(nullableZipper(z) == nullable(r)) - check(matchZipper(z, s) == matchR(r, s)) - } - case Cons(shd, stl) => { - val zDeriv = derivationStepZipper(z, shd) - val zDerivUp = derivationStepZipperUp(Context(Cons(hExp, tlExp)), shd) - val zDerivDown = derivationStepZipperDown(hExp, Context(tlExp), shd) - val zDerivUpUp = derivationStepZipperUp(Context(tlExp), shd) - SetUtils.lemmaFlatMapOnSingletonSet(z, hd, (c: Context[C]) => derivationStepZipperUp(c, shd)) - if(nullable(hExp)){ - assert(zDeriv == zDerivDown ++ zDerivUpUp) - } else { - assert(zDeriv == zDerivDown) - } - assert(r == generalisedConcat(hd.exprs)) - assert(isConcat(r)) - r match { - case Concat(r1, r2) => { - assert(matchR(r, s) == findConcatSeparation(r1, r2, Nil(), s, s).isDefined) - assert(r1 == hExp) - assert(r2 == generalisedConcat(tlExp)) - - assert(matchZipper(z, s) == matchZipper(zDeriv, stl)) - if(nullable(r1)){ - assert(zDeriv == zDerivDown ++ zDerivUpUp) - lemmaZipperConcatMatchesSameAsBothZippers(zDerivDown, zDerivUpUp, stl) - assert((matchZipper(zDerivDown, stl) || matchZipper(zDerivUpUp, stl)) == matchZipper(zDeriv, stl)) - assert((matchZipper(zDerivDown, stl) || matchZipper(zDerivUpUp, stl)) == matchZipper(z, s)) - - assert(matchZipper(zDerivDown, stl) == matchZipper(derivationStepZipperDown(r1, Context(tlExp), shd), stl)) - - val zR1 = Set(Context(Cons(r1, tlExp))) - val zR2 = Set(Context(tlExp)) - val derivZR1 = derivationStepZipper(zR1, shd) - val derivZR2 = derivationStepZipper(zR2, shd) - - val derivUpZR1 = derivationStepZipperUp(Context(Cons(r1, tlExp)), shd) - val derivUpZR2 = derivationStepZipperUp(Context(tlExp), shd) - - SetUtils.lemmaFlatMapOnSingletonSet(zR1, Context(Cons(r1, tlExp)), (c: Context[C]) => derivationStepZipperUp(c, shd)) - SetUtils.lemmaFlatMapOnSingletonSet(zR2, Context(tlExp), (c: Context[C]) => derivationStepZipperUp(c, shd)) - - // val derivDownZR1 = derivationStepZipperDown(r1, Context(tlExp), shd) - // val derivDownZR2 = derivationStepZipperDown(r2, Context(List()), shd) - - theoremZipperRegexEquivInductZ(zR1, List(Context(Cons(r1, tlExp))), r, s) - theoremZipperRegexEquivInductZ(zR2, List(Context(tlExp)), r2, s) - - - } else { - assert(zDeriv == zDerivDown) - } - - - - - assume(matchR(r, s) == matchZipper(z, s)) - } - } - - } - } - - } case Nil() => { assert(r == EmptyExpr[C]()) @@ -2374,6 +2720,65 @@ object VerifiedRegexMatcher { } }.ensuring (_ => findConcatSeparation(r1, r2, Nil(), s, s).isDefined) + + @ghost + @inlineOnce + @opaque + def lemmaConcatAssociative[C](r1: Regex[C], r2: Regex[C], r3: Regex[C], s: List[C]): Unit = { + require(validRegex(r1) && validRegex(r2) && validRegex(r3)) + decreases(s) + + val rL = Concat(Concat(r1, r2), r3) + val rR = Concat(r1, Concat(r2, r3)) + mainMatchTheorem(rL, s) + mainMatchTheorem(rR, s) + if(matchR(rL, s)){ + val (s1, s2) = findConcatSeparation(Concat(r1, r2), r3, Nil(), s, s).get + mainMatchTheorem(Concat(r1, r2), s1) + assert(matchR(Concat(r1, r2), s1)) + assert(matchR(r3, s2)) + val (s11, s22) = findConcatSeparation(r1, r2, Nil(), s1, s1).get + assert(matchR(r1, s11)) + assert(matchR(r2, s22)) + mainMatchTheorem(r1, s11) + mainMatchTheorem(r2, s22) + assert(s11 ++ s22 ++ s2 == s) + + mainMatchTheorem(Concat(r2, r3), s22 ++ s2) + lemmaR1MatchesS1AndR2MatchesS2ThenFindSeparationFindsAtLeastThem(r2, r3, s22, s2, s22 ++ s2, Nil(), s22 ++ s2) + assert(matchR(Concat(r2, r3), s22 ++ s2)) + ListUtils.lemmaTwoListsConcatAssociativity(s11, s22, s2) + assert(s11 ++ (s22 ++ s2) == s) + lemmaR1MatchesS1AndR2MatchesS2ThenFindSeparationFindsAtLeastThem(r1, Concat(r2, r3), s11, s22 ++ s2, s, Nil(), s) + } else { + if(findConcatSeparation(r1, Concat(r2, r3), Nil(), s, s).isDefined){ + val (s1, s2) = findConcatSeparation(r1, Concat(r2, r3), Nil(), s, s).get + mainMatchTheorem(r1, s1) + assert(matchR(r1, s1)) + assert(matchR(Concat(r2, r3), s2)) + mainMatchTheorem(Concat(r2, r3), s2) + val (s11, s22) = findConcatSeparation(r2, r3, Nil(), s2, s2).get + assert(matchR(r2, s11)) + assert(matchR(r3, s22)) + mainMatchTheorem(r2, s11) + mainMatchTheorem(r3, s22) + + assert(s1 ++ (s11 ++ s22) == s) + ListUtils.lemmaTwoListsConcatAssociativity(s1, s11, s22) + + mainMatchTheorem(Concat(r1, r2), s1 ++ s11) + lemmaR1MatchesS1AndR2MatchesS2ThenFindSeparationFindsAtLeastThem(r1, r2, s1, s11, s1 ++ s11, Nil(), s1 ++ s11) + assert(matchR(Concat(r1, r2), s1 ++ s11)) + + assert((s1 ++ s11) ++ s22 == s) + lemmaR1MatchesS1AndR2MatchesS2ThenFindSeparationFindsAtLeastThem(Concat(r1, r2), r3, s1 ++ s11, s22, s, Nil(), s) + + check(false) + } + } + + + }.ensuring (_ => matchR(Concat(Concat(r1, r2), r3), s) == matchR(Concat(r1, Concat(r2, r3)), s)) // Star lemmas @ghost From 07e015288f0eeeeda8da4cc9e0bed815033af689 Mon Sep 17 00:00:00 2001 From: Samuel Chassot Date: Wed, 20 Nov 2024 14:00:54 +0100 Subject: [PATCH 53/78] working on big proof direct induction - not working for star --- .../src/main/scala/ch/epfl/lexer/Utils.scala | 1 - .../src/main/scala/ch/epfl/lexer/VerifiedRegex.scala | 12 ++++++------ 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/Utils.scala b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/Utils.scala index 1b421045..65768172 100644 --- a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/Utils.scala +++ b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/Utils.scala @@ -12,7 +12,6 @@ import stainless.lang.StaticChecks._ import ch.epfl.lexer.ListUtils.lemmaSubseqRefl object SetUtils { - @opaque @ghost @inlineOnce diff --git a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedRegex.scala b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedRegex.scala index a195866f..716e45fc 100644 --- a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedRegex.scala +++ b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedRegex.scala @@ -531,7 +531,7 @@ object ZipperRegex { @ghost @opaque - @inlineOnce + @inlineOnce // type Zipper[C] = Set[Context[C]] def theoremZipperRegexEquivInductZ[C](z: Zipper[C], zl: List[Context[C]], r: Regex[C], s: List[C]): Unit = { require(validRegex(r)) require(z.toList == zl) @@ -993,17 +993,17 @@ object ZipperRegex { case Star(rInner) => { assert(zDerivDown == derivationStepZipperDown(rInner, Context(Cons(Star(rInner), tlExp)), shd)) - val zVirt = Set(Context(Cons(Star(rInner), tlExp))) + val zVirt = Set(Context(Cons(Concat(rInner, Star(rInner)), tlExp))) val zVirtDeriv = derivationStepZipper(zVirt, shd) - val zVirtDerivUp = derivationStepZipperUp(Context(Cons(Star(rInner), tlExp)), shd) - SetUtils.lemmaFlatMapOnSingletonSet(zVirt, Context(Cons(Star(rInner), tlExp)), (c: Context[C]) => derivationStepZipperUp(c, shd)) - assert(zVirtDeriv == derivationStepZipperDown(rInner, Context(Cons(Star(rInner), tlExp)), shd) ++ derivationStepZipperUp(Context(tlExp), shd)) + val zVirtDerivUp = derivationStepZipperUp(Context(Cons(Concat(rInner, Star(rInner)), tlExp)), shd) + SetUtils.lemmaFlatMapOnSingletonSet(zVirt, Context(Cons(Concat(rInner, Star(rInner)), tlExp)), (c: Context[C]) => derivationStepZipperUp(c, shd)) + assert(zVirtDeriv == derivationStepZipperDown(rInner, Context(Cons(Concat(rInner, Star(rInner)), tlExp)), shd) ++ derivationStepZipperUp(Context(tlExp), shd)) assert(matchZipper(zVirt, s) == matchZipper(zVirtDeriv, stl)) //TODO measures - theoremZipperRegexEquivInductZ(zVirt, List(Context(Cons(Star(rInner), tlExp))), generalisedConcat(Cons(Star(rInner), tlExp)), s) + theoremZipperRegexEquivInductZ(zVirt, List(Context(Cons(Concat(rInner, Star(rInner)), tlExp))), generalisedConcat(Cons(Concat(rInner, Star(rInner)), tlExp)), s) } case _ => { From 1d2bedbf8e9406d75497e7e2f031df3a931d58be Mon Sep 17 00:00:00 2001 From: Samuel Chassot Date: Wed, 20 Nov 2024 18:16:55 +0100 Subject: [PATCH 54/78] working on zipper proof --- .../scala/ch/epfl/lexer/VerifiedRegex.scala | 323 +++++++----------- lexers/regex/verifiedlexer/stainless.conf | 2 +- 2 files changed, 119 insertions(+), 206 deletions(-) diff --git a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedRegex.scala b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedRegex.scala index 716e45fc..bb6c8af3 100644 --- a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedRegex.scala +++ b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedRegex.scala @@ -289,6 +289,10 @@ object ZipperRegex { require(!isEmpty) Context(exprs.tail) } + inline def concat(that: Context[C]): Context[C] = { + ghostExpr(ListUtils.lemmaConcatPreservesForall(exprs, that.exprs, validRegex)) + Context(exprs ++ that.exprs) + } } type Zipper[C] = Set[Context[C]] @@ -551,157 +555,6 @@ object ZipperRegex { case Cons(hd, tl) if tl.isEmpty => { assert(r == generalisedConcat(hd.exprs)) hd.exprs match { - // case Cons(hExp, tlExp) if tlExp.isEmpty => { - // assert(r == hExp) - // r match { - // case EmptyExpr() => lemmaZipperOfEmptyExprMatchesOnlyEmptyString(z, s) - // case EmptyLang() => lemmaZipperStartingWithEmptyLangMatchesNothing(z, Context(List(r)), s) - // case ElementMatch(a) => lemmaElementMatchZipperAcceptsOnlyThisChar(z, Context(List(ElementMatch(a))), a, s) - // case Union(r1, r2) => { - // mainMatchTheorem(r1, s) - // mainMatchTheorem(r2, s) - // assert(matchR(r, s) == (matchR(r1, s) || matchR(r2, s))) - // s match { - // case Nil() => { - // lemmaUnfocusPreservesNullability(r, z) - // assert(nullableZipper(z) == nullable(r)) - // check(matchZipper(z, s) == matchR(r, s)) - // } - // case Cons(shd, stl) => { - // val deriv = derivationStepZipper(z, shd) - // val derivUp = derivationStepZipperUp(Context(List(r)), shd) - // val derivDown = derivationStepZipperDown(r, Context(List()), shd) - // assert(derivUp == derivDown) - // assert(derivDown == derivationStepZipperDown(r1, Context(List()), shd) ++ derivationStepZipperDown(r2, Context(List()), shd)) - // val z1 = derivationStepZipperDown(r1, Context(List()), shd) - // val z2 = derivationStepZipperDown(r2, Context(List()), shd) - // SetUtils.lemmaFlatMapOnSingletonSet(z, hd, (c: Context[C]) => derivationStepZipperUp(c, shd)) - // assert(deriv == z1 ++ z2) - // assert(matchZipper(z1 ++ z2, stl) == matchZipper(z, s)) - // lemmaZipperConcatMatchesSameAsBothZippers(z1, z2, stl) - // assert((matchZipper(z1, stl) || matchZipper(z2, stl)) == matchZipper(z1 ++ z2, stl)) - // assert((matchZipper(z1, stl) || matchZipper(z2, stl)) == matchZipper(z, s)) - - // val zR1 = Set(Context(List(r1))) - // val zR2 = Set(Context(List(r2))) - // val derivZR1 = derivationStepZipper(zR1, shd) - // val derivZR2 = derivationStepZipper(zR2, shd) - - // val derivUpZR1 = derivationStepZipperUp(Context(List(r1)), shd) - // val derivUpZR2 = derivationStepZipperUp(Context(List(r2)), shd) - // SetUtils.lemmaFlatMapOnSingletonSet(zR1, Context(List(r1)), (c: Context[C]) => derivationStepZipperUp(c, shd)) - // SetUtils.lemmaFlatMapOnSingletonSet(zR2, Context(List(r2)), (c: Context[C]) => derivationStepZipperUp(c, shd)) - - // val zR1Deriv = derivationStepZipperDown(r1, Context(List()), shd) - // val zR2Deriv = derivationStepZipperDown(r2, Context(List()), shd) - // assert(zR1Deriv == z1) - // assert(zR2Deriv == z2) - // assert(matchZipper(zR1, s) == matchZipper(zR1Deriv, stl)) - // assert(matchZipper(zR2, s) == matchZipper(zR2Deriv, stl)) - // assert((matchZipper(zR1, s) || matchZipper(zR2, s)) == matchZipper(z, s)) - // theoremZipperRegexEquivInductZ(zR1, List(Context(List(r1))), r1, s) - // theoremZipperRegexEquivInductZ(zR2, List(Context(List(r2))), r2, s) - // } - // } - // } - // case Concat(r1, r2) => { - // assert(matchR(r, s) == findConcatSeparation(r1, r2, Nil(), s, s).isDefined) - // s match { - // case Nil() => { - // lemmaUnfocusPreservesNullability(r, z) - // assert(nullableZipper(z) == nullable(r)) - // check(matchZipper(z, s) == matchR(r, s)) - // } - // case Cons(shd, stl) => { - // val zDeriv = derivationStepZipper(z, shd) - // val zDerivUp = derivationStepZipperUp(Context(List(r)), shd) - // val zDerivDown = derivationStepZipperDown(r, Context(List()), shd) - // assert(zDerivUp == zDerivDown) - // SetUtils.lemmaFlatMapOnSingletonSet(z, hd, (c: Context[C]) => derivationStepZipperUp(c, shd)) - // assert(zDeriv == zDerivDown) - // val zR1 = Set(Context(List(r1, r2))) - // val zR2 = Set(Context(List(r2))) - // val derivZR1 = derivationStepZipper(zR1, shd) - // val derivZR2 = derivationStepZipper(zR2, shd) - - // val derivUpZR1 = derivationStepZipperUp(Context(List(r1, r2)), shd) - // val derivUpZR2 = derivationStepZipperUp(Context(List(r2)), shd) - - // SetUtils.lemmaFlatMapOnSingletonSet(zR1, Context(List(r1, r2)), (c: Context[C]) => derivationStepZipperUp(c, shd)) - // SetUtils.lemmaFlatMapOnSingletonSet(zR2, Context(List(r2)), (c: Context[C]) => derivationStepZipperUp(c, shd)) - - // val derivDownZR1 = derivationStepZipperDown(r1, Context(List(r2)), shd) - // val derivDownZR2 = derivationStepZipperDown(r2, Context(List()), shd) - - // if(nullable(r1)){ - // assert(zDeriv == derivDownZR1 ++ derivDownZR2) - // lemmaZipperConcatMatchesSameAsBothZippers(derivDownZR1, derivDownZR2, stl) - // assert((matchZipper(derivDownZR1, stl) || matchZipper(derivDownZR2, stl)) == matchZipper(zDeriv, stl)) - - // assert(derivUpZR1 == derivDownZR1 ++ derivUpZR2) - // assert(derivUpZR1 == derivDownZR1 ++ derivDownZR2) - // assert(derivUpZR1 == zDeriv) - - // assert(matchZipper(zR1, s) == matchZipper(derivationStepZipper(zR1, shd), stl)) - - // assert(matchZipper(zR1, s) == matchZipper(derivZR1, stl)) - // assert(matchZipper(zR2, s) == matchZipper(derivZR2, stl)) - - // assert(regexDepth(r1) < regexDepth(r)) - // assert(contextDepth(Context(List(r1, r2))) < contextDepth(Context(List(r)))) - // assert(regexDepth(r) == regexDepth(Concat(r1, r2))) - // theoremZipperRegexEquivInductZ(zR1, List(Context(List(r1, r2))), Concat(r1, r2), s) - // theoremZipperRegexEquivInductZ(zR2, List(Context(List(r2))), r2, s) - - - // } else { - // assert(zDeriv == derivDownZR1) - - // theoremZipperRegexEquivInductZ(zR1, List(Context(List(r1, r2))), Concat(r1, r2), s) - // theoremZipperRegexEquivInductZ(zR2, List(Context(List(r2))), r2, s) - // } - // } - // } - // } - // case Star(rInner) => { - // assert(matchR(r, s) == s.isEmpty || findConcatSeparation(rInner, Star(rInner), Nil(), s, s).isDefined) - // s match { - // case Nil() => { - // lemmaUnfocusPreservesNullability(r, z) - // assert(nullableZipper(z) == nullable(r)) - // check(matchZipper(z, s) == matchR(r, s)) - // } - // case Cons(shd, stl) => { - // val zDeriv = derivationStepZipper(z, shd) - // val zDerivUp = derivationStepZipperUp(Context(List(r)), shd) - // val zDerivDown = derivationStepZipperDown(r, Context(List()), shd) - // assert(zDerivUp == zDerivDown) - // SetUtils.lemmaFlatMapOnSingletonSet(z, hd, (c: Context[C]) => derivationStepZipperUp(c, shd)) - // assert(zDeriv == zDerivDown) - - // val zR1 = Set(Context(List(rInner, Star(rInner)))) - // val derivZR1 = derivationStepZipper(zR1, shd) - - // val derivUpZR1 = derivationStepZipperUp(Context(List(rInner, Star(rInner))), shd) - - // SetUtils.lemmaFlatMapOnSingletonSet(zR1, Context(List(rInner, Star(rInner))), (c: Context[C]) => derivationStepZipperUp(c, shd)) - // val derivDownZR1 = derivationStepZipperDown(rInner, Context(List(Star(rInner))), shd) - - // assert(zDeriv == derivDownZR1) - // assert(matchZipper(zR1, s) == matchZipper(derivationStepZipper(zR1, shd), stl)) - // assert(matchZipper(zR1, s) == matchZipper(derivZR1, stl)) - - // assert(regexDepth(rInner) < regexDepth(r)) - // assert(contextDepth(Context(List(rInner, Star(rInner)))) == contextDepth(Context(List(r)))) - // assert(regexDepth(r) + 1 == regexDepth(Concat(rInner, Star(rInner)))) - // theoremZipperRegexEquivInductZ(zR1, List(Context(List(rInner, Star(rInner)))), Concat(rInner, Star(rInner)), s) - // assert(matchR(Concat(rInner, Star(rInner)), s) == matchZipper(zR1, s)) - // mainMatchTheorem(Concat(rInner, Star(rInner)), s) - // } - // } - // } - // } - // } case Cons(hExp, tlExp) => { assert(r == generalisedUnion(unfocusZipperList(zl))) r match { @@ -993,17 +846,7 @@ object ZipperRegex { case Star(rInner) => { assert(zDerivDown == derivationStepZipperDown(rInner, Context(Cons(Star(rInner), tlExp)), shd)) - val zVirt = Set(Context(Cons(Concat(rInner, Star(rInner)), tlExp))) - val zVirtDeriv = derivationStepZipper(zVirt, shd) - val zVirtDerivUp = derivationStepZipperUp(Context(Cons(Concat(rInner, Star(rInner)), tlExp)), shd) - SetUtils.lemmaFlatMapOnSingletonSet(zVirt, Context(Cons(Concat(rInner, Star(rInner)), tlExp)), (c: Context[C]) => derivationStepZipperUp(c, shd)) - assert(zVirtDeriv == derivationStepZipperDown(rInner, Context(Cons(Concat(rInner, Star(rInner)), tlExp)), shd) ++ derivationStepZipperUp(Context(tlExp), shd)) - - assert(matchZipper(zVirt, s) == matchZipper(zVirtDeriv, stl)) - - - //TODO measures - theoremZipperRegexEquivInductZ(zVirt, List(Context(Cons(Concat(rInner, Star(rInner)), tlExp))), generalisedConcat(Cons(Concat(rInner, Star(rInner)), tlExp)), s) + assume(matchR(r, s) == matchZipper(z, s)) // TODO } case _ => { @@ -1062,13 +905,49 @@ object ZipperRegex { assert(matchZipper(zR1, s) == matchZipper(derivationStepZipper(zR1, shd), stl)) assert(matchZipper(zR1, s) == matchZipper(derivZR1, stl)) - assert(regexDepth(rInner) < regexDepth(r)) - assert(contextDepth(Context(List(rInner, Star(rInner)))) == contextDepth(Context(List(r)))) - assert(regexDepth(r) + 1 == regexDepth(Concat(rInner, Star(rInner)))) - assume(matchR(r, s) == matchZipper(z, s)) - // theoremZipperRegexEquivInductZ(zR1, List(Context(List(rInner, Star(rInner)))), Concat(rInner, Star(rInner)), s) // TODO measure - // assert(matchR(Concat(rInner, Star(rInner)), s) == matchZipper(zR1, s)) - mainMatchTheorem(Concat(rInner, Star(rInner)), s) + // equivalent regex to zR1 + assert(unfocusZipper(zl) == r) + assert(r == Star(rInner)) + val rR1 = Concat(rInner, Star(rInner)) + assert(unfocusZipper(List(Context(List(rInner, Star(rInner))))) == rR1) + + val subZR1 = Set(Context(List(rInner))) + val subZR2 = Set(Context(List(Star(rInner)))) + + val derivSubZR1 = derivationStepZipper(subZR1, shd) + val derivSubZR2 = derivationStepZipper(subZR2, shd) + + val derivUpSubZR1 = derivationStepZipperUp(Context(List(rInner)), shd) + val derivUpSubZR2 = derivationStepZipperUp(Context(List(Star(rInner))), shd) + + SetUtils.lemmaFlatMapOnSingletonSet(subZR1, Context(List(rInner)), (c: Context[C]) => derivationStepZipperUp(c, shd)) + SetUtils.lemmaFlatMapOnSingletonSet(subZR2, Context(List(Star(rInner))), (c: Context[C]) => derivationStepZipperUp(c, shd)) + + assert(zDeriv == derivSubZR2) + + assert(unfocusZipper(List(Context(List(rInner)))) == rInner) + assert(unfocusZipper(List(Context(List(Star(rInner))))) == Star(rInner)) + mainMatchTheorem(rR1, s) + + if(matchR(rR1, s)){ + val (s1, s2) = findConcatSeparation(rInner, Star(rInner), Nil(), s, s).get + assert(s == s1 ++ s2) + assert(matchR(rInner, s1)) + assert(matchR(Star(rInner), s2)) + + theoremZipperRegexEquivInductZ(subZR1, List(Context(List(rInner))), rInner, s1) + theoremZipperRegexEquivInductZ(subZR2, List(Context(List(Star(rInner)))), Star(rInner), s2) + assert(matchZipper(subZR1, s1)) + assert(matchZipper(subZR2, s2)) + lemmaConcatenateContextMatchesConcatOfStrings(Context(List(rInner)), Context(List(Star(rInner))), s1, s2) + check(matchR(r, s) == matchZipper(z, s)) + } else{ + check(matchR(r, s) == matchZipper(z, s)) + } + + + + } } } @@ -1207,6 +1086,76 @@ object ZipperRegex { }.ensuring(_ => derivationStepZipper(z, a) == derivationStepZipper(z1, a) ++ derivationStepZipper(z2, a)) + @ghost + @opaque + @inlineOnce + def lemmaConcatenateContextMatchesConcatOfStrings[C](ct1: Context[C], ct2: Context[C], s1: List[C], s2: List[C]): Unit = { + // require(r1 == generalisedConcat(ct1.exprs)) + // require(r2 == generalisedConcat(ct2.exprs)) + require(matchZipper(Set(ct1), s1)) + require(matchZipper(Set(ct2), s2)) + decreases(s1.size, s2.size) + val z1 = Set(ct1) + val z2 = Set(ct2) + if(ct1.isEmpty){ + lemmaZipperOfEmptyContextMatchesEmptyString(Set(ct1), s1) + assert(s1.isEmpty) + assert(ct1.concat(ct2) == ct2) + assert(s1 ++ s2 == s2) + check(matchZipper(Set(ct1.concat(ct2)), s1 ++ s2)) + } else { + assert(matchZipper(z1, s1)) + s1 match { + case Cons(shd, stl) => { + assert(matchZipper(derivationStepZipper(z1, shd), stl)) + val z1Deriv = derivationStepZipper(z1, shd) + lemmaZipperMatchesExistsMatchingContext(z1Deriv.toList, stl) + assert(z1Deriv.exists(c => matchZipper(Set(c), stl))) + val witnessContext = SetUtils.getWitness(z1Deriv, (c: Context[C]) => matchZipper(Set(c), stl)) + lemmaConcatenateContextMatchesConcatOfStrings(witnessContext, ct2, stl, s2) + assert(matchZipper(Set(witnessContext.concat(ct2)), stl ++ s2)) + check(matchZipper(Set(ct1.concat(ct2)), s1 ++ s2)) + } + case Nil() => { + assert(nullableZipper(z1)) + val witness = SetUtils.getWitness(z1, (c: Context[C]) => nullableContext(c)) + assert(ct1 == witness) + assert(nullableContext(ct1)) + + } + } + } + }.ensuring(_ => matchZipper(Set(ct1.concat(ct2)), s1 ++ s2)) + + @ghost + @opaque + @inlineOnce + def lemmaZipperMatchesExistsMatchingContext[C](zl: List[Context[C]], s: List[C]): Unit = { + require(matchZipper(zl.content, s)) + decreases(zl.size) + zl match { + case Cons(ctHd, ctTl) => { + val zHd = Set(ctHd) + lemmaZipperConcatMatchesSameAsBothZippers(zHd, ctTl.content, s) + assert(matchZipper(zl.content, s) == matchZipper(zHd, s) || matchZipper(ctTl.content, s)) + if(matchZipper(zHd, s)){ + SetUtils.lemmaContainsThenExists(zl.content, ctHd, (c: Context[C]) => matchZipper(Set(c), s)) + check(zl.content.exists(c => matchZipper(Set(c), s))) + } else { + assert(matchZipper(ctTl.content, s)) + lemmaZipperMatchesExistsMatchingContext( ctTl, s) + assert(matchZipper(zl.content, s)) + check(matchZipper(zl.content, s)) + } + check(zl.exists(c => matchZipper(Set(c), s))) + } + case Nil() => + lemmaEmptyZipperMatchesNothing(zl.content, s) + check(false) + } + }.ensuring(_ => zl.exists(c => matchZipper(Set(c), s))) + + @ghost @opaque @inlineOnce @@ -1265,42 +1214,6 @@ object ZipperRegex { }.ensuring(_ => matchZipper(z, s) == (s == List(a))) - // @ghost - // @opaque - // @inlineOnce - // def lemmaEmptyExprZipperMatchesOnlyEmptyString[C](z: Zipper[C], c: Context[C], s: List[C]): Unit = { - // require(z == Set(Context(List(EmptyExpr[C]())))) - // require(!c.isEmpty) - // require(c.head == EmptyExpr[C]()) - // require(c.tail.isEmpty) - - // s match { - // case Cons(hd, tl) => { - // val deriv: Zipper[C]= derivationStepZipper(z, hd) - // val derivUp = derivationStepZipperUp(c, hd) - // assert(derivUp == derivationStepZipperDown(c.head, Context(List()), hd) ++ derivationStepZipperUp(Context(List()), hd)) - // if(!deriv.isEmpty){ - // val hd = deriv.toList.head - // val f: Context[C] => Zipper[C] = ct => derivationStepZipperUp(ct, s.head) - // assert(z.flatMap(f).contains(hd)) - // assert(deriv.contains(hd)) - // unfold(z.flatMapPost(f)(hd)) - // assert(z.exists(context => derivationStepZipperUp(context, s.head).contains(hd))) - // assert(z == Set(c)) - // check(false) - // } - // assert(deriv.isEmpty) - // lemmaEmptyZipperMatchesNothing(deriv, s.tail) - // check(!s.isEmpty) - // check(matchZipper(z, s) == false) - // } - // case Nil() => - // check(nullableContext(c)) - // check(matchZipper(z, s)) - // } - - // }.ensuring(_ => matchZipper(z, s) == s.isEmpty) - @ghost @opaque @inlineOnce diff --git a/lexers/regex/verifiedlexer/stainless.conf b/lexers/regex/verifiedlexer/stainless.conf index 960e9600..21f1c5b1 100644 --- a/lexers/regex/verifiedlexer/stainless.conf +++ b/lexers/regex/verifiedlexer/stainless.conf @@ -3,7 +3,7 @@ vc-cache = true # debug = ["verification", "smt"] -timeout = 20 +timeout = 5 check-models = false print-ids = false print-types = false From fce7d93996c39cb2845c190be71b060c0c20577a Mon Sep 17 00:00:00 2001 From: Samuel Chassot Date: Thu, 21 Nov 2024 19:31:20 +0100 Subject: [PATCH 55/78] working on the zipper proof --- .../scala/ch/epfl/lexer/VerifiedRegex.scala | 664 +++++++++++++++--- lexers/regex/verifiedlexer/stainless.conf | 2 +- 2 files changed, 565 insertions(+), 101 deletions(-) diff --git a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedRegex.scala b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedRegex.scala index bb6c8af3..0e7a9fae 100644 --- a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedRegex.scala +++ b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedRegex.scala @@ -18,6 +18,7 @@ import ch.epfl.map.TupleListOpsGenK.invariantList import ch.epfl.map.MutableHashMap import stainless.lang.StaticChecks._ +import stainless.annotation.isabelle.lemma // import ch.epfl.map.OptimisedChecks.* object Memoisation { @@ -462,76 +463,76 @@ object ZipperRegex { }.ensuring(_ => zl.exists(c => VerifiedRegex.generalisedConcat(c.exprs) == r)) - @ghost - @opaque - @inlineOnce - def theoremZipperRegexEquiv[C](z: Zipper[C], zl: List[Context[C]], r: Regex[C], s: List[C]): Unit = { - require(validRegex(r)) - require(z.toList == zl) - require(r == unfocusZipper(zl)) - decreases(regexDepth(r)) - mainMatchTheorem(r, s) - r match { - case EmptyExpr() => lemmaZipperOfEmptyExprMatchesOnlyEmptyString(z, s) - case EmptyLang() => - if(z.isEmpty){ - lemmaEmptyZipperMatchesNothing(z, s) - } else { - lemmaZipperStartingWithEmptyLangMatchesNothing(z, Context(List(r)), s) - } - case ElementMatch(a) => lemmaElementMatchZipperAcceptsOnlyThisChar(z, Context(List(ElementMatch(a))), a, s) - case Union(r1, r2) => - { - mainMatchTheorem(r1, s) - mainMatchTheorem(r2, s) - assert(matchR(r, s) == (matchR(r1, s) || matchR(r2, s))) - s match { - case Nil() => { - lemmaUnfocusPreservesNullability(r, z) - assert(nullableZipper(z) == nullable(r)) - check(matchZipper(z, s) == matchR(r, s)) - } - case Cons(shd, stl) => { - assert(!z.toList.isEmpty) - val zHd: Context[C] = z.toList.head - val zTl: List[Context[C]] = z.toList.tail - - assert(matchZipper(z, s) == matchZipper(derivationStepZipper(z, shd), stl)) - assert(r == generalisedUnion(unfocusZipperList(z.toList))) - matchRGenUnionSpec(r, unfocusZipperList(z.toList), s) - assert(matchR(r, s) == unfocusZipperList(z.toList).exists(rr => validRegex(rr) && matchR(rr, s))) - - if(zTl.isEmpty) { - assert(r == generalisedConcat(zHd.exprs)) - assert(zHd.exprs == List(r)) - // Now let's dive in the derivative computation - val deriv = derivationStepZipper(z, shd) - val derivUp = derivationStepZipperUp(Context(List(r)), shd) - zHd.exprs match { - case Cons(right, parent) if nullable(right) => assert(derivationStepZipperDown(right, Context(parent), shd) ++ derivationStepZipperUp(Context(parent),shd) == derivationStepZipperUp(Context(List(r)), shd)) - case Cons(right, parent) => assert(derivationStepZipperDown(right, Context(parent), shd) == derivationStepZipperUp(Context(List(r)), shd)) - case Nil() => assert(Set[Context[C]]() == derivationStepZipperUp(Context(List(r)), shd)) - } - - } else { - unfold(generalisedUnion(unfocusZipperList(z.toList))) - assert(r2 == unfocusZipper(zTl)) - } + // @ghost + // @opaque + // @inlineOnce + // def theoremZipperRegexEquiv[C](z: Zipper[C], zl: List[Context[C]], r: Regex[C], s: List[C]): Unit = { + // require(validRegex(r)) + // require(z.toList == zl) + // require(r == unfocusZipper(zl)) + // decreases(regexDepth(r)) + // mainMatchTheorem(r, s) + // r match { + // case EmptyExpr() => lemmaZipperOfEmptyExprMatchesOnlyEmptyString(z, s) + // case EmptyLang() => + // if(z.isEmpty){ + // lemmaEmptyZipperMatchesNothing(z, s) + // } else { + // lemmaZipperStartingWithEmptyLangMatchesNothing(z, Context(List(r)), s) + // } + // case ElementMatch(a) => lemmaElementMatchZipperAcceptsOnlyThisChar(z, Context(List(ElementMatch(a))), a, s) + // case Union(r1, r2) => + // { + // mainMatchTheorem(r1, s) + // mainMatchTheorem(r2, s) + // assert(matchR(r, s) == (matchR(r1, s) || matchR(r2, s))) + // s match { + // case Nil() => { + // lemmaUnfocusPreservesNullability(r, z) + // assert(nullableZipper(z) == nullable(r)) + // check(matchZipper(z, s) == matchR(r, s)) + // } + // case Cons(shd, stl) => { + // assert(!z.toList.isEmpty) + // val zHd: Context[C] = z.toList.head + // val zTl: List[Context[C]] = z.toList.tail + + // assert(matchZipper(z, s) == matchZipper(derivationStepZipper(z, shd), stl)) + // assert(r == generalisedUnion(unfocusZipperList(z.toList))) + // matchRGenUnionSpec(r, unfocusZipperList(z.toList), s) + // assert(matchR(r, s) == unfocusZipperList(z.toList).exists(rr => validRegex(rr) && matchR(rr, s))) + + // if(zTl.isEmpty) { + // assert(r == generalisedConcat(zHd.exprs)) + // assert(zHd.exprs == List(r)) + // // Now let's dive in the derivative computation + // val deriv = derivationStepZipper(z, shd) + // val derivUp = derivationStepZipperUp(Context(List(r)), shd) + // zHd.exprs match { + // case Cons(right, parent) if nullable(right) => assert(derivationStepZipperDown(right, Context(parent), shd) ++ derivationStepZipperUp(Context(parent),shd) == derivationStepZipperUp(Context(List(r)), shd)) + // case Cons(right, parent) => assert(derivationStepZipperDown(right, Context(parent), shd) == derivationStepZipperUp(Context(List(r)), shd)) + // case Nil() => assert(Set[Context[C]]() == derivationStepZipperUp(Context(List(r)), shd)) + // } + + // } else { + // unfold(generalisedUnion(unfocusZipperList(z.toList))) + // assert(r2 == unfocusZipper(zTl)) + // } - } - } - } - case Star(rInner) => assume(matchR(r, s) == matchZipper(z, s)) - case Concat(r1, r2) => assume(matchR(r, s) == matchZipper(z, s)) - } + // } + // } + // } + // case Star(rInner) => assume(matchR(r, s) == matchZipper(z, s)) + // case Concat(r1, r2) => assume(matchR(r, s) == matchZipper(z, s)) + // } - }.ensuring(_ => matchR(r, s) == matchZipper(z, s)) + // }.ensuring(_ => matchR(r, s) == matchZipper(z, s)) @ghost @opaque @@ -805,7 +806,7 @@ object ZipperRegex { assert(matchR(r, s) == matchR(Concat(Concat(rOne, rTwo), generalisedConcat(tlExp)), s)) lemmaConcatAssociative(rOne, rTwo, generalisedConcat(tlExp), s) - assert(matchR(Concat(Concat(rOne, rTwo), generalisedConcat(tlExp)), s) == matchR(Concat(rOne, Concat(rTwo, generalisedConcat(tlExp))), s)) // TODO + assert(matchR(Concat(Concat(rOne, rTwo), generalisedConcat(tlExp)), s) == matchR(Concat(rOne, Concat(rTwo, generalisedConcat(tlExp))), s)) assert(matchR(r, s) == matchZipper(z, s)) } @@ -839,14 +840,14 @@ object ZipperRegex { assert(matchR(r, s) == matchR(Concat(Concat(rOne, rTwo), generalisedConcat(tlExp)), s)) lemmaConcatAssociative(rOne, rTwo, generalisedConcat(tlExp), s) - assert(matchR(Concat(Concat(rOne, rTwo), generalisedConcat(tlExp)), s) == matchR(Concat(rOne, Concat(rTwo, generalisedConcat(tlExp))), s)) // TODO + assert(matchR(Concat(Concat(rOne, rTwo), generalisedConcat(tlExp)), s) == matchR(Concat(rOne, Concat(rTwo, generalisedConcat(tlExp))), s)) assert(matchR(r, s) == matchZipper(z, s)) } case Star(rInner) => { assert(zDerivDown == derivationStepZipperDown(rInner, Context(Cons(Star(rInner), tlExp)), shd)) - assume(matchR(r, s) == matchZipper(z, s)) // TODO + assume(matchR(r, s) == matchZipper(z, s)) } case _ => { @@ -929,25 +930,65 @@ object ZipperRegex { assert(unfocusZipper(List(Context(List(Star(rInner))))) == Star(rInner)) mainMatchTheorem(rR1, s) - if(matchR(rR1, s)){ - val (s1, s2) = findConcatSeparation(rInner, Star(rInner), Nil(), s, s).get + if(findConcatSeparationZippers(subZR1, subZR2, Nil(), s, s).isDefined){ + val (s1, s2) = findConcatSeparationZippers(subZR1, subZR2, Nil(), s, s).get assert(s == s1 ++ s2) - assert(matchR(rInner, s1)) - assert(matchR(Star(rInner), s2)) - - theoremZipperRegexEquivInductZ(subZR1, List(Context(List(rInner))), rInner, s1) - theoremZipperRegexEquivInductZ(subZR2, List(Context(List(Star(rInner)))), Star(rInner), s2) assert(matchZipper(subZR1, s1)) assert(matchZipper(subZR2, s2)) + theoremZipperRegexEquivInductZ(subZR1, List(Context(List(rInner))), rInner, s1) + theoremZipperRegexEquivInductZ(subZR2, List(Context(List(Star(rInner)))), Star(rInner), s2) lemmaConcatenateContextMatchesConcatOfStrings(Context(List(rInner)), Context(List(Star(rInner))), s1, s2) + lemmaTwoRegexMatchThenConcatMatchesConcatString(rInner, Star(rInner), s1, s2) check(matchR(r, s) == matchZipper(z, s)) } else{ - check(matchR(r, s) == matchZipper(z, s)) + if(matchR(rR1, s)){ + val (s1, s2) = findConcatSeparation(rInner, Star(rInner), Nil(), s, s).get + assert(s == s1 ++ s2) + assert(matchR(rInner, s1)) + assert(matchR(Star(rInner), s2)) + theoremZipperRegexEquivInductZ(subZR1, List(Context(List(rInner))), rInner, s1) + theoremZipperRegexEquivInductZ(subZR2, List(Context(List(Star(rInner)))), Star(rInner), s2) + assert(matchZipper(subZR1, s1)) + assert(matchZipper(subZR2, s2)) + lemmaZ1MatchesS1AndZ2MatchesS2ThenFindSeparationZipperFindsAtLeastThem(subZR1, subZR2, s1, s2, s, Nil(), s) + check(false) + } + check(!matchR(rR1, s)) + // I'm stuck here, I need to prove that if the findConcatSeparationZippers returns None, then the matchZipper(z, s) is false + check(!matchZipper(z, s)) } - - - + // if(matchR(rR1, s)){ + // val (s1, s2) = findConcatSeparation(rInner, Star(rInner), Nil(), s, s).get + // assert(s == s1 ++ s2) + // assert(matchR(rInner, s1)) + // assert(matchR(Star(rInner), s2)) + + // theoremZipperRegexEquivInductZ(subZR1, List(Context(List(rInner))), rInner, s1) + // theoremZipperRegexEquivInductZ(subZR2, List(Context(List(Star(rInner)))), Star(rInner), s2) + // assert(matchZipper(subZR1, s1)) + // assert(matchZipper(subZR2, s2)) + // lemmaConcatenateContextMatchesConcatOfStrings(Context(List(rInner)), Context(List(Star(rInner))), s1, s2) + // check(matchR(r, s) == matchZipper(z, s)) + // } else{ + // assert(!matchR(rR1, s)) + // if(matchZipper(z, s)){ + // lemmaConcatZipperMatchesStringThenFindConcatDefined(Context(List(rInner)), Context(List(Star(rInner))), s) + // val (s1, s2) = findConcatSeparationZippers(subZR1, subZR2, Nil(), s, s).get + // assert(s == s1 ++ s2) + // assert(matchZipper(subZR1, s1)) + // assert(matchZipper(subZR2, s2)) + // lemmaConcatenateContextMatchesConcatOfStrings(Context(List(rInner)), Context(List(Star(rInner))), s1, s2) + // theoremZipperRegexEquivInductZ(subZR1, List(Context(List(rInner))), rInner, s1) + // theoremZipperRegexEquivInductZ(subZR2, List(Context(List(Star(rInner)))), Star(rInner), s2) + // assert(matchR(rInner, s1)) + // assert(matchR(Star(rInner), s2)) + // lemmaTwoRegexMatchThenConcatMatchesConcatString(rInner, Star(rInner), s1, s2) + // check(matchR(rR1, s)) + // check(false) + // } + // check(!matchZipper(z, s)) + // } } } } @@ -978,6 +1019,35 @@ object ZipperRegex { }.ensuring(_ => matchR(r, s) == matchZipper(z, s)) + + /** Enumerate all cuts in s and returns one that works, i.e., z1 matches s1 and z2 matches s2 Specifically, it is the right most one, i.e., s2 is the largest, if multiple exist + * Returns None if no valid cut exists + * + * @param z1 + * @param z2 + * @param s1 + * @param s2 + * @param s + */ + @ghost + def findConcatSeparationZippers[C](z1: Zipper[C], z2: Zipper[C], s1: List[C], s2: List[C], s: List[C]): Option[(List[C], List[C])] = { + require(s1 ++ s2 == s) + decreases(s2.size) + + val res: Option[(List[C], List[C])] = (s1, s2) match { + case (_, _) if matchZipper(z1, s1) && matchZipper(z2, s2) => Some((s1, s2)) + case (_, Nil()) => None() + case (_, Cons(hd2, tl2)) => { + ListUtils.lemmaMoveElementToOtherListKeepsConcatEq(s1, hd2, tl2, s) + assert(s1 ++ List(hd2) ++ tl2 == s) + findConcatSeparationZippers(z1, z2, s1 ++ List(hd2), tl2, s) + } + } + res + + }.ensuring (res => (res.isDefined && matchZipper(z1, res.get._1) && matchZipper(z2, res.get._2) && res.get._1 ++ res.get._2 == s) || !res.isDefined) + + @ghost @opaque @inlineOnce @@ -1024,6 +1094,200 @@ object ZipperRegex { }.ensuring(_ => matchZipper(z1 ++ z2, s) == (matchZipper(z1, s) || matchZipper(z2, s))) + @ghost + @opaque + @inlineOnce + def lemmaZ1MatchesS1AndZ2MatchesS2ThenFindSeparationZipperFindsAtLeastThem[C](z1: Zipper[C], z2: Zipper[C], s1: List[C], s2: List[C], s: List[C], s1Rec: List[C], s2Rec: List[C]): Unit = { + require(matchZipper(z1, s1)) + require(matchZipper(z2, s2)) + require(s1 ++ s2 == s) + require(ListUtils.isPrefix(s1Rec, s1)) + require(s1Rec ++ s2Rec == s) + + decreases(s2Rec.size) + + (s1Rec, s2Rec) match { + case (_, _) if matchZipper(z1, s1Rec) && matchZipper(z2, s2Rec) => () + case (_, Nil()) => { + assert(s1Rec.size == s.size) + assert(s1Rec.size == s1.size) + assert(s1Rec == s1) + assert(s2Rec == s2) + assert(findConcatSeparationZippers(z1, z2, s1Rec, s2Rec, s) == Some(s1Rec, s2Rec)) + } + case (_, Cons(hd2, tl2)) => { + ListUtils.lemmaConcatTwoListThenFirstIsPrefix(s1, s2) + ListUtils.lemmaConcatTwoListThenFirstIsPrefix(s1Rec, s2Rec) + if (s1Rec == s1) { + ListUtils.lemmaConcatTwoListThenFirstIsPrefix(s1, s2) + ListUtils.lemmaSamePrefixThenSameSuffix(s1, s2, s1Rec, s2Rec, s) + check(false) + } + ListUtils.lemmaMoveElementToOtherListKeepsConcatEq(s1Rec, hd2, tl2, s) + ListUtils.lemmaConcatTwoListThenFirstIsPrefix(s1Rec ++ List(hd2), tl2) + if (s1Rec.size == s1.size) { + ListUtils.lemmaIsPrefixSameLengthThenSameList(s1, s1Rec, s) + check(false) + } + + ListUtils.lemmaPrefixFromSameListAndStrictlySmallerThenPrefixFromEachOther(s1, s1Rec ++ List(hd2), s) + lemmaZ1MatchesS1AndZ2MatchesS2ThenFindSeparationZipperFindsAtLeastThem(z1, z2, s1, s2, s, s1Rec ++ List(hd2), tl2) + } + } + }.ensuring(_ => findConcatSeparationZippers(z1, z2, s1Rec, s2Rec, s).isDefined) + + + // @ghost + // @opaque + // @inlineOnce + // def lemmaConcatZipperMatchesStringThenFindConcatDefined[C](ct1: Context[C], ct2: Context[C], s: List[C]):Unit = { + // require(matchZipper(Set(ct1.concat(ct2)), s)) + // decreases(ct1.exprs.size, contextDepthTotal(ct1), contextDepthTotal(ct2)) + // s match { + // case Cons(shd, stl) => { + // val zipper = Set(ct1.concat(ct2)) + // val ctx = ct1.concat(ct2) + // val deriv = derivationStepZipper(zipper, shd) + // val derivUp = derivationStepZipperUp(ctx, shd) + // SetUtils.lemmaFlatMapOnSingletonSet(zipper, ctx, (c: Context[C]) => derivationStepZipperUp(c, shd)) + // assert(deriv == derivUp) + // ct1.exprs match { + // case Cons(ct1Hd, ct1Tl) => { + // val ct1Tl = ct1.tail + // if(nullable(ct1Hd) ){ + // assert(derivUp == derivationStepZipperDown(ct1Hd, ct1Tl.concat(ct2), shd) ++ derivationStepZipperUp(ct1Tl.concat(ct2), shd)) + // lemmaZipperConcatMatchesSameAsBothZippers(derivationStepZipperDown(ct1Hd, ct1Tl.concat(ct2), shd), derivationStepZipperUp(ct1Tl.concat(ct2), shd), stl) + // assert(matchZipper(derivUp, stl) == matchZipper(derivationStepZipperDown(ct1Hd, ct1Tl.concat(ct2), shd), stl) || matchZipper(derivationStepZipperUp(ct1Tl.concat(ct2), shd), stl)) + // assert(matchZipper(derivUp, stl)) + // if(matchZipper(derivationStepZipperUp(ct1Tl.concat(ct2), shd), stl)){ + // SetUtils.lemmaFlatMapOnSingletonSet(Set(ct1Tl.concat(ct2)), ct1Tl.concat(ct2), (c: Context[C]) => derivationStepZipperUp(c, shd)) + // assert(derivationStepZipper(Set(ct1Tl.concat(ct2)), shd) == derivationStepZipperUp(ct1Tl.concat(ct2), shd)) + // assert(matchZipper(Set(ct1Tl.concat(ct2)), s)) + // lemmaConcatZipperMatchesStringThenFindConcatDefined(ct1Tl, ct2, s) + // check(findConcatSeparationZippers(Set(ct1Tl), Set(ct2), Nil(), s, s).isDefined) + // val (s1, s2) = findConcatSeparationZippers(Set(ct1Tl), Set(ct2), Nil(), s, s).get + // assert(matchZipper(Set(ct1Tl), s1)) + // assert(matchZipper(Set(ct2), s2)) + // assert(s1 ++ s2 == s) + // if(s1.isEmpty){ + // assert(matchZipper(Set(ct1Tl), s1)) + // assert(Set(ct1Tl).exists(c => nullableContext(c))) + // val witness = SetUtils.getWitness(Set(ct1Tl), (c: Context[C]) => nullableContext(c)) + // assert(nullableContext(ct1Tl)) + // assert(nullableContext(ct1)) + // SetUtils.lemmaContainsThenExists(Set(ct1Tl), ct1Tl, (c: Context[C]) => nullableContext(c)) + // SetUtils.lemmaContainsThenExists(Set(ct1), ct1, (c: Context[C]) => nullableContext(c)) + // assert(matchZipper(Set(ct1Tl), s1)) + // assert(matchZipper(Set(ct1), s1)) + // lemmaZ1MatchesS1AndZ2MatchesS2ThenFindSeparationZipperFindsAtLeastThem(Set(ct1), Set(ct2), s1, s2, s, Nil(), s) + // check(findConcatSeparationZippers(Set(ct1), Set(ct2), Nil(), s, s).isDefined) + // } else { + // SetUtils.lemmaFlatMapOnSingletonSet(Set(ct1), ct1, (c: Context[C]) => derivationStepZipperUp(c, shd)) + // assert(derivationStepZipper(Set(ct1), shd) == derivationStepZipperUp(ct1, shd)) + // assert(derivationStepZipperUp(ct1, shd) == derivationStepZipperDown(ct1Hd, ct1Tl, shd) ++ derivationStepZipperUp(ct1Tl, shd)) + // lemmaZipperConcatMatchesSameAsBothZippers(derivationStepZipperDown(ct1Hd, ct1Tl, shd), derivationStepZipperUp(ct1Tl, shd), s1.tail) + // assert(matchZipper(Set(ct1), s1) == matchZipper(derivationStepZipperDown(ct1Hd, ct1Tl, shd), s1.tail) || matchZipper(derivationStepZipperUp(ct1Tl, shd), s1.tail)) + + // SetUtils.lemmaFlatMapOnSingletonSet(Set(ct1Tl), ct1Tl, (c: Context[C]) => derivationStepZipperUp(c, shd)) + // assert(derivationStepZipper(Set(ct1Tl), s1.head) == derivationStepZipperUp(ct1Tl, s1.head)) + // assert(matchZipper(Set(ct1Tl), s1) == matchZipper(derivationStepZipperUp(ct1Tl, s1.head), s1.tail)) + // assert(matchZipper(derivationStepZipperUp(ct1Tl, s1.head), s1.tail)) + // assert(matchZipper(Set(ct1), s1)) + + // lemmaZ1MatchesS1AndZ2MatchesS2ThenFindSeparationZipperFindsAtLeastThem(Set(ct1), Set(ct2), s1, s2, s, Nil(), s) + // check(findConcatSeparationZippers(Set(ct1), Set(ct2), Nil(), s, s).isDefined) + // } + // } else { + // // ct1Hd is nullable but derivDown matches + // check(matchZipper(derivationStepZipperUp(ct1Tl.concat(ct2), shd), stl) == false) + // assert(matchZipper(derivationStepZipperDown(ct1Hd, ct1Tl.concat(ct2), shd), stl)) + + // val derivOnly1 = derivationStepZipper(Set(ct1), shd) + // val deriOnly1Up = derivationStepZipperUp(ct1, shd) + // SetUtils.lemmaFlatMapOnSingletonSet(Set(ct1), ct1, (c: Context[C]) => derivationStepZipperUp(c, shd)) + // assert(derivOnly1 == deriOnly1Up) + // assert(derivOnly1 == derivationStepZipperDown(ct1Hd, ct1Tl, shd) ++ derivationStepZipperUp(ct1Tl, shd)) + // lemmaZipperConcatMatchesSameAsBothZippers(derivationStepZipperDown(ct1Hd, ct1Tl, shd), derivationStepZipperUp(ct1Tl, shd), stl) + + // // Case: ct1Hd is nullable but derivDown matches + // assert(matchZipper(derivationStepZipperDown(ct1Hd, ct1Tl.concat(ct2), shd), stl)) + // ct1Hd match { + // case ElementMatch(c) if c == shd => { + // check(findConcatSeparationZippers(Set(ct1), Set(ct2), Nil(), s, s).isDefined) + // } + // case Union(rOne, rTwo) => { + // assert(derivationStepZipperDown(ct1Hd, ct1Tl.concat(ct2), shd) == derivationStepZipperDown(rOne, ct1Tl.concat(ct2), shd) ++ derivationStepZipperDown(rTwo, ct1Tl.concat(ct2), shd)) + // lemmaZipperConcatMatchesSameAsBothZippers(derivationStepZipperDown(rOne, ct1Tl.concat(ct2), shd), derivationStepZipperDown(rTwo, ct1Tl.concat(ct2), shd), stl) + // if(matchZipper(derivationStepZipperDown(rOne, ct1Tl.concat(ct2), shd), stl)){ + // assert(matchZipper(derivationStepZipperDown(rOne, ct1Tl.concat(ct2), shd), stl)) + // val newContext1 = ct1Tl.prepend(rOne) + // SetUtils.lemmaFlatMapOnSingletonSet(Set(newContext1), newContext1, (c: Context[C]) => derivationStepZipperUp(c, shd)) + // SetUtils.lemmaFlatMapOnSingletonSet(Set(newContext1.concat(ct2)), newContext1.concat(ct2), (c: Context[C]) => derivationStepZipperUp(c, shd)) + // if(nullable(rOne)){ + // val derivUp = derivationStepZipperUp(newContext1.concat(ct2), shd) + // assert(derivUp == derivationStepZipperDown(rOne, ct1Tl.concat(ct2), shd) ++ derivationStepZipperUp(ct1Tl.concat(ct2), shd)) + // lemmaZipperConcatMatchesSameAsBothZippers(derivationStepZipperDown(rOne, ct1Tl.concat(ct2), shd), derivationStepZipperUp(ct1Tl.concat(ct2), shd), stl) + // } + // lemmaConcatZipperMatchesStringThenFindConcatDefined(newContext1, ct2, s) + // val (s1, s2) = findConcatSeparationZippers(Set(newContext1), Set(ct2), Nil(), s, s).get + // assert(matchZipper(Set(newContext1), s1)) + // assert(matchZipper(Set(ct1), s1)) + // assert(matchZipper(Set(ct2), s2)) + // lemmaZ1MatchesS1AndZ2MatchesS2ThenFindSeparationZipperFindsAtLeastThem(Set(ct1), Set(ct2), s1, s2, s, Nil(), s) + // check(findConcatSeparationZippers(Set(ct1), Set(ct2), Nil(), s, s).isDefined) + // }else{ + // check(findConcatSeparationZippers(Set(ct1), Set(ct2), Nil(), s, s).isDefined) + // } + // } + // case Concat(rOne, rTwo) if nullable(rOne) => assume(findConcatSeparationZippers(Set(ct1), Set(ct2), Nil(), s, s).isDefined) + // case Concat(rOne, rTwo) => { + // assert(nullable(ct1Hd)) + // check(false) + // } + // case Star(rInner) => assume(findConcatSeparationZippers(Set(ct1), Set(ct2), Nil(), s, s).isDefined) + // case _ => check(findConcatSeparationZippers(Set(ct1), Set(ct2), Nil(), s, s).isDefined) + // } + + + // check(findConcatSeparationZippers(Set(ct1), Set(ct2), Nil(), s, s).isDefined) + // } + // } else { + // val derivDown = derivationStepZipperDown(ct1Hd, ct1Tl.concat(ct2), shd) + + // assert(deriv == derivUp) + // assert(matchZipper(derivationStepZipperDown(ct1Hd, ct1Tl.concat(ct2), shd), stl)) + + // // TODO + + + // check(findConcatSeparationZippers(Set(ct1), Set(ct2), Nil(), s, s).isDefined) + // } + // } + // case Nil() => { + // assert(ct1.concat(ct2) == ct2) + // assert(matchZipper(Set(ct2), s)) + // lemmaZipperOfEmptyContextMatchesEmptyString(Set(ct1), Nil()) + // assert(matchZipper(Set(ct1), Nil())) + // lemmaZ1MatchesS1AndZ2MatchesS2ThenFindSeparationZipperFindsAtLeastThem(Set(ct1), Set(ct2), Nil(), s, s, Nil(), s) + // } + // } + + // } + // case Nil() => { + // assert(nullableContext(ct1.concat(ct2))) + // assert(ct1.concat(ct2).exprs == ct1.exprs ++ ct2.exprs) + // ListUtils.lemmaConcatThenFirstSubseqOfTot(ct1.exprs, ct2.exprs) + // ListUtils.lemmaConcatThenSecondSubseqOfTot(ct1.exprs, ct2.exprs) + // ListUtils.lemmaContentSubsetPreservesForall(ct1.exprs ++ ct2.exprs, ct1.exprs, (r: Regex[C]) => nullable(r)) + // ListUtils.lemmaContentSubsetPreservesForall(ct1.exprs ++ ct2.exprs, ct2.exprs, (r: Regex[C]) => nullable(r)) + // assert(nullableContext(ct1) && nullableContext(ct2)) + // SetUtils.lemmaContainsThenExists(Set(ct1), ct1, (c: Context[C]) => nullableContext(c)) + // SetUtils.lemmaContainsThenExists(Set(ct2), ct2, (c: Context[C]) => nullableContext(c)) + // } + // } + + // }.ensuring( _ => findConcatSeparationZippers(Set(ct1), Set(ct2), Nil(), s, s).isDefined) + // LEMMAS ----------------------------------------------------------------------------------------------------- @@ -1097,36 +1361,214 @@ object ZipperRegex { decreases(s1.size, s2.size) val z1 = Set(ct1) val z2 = Set(ct2) - if(ct1.isEmpty){ - lemmaZipperOfEmptyContextMatchesEmptyString(Set(ct1), s1) - assert(s1.isEmpty) - assert(ct1.concat(ct2) == ct2) - assert(s1 ++ s2 == s2) - check(matchZipper(Set(ct1.concat(ct2)), s1 ++ s2)) - } else { - assert(matchZipper(z1, s1)) - s1 match { - case Cons(shd, stl) => { - assert(matchZipper(derivationStepZipper(z1, shd), stl)) - val z1Deriv = derivationStepZipper(z1, shd) - lemmaZipperMatchesExistsMatchingContext(z1Deriv.toList, stl) - assert(z1Deriv.exists(c => matchZipper(Set(c), stl))) - val witnessContext = SetUtils.getWitness(z1Deriv, (c: Context[C]) => matchZipper(Set(c), stl)) - lemmaConcatenateContextMatchesConcatOfStrings(witnessContext, ct2, stl, s2) - assert(matchZipper(Set(witnessContext.concat(ct2)), stl ++ s2)) - check(matchZipper(Set(ct1.concat(ct2)), s1 ++ s2)) - } - case Nil() => { - assert(nullableZipper(z1)) - val witness = SetUtils.getWitness(z1, (c: Context[C]) => nullableContext(c)) - assert(ct1 == witness) - assert(nullableContext(ct1)) + val concatenated = Set(ct1.concat(ct2)) + ct1.exprs match { + case Nil() => { + lemmaZipperOfEmptyContextMatchesEmptyString(Set(ct1), s1) + assert(s1.isEmpty) + assert(ct1.concat(ct2) == ct2) + assert(s1 ++ s2 == s2) + check(matchZipper(Set(ct1.concat(ct2)), s1 ++ s2)) + } + case Cons(ct1hd, ct1tl) => { + assert(matchZipper(z1, s1)) + s1 match { + case Cons(shd, stl) => { + assert(matchZipper(derivationStepZipper(z1, shd), stl)) + val z1Deriv = derivationStepZipper(z1, shd) + val concatenatedDeriv = derivationStepZipper(concatenated, shd) + lemmaZipperMatchesExistsMatchingContext(z1Deriv.toList, stl) + assert(z1Deriv.exists(c => matchZipper(Set(c), stl))) + val witnessContext = SetUtils.getWitness(z1Deriv, (c: Context[C]) => matchZipper(Set(c), stl)) + lemmaConcatenateContextMatchesConcatOfStrings(witnessContext, ct2, stl, s2) + + assert(matchZipper(Set(witnessContext.concat(ct2)), stl ++ s2)) + + // Here the idea is to prove that witnessContext.concat(ct2) is contained in the derivativeStep of ct1.concat(ct2) + // And then to use another lemma that states that if a zipper contains a context that matches alone, then the zipper + // matches the string + + val z1DerivUp = derivationStepZipperUp(ct1, shd) + val z1DerivDown = derivationStepZipperDown(ct1hd, ct1.tail, shd) + val concatenatedDerivUp = derivationStepZipperUp(ct1.concat(ct2), shd) + val concatenatedDerivDown = derivationStepZipperDown(ct1hd, ct1.concat(ct2).tail, shd) + SetUtils.lemmaFlatMapOnSingletonSet(z1, ct1, (c: Context[C]) => derivationStepZipperUp(c, shd)) + SetUtils.lemmaFlatMapOnSingletonSet(concatenated, ct1.concat(ct2), (c: Context[C]) => derivationStepZipperUp(c, shd)) + + if(nullable(ct1hd)){ + assert(z1DerivUp == z1DerivDown ++ derivationStepZipperUp(ct1.tail, shd)) + assert(z1Deriv == z1DerivUp) + assert(concatenatedDerivUp == concatenatedDerivDown ++ derivationStepZipperUp(ct1.concat(ct2).tail, shd)) + assert(concatenatedDeriv == concatenatedDerivUp) + lemmaZipperConcatMatchesSameAsBothZippers(z1DerivDown, derivationStepZipperUp(ct1.tail, shd), stl) + lemmaZipperConcatMatchesSameAsBothZippers(concatenatedDerivDown, derivationStepZipperUp(ct1.concat(ct2).tail, shd), stl ++ s2) + assert(matchZipper(z1Deriv, stl) == matchZipper(z1, s1)) + assert(matchZipper(z1, s1) == (matchZipper(z1DerivDown, stl) || matchZipper(derivationStepZipperUp(ct1.tail, shd), stl))) + assert(matchZipper(concatenatedDeriv, stl) == matchZipper(concatenated, s1)) + } else { + assert(z1DerivUp == z1DerivDown) + assert(z1Deriv == z1DerivDown) + assert(matchZipper(z1DerivDown, stl) == matchZipper(z1, s1)) + } + + check((s1 ++ s2).tail == stl ++ s2) + lemmaDerivContainsCtxtThenConcatDerivContainsConcatCtx(ct1, ct2, witnessContext, shd) + check(concatenatedDeriv.contains(witnessContext.concat(ct2))) + SetUtils.lemmaContainsThenExists(concatenatedDeriv, witnessContext.concat(ct2), (c: Context[C]) => matchZipper(Set(c), stl ++ s2)) + check(concatenatedDeriv.exists(c => matchZipper(Set(c), stl ++ s2))) + lemmaExistsMatchingContextThenMatchingString(concatenatedDeriv.toList, stl ++ s2) + check(matchZipper(concatenatedDeriv, stl ++ s2)) + + check(matchZipper(Set(ct1.concat(ct2)), s1 ++ s2)) + } + case Nil() => { + assert(nullableZipper(z1)) + val witness = SetUtils.getWitness(z1, (c: Context[C]) => nullableContext(c)) + assert(ct1 == witness) + assert(nullableContext(ct1)) + assert(matchZipper(z2, s2)) + lemmaPrependingNullableCtxStillMatches(ct1, ct2, s2) + } } } } }.ensuring(_ => matchZipper(Set(ct1.concat(ct2)), s1 ++ s2)) + @ghost + @opaque + @inlineOnce + def lemmaPrependingNullableCtxStillMatches[C](ct1: Context[C], ct2: Context[C], s: List[C]): Unit = { + require(matchZipper(Set(ct2), s)) + require(nullableContext(ct1)) + decreases(ct1.exprs.size) + s match { + case Cons(shd, stl) => { + // We need to prove that a nullable context followed by another context contains the other context derivative + // when deriving as the nullable context will be consumed in derivUp + val concatDerivUp = derivationStepZipperUp(ct1.concat(ct2), shd) + SetUtils.lemmaFlatMapOnSingletonSet(Set(ct1), ct1, (c: Context[C]) => derivationStepZipperUp(c, shd)) + SetUtils.lemmaFlatMapOnSingletonSet(Set(ct1.concat(ct2)), ct1.concat(ct2), (c: Context[C]) => derivationStepZipperUp(c, shd)) + assert(concatDerivUp == derivationStepZipper(Set(ct1.concat(ct2)), shd)) + ct1.exprs match { + case Cons(ct1hd, ct1tl) => { + val ct1tl = ct1.tail + val derivUp = derivationStepZipperUp(ct1, shd) + assert(nullable(ct1hd)) + assert(derivUp == derivationStepZipperDown(ct1hd, ct1tl, shd) ++ derivationStepZipperUp(ct1tl, shd)) + assert(concatDerivUp == derivationStepZipperDown(ct1hd, ct1tl.concat(ct2), shd) ++ derivationStepZipperUp(ct1tl.concat(ct2), shd)) + lemmaPrependingNullableCtxStillMatches(ct1tl, ct2, s) + check(matchZipper(Set(ct1tl.concat(ct2)), s)) + SetUtils.lemmaFlatMapOnSingletonSet(Set(ct1tl.concat(ct2)), ct1tl.concat(ct2), (c: Context[C]) => derivationStepZipperUp(c, shd)) + assert(matchZipper(derivationStepZipperUp(ct1tl.concat(ct2), shd), stl)) + lemmaZipperConcatMatchesSameAsBothZippers(derivationStepZipperDown(ct1hd, ct1tl.concat(ct2), shd), derivationStepZipperUp(ct1tl.concat(ct2), shd), stl) + check(matchZipper(Set(ct1.concat(ct2)), s)) + } + case Nil() => check(matchZipper(Set(ct1.concat(ct2)), s)) + } + + } + case Nil() => + assert(nullableContext(ct2)) + ListUtils.lemmaConcatPreservesForall(ct1.exprs, ct2.exprs, (r: Regex[C]) => nullable(r)) + check(matchZipper(Set(ct1.concat(ct2)), s)) + } + + }.ensuring(_ => matchZipper(Set(ct1.concat(ct2)), s)) + + + @ghost + @opaque + @inlineOnce + def lemmaDerivContainsCtxtThenConcatDerivContainsConcatCtx[C](ct1: Context[C], ct2: Context[C], cWitness: Context[C], c: C): Unit = { + require(derivationStepZipperUp(ct1, c).contains(cWitness)) + decreases(ct1.exprs.size, contextDepthTotal(ct1)) + val concatCtx = ct1.concat(ct2) + ct1.exprs match{ + case Cons(hd, _) => { + val tl = ct1.tail + val derivUp = derivationStepZipperUp(ct1, c) + val derivDown = derivationStepZipperDown(hd, tl, c) + if(nullable(hd)){ + assert(derivUp == derivDown ++ derivationStepZipperUp(tl, c)) + assert(derivDown.contains(cWitness) || derivationStepZipperUp(tl, c).contains(cWitness)) + if(derivationStepZipperUp(tl,c).contains(cWitness)) { + lemmaDerivContainsCtxtThenConcatDerivContainsConcatCtx(tl, ct2, cWitness, c) + check(derivationStepZipperUp(concatCtx, c).contains(cWitness.concat(ct2))) + } else{ + val concatDerivDown = derivationStepZipperDown(hd, concatCtx.tail, c) + lemmaDerivContainsCtxtThenConcatDerivContainsConcatCtxDerivDown(hd, tl, ct2, cWitness, c) + check(concatDerivDown.contains(cWitness.concat(ct2))) + } + } else { + assert(derivUp == derivDown) + assert(derivDown.contains(cWitness)) + val concatDerivDown = derivationStepZipperDown(hd, concatCtx.tail, c) + lemmaDerivContainsCtxtThenConcatDerivContainsConcatCtxDerivDown(hd, tl, ct2, cWitness, c) + check(concatDerivDown.contains(cWitness.concat(ct2))) + } + + } + case Nil() => check(false) + } + }.ensuring(_ => derivationStepZipperUp(ct1.concat(ct2), c).contains(cWitness.concat(ct2))) + + @ghost + @opaque + @inlineOnce + def lemmaDerivContainsCtxtThenConcatDerivContainsConcatCtxDerivDown[C](r1: Regex[C], ct1: Context[C], ct2: Context[C], cWitness: Context[C], c: C): Unit = { + require(validRegex(r1)) + require(derivationStepZipperDown(r1, ct1, c).contains(cWitness)) + decreases(regexDepth(r1)) + val concatCtx = ct1.concat(ct2) + + val derivDown = derivationStepZipperDown(r1, ct1, c) + val concatDerivDown = derivationStepZipperDown(r1, concatCtx, c) + + r1 match { + case ElementMatch(cc) if c == cc => { + assert(derivDown == Set(ct1)) + assert(Set(ct1).contains(cWitness)) + assert(cWitness == ct1) + assert(concatDerivDown == Set(ct1.concat(ct2))) + check(concatDerivDown.contains(cWitness.concat(ct2))) + } + case Union(rOne, rTwo) => { + assert(derivDown == derivationStepZipperDown(rOne, ct1, c) ++ derivationStepZipperDown(rTwo, ct1, c)) + if(derivationStepZipperDown(rOne, ct1, c).contains(cWitness)){ + lemmaDerivContainsCtxtThenConcatDerivContainsConcatCtxDerivDown(rOne, ct1, ct2, cWitness, c) + check(concatDerivDown.contains(cWitness.concat(ct2))) + } else { + lemmaDerivContainsCtxtThenConcatDerivContainsConcatCtxDerivDown(rTwo, ct1, ct2, cWitness, c) + check(concatDerivDown.contains(cWitness.concat(ct2))) + } + } + case Concat(rOne, rTwo) if nullable(rOne) => { + assert(derivDown == derivationStepZipperDown(rOne, ct1.prepend(rTwo), c) ++ derivationStepZipperDown(rTwo, ct1, c)) + if(derivationStepZipperDown(rOne, ct1.prepend(rTwo), c).contains(cWitness)) { + lemmaDerivContainsCtxtThenConcatDerivContainsConcatCtxDerivDown(rOne, ct1.prepend(rTwo), ct2, cWitness, c) + check(concatDerivDown.contains(cWitness.concat(ct2))) + } else { + lemmaDerivContainsCtxtThenConcatDerivContainsConcatCtxDerivDown(rTwo, ct1, ct2, cWitness, c) + check(concatDerivDown.contains(cWitness.concat(ct2))) + } + } + case Concat(rOne, rTwo) => { + assert(derivDown == derivationStepZipperDown(rOne, ct1.prepend(rTwo), c)) + lemmaDerivContainsCtxtThenConcatDerivContainsConcatCtxDerivDown(rOne, ct1.prepend(rTwo), ct2, cWitness, c) + check(concatDerivDown.contains(cWitness.concat(ct2))) + } + case Star(rInner) => { + assert(derivDown == derivationStepZipperDown(rInner, ct1.prepend(Star(rInner)), c)) + lemmaDerivContainsCtxtThenConcatDerivContainsConcatCtxDerivDown(rInner, ct1.prepend(Star(rInner)), ct2, cWitness, c) + check(concatDerivDown.contains(cWitness.concat(ct2))) + } + case _ => + } + + }.ensuring(_ => derivationStepZipperDown(r1, ct1.concat(ct2), c).contains(cWitness.concat(ct2))) + + @ghost @opaque @inlineOnce @@ -1155,6 +1597,28 @@ object ZipperRegex { } }.ensuring(_ => zl.exists(c => matchZipper(Set(c), s))) + @ghost + @opaque + @inlineOnce + def lemmaExistsMatchingContextThenMatchingString[C](zl: List[Context[C]], s: List[C]): Unit = { + require(zl.exists(c => matchZipper(Set(c), s))) + decreases(zl.size) + zl match { + case Cons(ctHd, ctTl) => { + val zHd = Set(ctHd) + lemmaZipperConcatMatchesSameAsBothZippers(zHd, ctTl.content, s) + if(matchZipper(zHd, s)){ + check(matchZipper(zl.content, s)) + } else { + lemmaExistsMatchingContextThenMatchingString(ctTl, s) + check(matchZipper(zl.content, s)) + } + } + case Nil() => + check(false) + } + }.ensuring(_ => matchZipper(zl.content, s)) + @ghost @opaque diff --git a/lexers/regex/verifiedlexer/stainless.conf b/lexers/regex/verifiedlexer/stainless.conf index 21f1c5b1..960e9600 100644 --- a/lexers/regex/verifiedlexer/stainless.conf +++ b/lexers/regex/verifiedlexer/stainless.conf @@ -3,7 +3,7 @@ vc-cache = true # debug = ["verification", "smt"] -timeout = 5 +timeout = 20 check-models = false print-ids = false print-types = false From f6d82ee42d9f3f9d8124e02b80a528598d4a24d1 Mon Sep 17 00:00:00 2001 From: Samuel Chassot Date: Fri, 22 Nov 2024 11:58:23 +0100 Subject: [PATCH 56/78] working on zipper --- .../src/main/scala/ch/epfl/lexer/Utils.scala | 25 ++ .../scala/ch/epfl/lexer/VerifiedRegex.scala | 262 ++++++++++++++---- lexers/regex/verifiedlexer/stainless.conf | 2 +- 3 files changed, 237 insertions(+), 52 deletions(-) diff --git a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/Utils.scala b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/Utils.scala index 65768172..dd346633 100644 --- a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/Utils.scala +++ b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/Utils.scala @@ -147,6 +147,31 @@ object SetUtils { }.ensuring(_ => l2.forall(l1.contains)) + @opaque + @ghost + @inlineOnce + def lemmaMapOnSingletonSet[A, B](s: Set[A], elmt: A, f: A => B): Unit = { + require(s == Set(elmt)) + val smap = s.map(f) + smap.toList match { + case Cons(hd, tl) => { + unfold(s.mapPost2(f)(hd)) + tl match{ + case Cons(h, t) => { + unfold(s.mapPost2(f)(h)) + check(false) + } + case Nil() => () + } + + } + case Nil() => { + unfold(s.mapPost1(f)(elmt)) + check(false) + } + } + // unfold(s.mapPost2(f)(f(elmt))) + }.ensuring(_ => s.map(f) == Set(f(elmt))) @opaque @ghost diff --git a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedRegex.scala b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedRegex.scala index 0e7a9fae..a786f75a 100644 --- a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedRegex.scala +++ b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedRegex.scala @@ -422,6 +422,10 @@ object ZipperRegex { if (input.isEmpty) nullableZipper(z) else matchZipper(derivationStepZipper(z, input.head), input.tail) } + def appendTo[C](z: Zipper[C], c: Context[C]): Zipper[C] = { + z.map(cz => cz.concat(c)) + } + // PROOFS ----------------------------------------------------------------------------------------------------- @@ -930,65 +934,69 @@ object ZipperRegex { assert(unfocusZipper(List(Context(List(Star(rInner))))) == Star(rInner)) mainMatchTheorem(rR1, s) - if(findConcatSeparationZippers(subZR1, subZR2, Nil(), s, s).isDefined){ - val (s1, s2) = findConcatSeparationZippers(subZR1, subZR2, Nil(), s, s).get - assert(s == s1 ++ s2) - assert(matchZipper(subZR1, s1)) - assert(matchZipper(subZR2, s2)) - theoremZipperRegexEquivInductZ(subZR1, List(Context(List(rInner))), rInner, s1) - theoremZipperRegexEquivInductZ(subZR2, List(Context(List(Star(rInner)))), Star(rInner), s2) - lemmaConcatenateContextMatchesConcatOfStrings(Context(List(rInner)), Context(List(Star(rInner))), s1, s2) - lemmaTwoRegexMatchThenConcatMatchesConcatString(rInner, Star(rInner), s1, s2) - check(matchR(r, s) == matchZipper(z, s)) - } else{ - if(matchR(rR1, s)){ - val (s1, s2) = findConcatSeparation(rInner, Star(rInner), Nil(), s, s).get - assert(s == s1 ++ s2) - assert(matchR(rInner, s1)) - assert(matchR(Star(rInner), s2)) - theoremZipperRegexEquivInductZ(subZR1, List(Context(List(rInner))), rInner, s1) - theoremZipperRegexEquivInductZ(subZR2, List(Context(List(Star(rInner)))), Star(rInner), s2) - assert(matchZipper(subZR1, s1)) - assert(matchZipper(subZR2, s2)) - lemmaZ1MatchesS1AndZ2MatchesS2ThenFindSeparationZipperFindsAtLeastThem(subZR1, subZR2, s1, s2, s, Nil(), s) - check(false) - } - check(!matchR(rR1, s)) - // I'm stuck here, I need to prove that if the findConcatSeparationZippers returns None, then the matchZipper(z, s) is false - check(!matchZipper(z, s)) - } - - // if(matchR(rR1, s)){ - // val (s1, s2) = findConcatSeparation(rInner, Star(rInner), Nil(), s, s).get + // if(findConcatSeparationZippers(subZR1, subZR2, Nil(), s, s).isDefined){ + // val (s1, s2) = findConcatSeparationZippers(subZR1, subZR2, Nil(), s, s).get // assert(s == s1 ++ s2) - // assert(matchR(rInner, s1)) - // assert(matchR(Star(rInner), s2)) - - // theoremZipperRegexEquivInductZ(subZR1, List(Context(List(rInner))), rInner, s1) - // theoremZipperRegexEquivInductZ(subZR2, List(Context(List(Star(rInner)))), Star(rInner), s2) // assert(matchZipper(subZR1, s1)) // assert(matchZipper(subZR2, s2)) + // theoremZipperRegexEquivInductZ(subZR1, List(Context(List(rInner))), rInner, s1) + // theoremZipperRegexEquivInductZ(subZR2, List(Context(List(Star(rInner)))), Star(rInner), s2) // lemmaConcatenateContextMatchesConcatOfStrings(Context(List(rInner)), Context(List(Star(rInner))), s1, s2) + // lemmaTwoRegexMatchThenConcatMatchesConcatString(rInner, Star(rInner), s1, s2) // check(matchR(r, s) == matchZipper(z, s)) // } else{ - // assert(!matchR(rR1, s)) - // if(matchZipper(z, s)){ - // lemmaConcatZipperMatchesStringThenFindConcatDefined(Context(List(rInner)), Context(List(Star(rInner))), s) - // val (s1, s2) = findConcatSeparationZippers(subZR1, subZR2, Nil(), s, s).get - // assert(s == s1 ++ s2) - // assert(matchZipper(subZR1, s1)) - // assert(matchZipper(subZR2, s2)) - // lemmaConcatenateContextMatchesConcatOfStrings(Context(List(rInner)), Context(List(Star(rInner))), s1, s2) - // theoremZipperRegexEquivInductZ(subZR1, List(Context(List(rInner))), rInner, s1) - // theoremZipperRegexEquivInductZ(subZR2, List(Context(List(Star(rInner)))), Star(rInner), s2) - // assert(matchR(rInner, s1)) - // assert(matchR(Star(rInner), s2)) - // lemmaTwoRegexMatchThenConcatMatchesConcatString(rInner, Star(rInner), s1, s2) - // check(matchR(rR1, s)) - // check(false) + // if(matchR(rR1, s)){ + // val (s1, s2) = findConcatSeparation(rInner, Star(rInner), Nil(), s, s).get + // assert(s == s1 ++ s2) + // assert(matchR(rInner, s1)) + // assert(matchR(Star(rInner), s2)) + // theoremZipperRegexEquivInductZ(subZR1, List(Context(List(rInner))), rInner, s1) + // theoremZipperRegexEquivInductZ(subZR2, List(Context(List(Star(rInner)))), Star(rInner), s2) + // assert(matchZipper(subZR1, s1)) + // assert(matchZipper(subZR2, s2)) + // lemmaZ1MatchesS1AndZ2MatchesS2ThenFindSeparationZipperFindsAtLeastThem(subZR1, subZR2, s1, s2, s, Nil(), s) + // check(false) // } + // check(!matchR(rR1, s)) + // assert(appendTo(subZR1, Context(List(Star(rInner)))) == Set(Context(List(rInner, Star(rInner))))) + // lemmaConcatZipperMatchesStringThenFindConcatDefined(subZR1, Context(List(Star(rInner))), s) + // // I'm stuck here, I need to prove that if the findConcatSeparationZippers returns None, then the matchZipper(z, s) is false // check(!matchZipper(z, s)) // } + + if(matchR(rR1, s)){ + val (s1, s2) = findConcatSeparation(rInner, Star(rInner), Nil(), s, s).get + assert(s == s1 ++ s2) + assert(matchR(rInner, s1)) + assert(matchR(Star(rInner), s2)) + + theoremZipperRegexEquivInductZ(subZR1, List(Context(List(rInner))), rInner, s1) + theoremZipperRegexEquivInductZ(subZR2, List(Context(List(Star(rInner)))), Star(rInner), s2) + assert(matchZipper(subZR1, s1)) + assert(matchZipper(subZR2, s2)) + lemmaConcatenateContextMatchesConcatOfStrings(Context(List(rInner)), Context(List(Star(rInner))), s1, s2) + check(matchR(r, s) == matchZipper(z, s)) + } else{ + assert(!matchR(rR1, s)) + if(matchZipper(z, s)){ + SetUtils.lemmaMapOnSingletonSet(subZR1, Context(List(rInner)), c => c.concat(Context(List(Star(rInner))))) + assert(appendTo(subZR1, Context(List(Star(rInner)))) == Set(Context(List(rInner, Star(rInner))))) + lemmaConcatZipperMatchesStringThenFindConcatDefined(subZR1, Context(List(Star(rInner))), s) + val (s1, s2) = findConcatSeparationZippers(subZR1, subZR2, Nil(), s, s).get + assert(s == s1 ++ s2) + assert(matchZipper(subZR1, s1)) + assert(matchZipper(subZR2, s2)) + lemmaConcatenateContextMatchesConcatOfStrings(Context(List(rInner)), Context(List(Star(rInner))), s1, s2) + theoremZipperRegexEquivInductZ(subZR1, List(Context(List(rInner))), rInner, s1) + theoremZipperRegexEquivInductZ(subZR2, List(Context(List(Star(rInner)))), Star(rInner), s2) + assert(matchR(rInner, s1)) + assert(matchR(Star(rInner), s2)) + lemmaTwoRegexMatchThenConcatMatchesConcatString(rInner, Star(rInner), s1, s2) + check(matchR(rR1, s)) + check(false) + } + check(!matchZipper(z, s)) + } } } } @@ -1047,7 +1055,6 @@ object ZipperRegex { }.ensuring (res => (res.isDefined && matchZipper(z1, res.get._1) && matchZipper(z2, res.get._2) && res.get._1 ++ res.get._2 == s) || !res.isDefined) - @ghost @opaque @inlineOnce @@ -1137,6 +1144,159 @@ object ZipperRegex { }.ensuring(_ => findConcatSeparationZippers(z1, z2, s1Rec, s2Rec, s).isDefined) + @ghost // TODO finish this lemma + @opaque + @inlineOnce + def lemmaConcatZipperMatchesStringThenFindConcatDefined[C](z1: Zipper[C], ct2: Context[C], s: List[C]): Unit = { + require(matchZipper(appendTo(z1, ct2), s)) + decreases(s) + val zipperTot = appendTo(z1, ct2) + s match { + case Cons(shd, stl) => { + assert(matchZipper(zipperTot, s)) + lemmaZipperMatchesExistsMatchingContext(zipperTot.toList, s) + val matchingContext = SetUtils.getWitness(zipperTot, c => matchZipper(Set(c), s)) + assert(matchZipper(Set(matchingContext), s)) + assert(zipperTot.contains(matchingContext)) + val matchingContextBeforeConcat = z1.mapPost2(c => c.concat(ct2))(matchingContext) + assert(matchingContextBeforeConcat.concat(ct2) == matchingContext) + + val ct1 = matchingContextBeforeConcat + val zipper = Set(ct1.concat(ct2)) + assert(zipper == Set(matchingContext)) + val ctx = ct1.concat(ct2) + val deriv = derivationStepZipper(zipper, shd) + val derivUp = derivationStepZipperUp(ctx, shd) + SetUtils.lemmaFlatMapOnSingletonSet(zipper, ctx, (c: Context[C]) => derivationStepZipperUp(c, shd)) + assert(deriv == derivUp) + ct1.exprs match { + case Cons(ct1Hd, ct1Tl) => { + val ct1Tl = ct1.tail + if(nullable(ct1Hd) ){ + assert(derivUp == derivationStepZipperDown(ct1Hd, ct1Tl.concat(ct2), shd) ++ derivationStepZipperUp(ct1Tl.concat(ct2), shd)) + lemmaZipperConcatMatchesSameAsBothZippers(derivationStepZipperDown(ct1Hd, ct1Tl.concat(ct2), shd), derivationStepZipperUp(ct1Tl.concat(ct2), shd), stl) + assert(matchZipper(derivUp, stl) == matchZipper(derivationStepZipperDown(ct1Hd, ct1Tl.concat(ct2), shd), stl) || matchZipper(derivationStepZipperUp(ct1Tl.concat(ct2), shd), stl)) + assert(matchZipper(derivUp, stl)) + if(matchZipper(derivationStepZipperUp(ct1Tl.concat(ct2), shd), stl)){ // Can be simplified now that we can call on zipper, we'll do + // SetUtils.lemmaFlatMapOnSingletonSet(Set(ct1Tl.concat(ct2)), ct1Tl.concat(ct2), (c: Context[C]) => derivationStepZipperUp(c, shd)) + // assert(derivationStepZipper(Set(ct1Tl.concat(ct2)), shd) == derivationStepZipperUp(ct1Tl.concat(ct2), shd)) + // assert(matchZipper(Set(ct1Tl.concat(ct2)), s)) + // lemmaConcatZipperMatchesStringThenFindConcatDefined(Set(ct1Tl), ct2, s) + // check(findConcatSeparationZippers(Set(ct1Tl), Set(ct2), Nil(), s, s).isDefined) + // val (s1, s2) = findConcatSeparationZippers(Set(ct1Tl), Set(ct2), Nil(), s, s).get + // assert(matchZipper(Set(ct1Tl), s1)) + // assert(matchZipper(Set(ct2), s2)) + // assert(s1 ++ s2 == s) + // if(s1.isEmpty){ + // assert(matchZipper(Set(ct1Tl), s1)) + // assert(Set(ct1Tl).exists(c => nullableContext(c))) + // val witness = SetUtils.getWitness(Set(ct1Tl), (c: Context[C]) => nullableContext(c)) + // assert(nullableContext(ct1Tl)) + // assert(nullableContext(ct1)) + // SetUtils.lemmaContainsThenExists(Set(ct1Tl), ct1Tl, (c: Context[C]) => nullableContext(c)) + // SetUtils.lemmaContainsThenExists(Set(ct1), ct1, (c: Context[C]) => nullableContext(c)) + // assert(matchZipper(Set(ct1Tl), s1)) + // assert(matchZipper(Set(ct1), s1)) + // lemmaZ1MatchesS1AndZ2MatchesS2ThenFindSeparationZipperFindsAtLeastThem(Set(ct1), Set(ct2), s1, s2, s, Nil(), s) + // check(findConcatSeparationZippers(Set(ct1), Set(ct2), Nil(), s, s).isDefined) + // } else { + // SetUtils.lemmaFlatMapOnSingletonSet(Set(ct1), ct1, (c: Context[C]) => derivationStepZipperUp(c, shd)) + // assert(derivationStepZipper(Set(ct1), shd) == derivationStepZipperUp(ct1, shd)) + // assert(derivationStepZipperUp(ct1, shd) == derivationStepZipperDown(ct1Hd, ct1Tl, shd) ++ derivationStepZipperUp(ct1Tl, shd)) + // lemmaZipperConcatMatchesSameAsBothZippers(derivationStepZipperDown(ct1Hd, ct1Tl, shd), derivationStepZipperUp(ct1Tl, shd), s1.tail) + // assert(matchZipper(Set(ct1), s1) == matchZipper(derivationStepZipperDown(ct1Hd, ct1Tl, shd), s1.tail) || matchZipper(derivationStepZipperUp(ct1Tl, shd), s1.tail)) + + // SetUtils.lemmaFlatMapOnSingletonSet(Set(ct1Tl), ct1Tl, (c: Context[C]) => derivationStepZipperUp(c, shd)) + // assert(derivationStepZipper(Set(ct1Tl), s1.head) == derivationStepZipperUp(ct1Tl, s1.head)) + // assert(matchZipper(Set(ct1Tl), s1) == matchZipper(derivationStepZipperUp(ct1Tl, s1.head), s1.tail)) + // assert(matchZipper(derivationStepZipperUp(ct1Tl, s1.head), s1.tail)) + // assert(matchZipper(Set(ct1), s1)) + + // lemmaZ1MatchesS1AndZ2MatchesS2ThenFindSeparationZipperFindsAtLeastThem(Set(ct1), Set(ct2), s1, s2, s, Nil(), s) + // check(findConcatSeparationZippers(Set(ct1), Set(ct2), Nil(), s, s).isDefined) + // } + + check(findConcatSeparationZippers(z1, Set(ct2), Nil(), s, s).isDefined) + } else { + // ct1Hd is nullable but derivDown matches + check(matchZipper(derivationStepZipperUp(ct1Tl.concat(ct2), shd), stl) == false) + assert(matchZipper(derivationStepZipperDown(ct1Hd, ct1Tl.concat(ct2), shd), stl)) + + assert(derivationStepZipperDown(ct1Hd, ct1Tl.concat(ct2), shd) == appendTo(derivationStepZipperDown(ct1Hd, ct1Tl, shd), ct2)) // TODO lemma + lemmaConcatZipperMatchesStringThenFindConcatDefined(derivationStepZipperDown(ct1Hd, ct1Tl, shd), ct2, stl) + + assert(findConcatSeparationZippers(derivationStepZipperDown(ct1Hd, ct1Tl, shd), Set(ct2), Nil(), stl, stl).isDefined) + val (s1, s2) = findConcatSeparationZippers(derivationStepZipperDown(ct1Hd, ct1Tl, shd), Set(ct2), Nil(), stl, stl).get + assume(matchZipper(derivationStepZipperDown(ct1Hd, ct1Tl, shd), s1)) + assume(matchZipper(Set(ct2), s2)) + assert(s1 ++ s2 == stl) + + val derivOnly1 = derivationStepZipper(Set(ct1), shd) + val deriOnly1Up = derivationStepZipperUp(ct1, shd) + SetUtils.lemmaFlatMapOnSingletonSet(Set(ct1), ct1, (c: Context[C]) => derivationStepZipperUp(c, shd)) + assert(derivOnly1 == deriOnly1Up) + assert(derivOnly1 == derivationStepZipperDown(ct1Hd, ct1Tl, shd) ++ derivationStepZipperUp(ct1Tl, shd)) + lemmaZipperConcatMatchesSameAsBothZippers(derivationStepZipperDown(ct1Hd, ct1Tl, shd), derivationStepZipperUp(ct1Tl, shd), s1) + assert(matchZipper(Set(ct1), Cons(shd, s1)) == matchZipper(derivationStepZipperDown(ct1Hd, ct1Tl, shd), s1) || matchZipper(derivationStepZipperUp(ct1Tl, shd), s1)) + assert(matchZipper(Set(ct1), Cons(shd, s1))) + + + lemmaZ1MatchesS1AndZ2MatchesS2ThenFindSeparationZipperFindsAtLeastThem(Set(ct1), Set(ct2), Cons(shd, s1), s2, s, Nil(), s) + check(findConcatSeparationZippers(Set(ct1), Set(ct2), Nil(), s, s).isDefined) + SetUtils.lemmaContainsThenExists(z1, ct1, (c: Context[C]) => matchZipper(Set(c), s)) + lemmaExistsMatchingContextThenMatchingString(z1.toList, Cons(shd, s1)) + lemmaZ1MatchesS1AndZ2MatchesS2ThenFindSeparationZipperFindsAtLeastThem(z1, Set(ct2), s1, s2, stl, Nil(), stl) + check(findConcatSeparationZippers(z1, Set(ct2), Nil(), s, s).isDefined) + } + } else { + val derivDown = derivationStepZipperDown(ct1Hd, ct1Tl.concat(ct2), shd) + + assert(deriv == derivUp) + assert(matchZipper(derivationStepZipperDown(ct1Hd, ct1Tl.concat(ct2), shd), stl)) + + // TODO + + + check(findConcatSeparationZippers(Set(ct1), Set(ct2), Nil(), s, s).isDefined) + check(findConcatSeparationZippers(z1, Set(ct2), Nil(), s, s).isDefined) + } + } + case Nil() => { + assert(ct1.concat(ct2) == ct2) + assert(matchZipper(Set(ct2), s)) + lemmaZipperOfEmptyContextMatchesEmptyString(Set(ct1), Nil()) + assert(matchZipper(Set(ct1), Nil())) + lemmaZ1MatchesS1AndZ2MatchesS2ThenFindSeparationZipperFindsAtLeastThem(Set(ct1), Set(ct2), Nil(), s, s, Nil(), s) + + check(findConcatSeparationZippers(z1, Set(ct2), Nil(), s, s).isDefined) + } + } + + + } + case Nil() => { + + assert(nullableZipper(zipperTot)) + assert(zipperTot.exists(c => nullableContext(c))) + val witness = SetUtils.getWitness(zipperTot, (c: Context[C]) => nullableContext(c)) + assert(nullableContext(witness)) + val witnessBeforeConcat = z1.mapPost2(c => c.concat(ct2))(witness) + assert(witnessBeforeConcat.concat(ct2) == witness) + ListUtils.lemmaConcatThenFirstSubseqOfTot(witnessBeforeConcat.exprs, ct2.exprs) + ListUtils.lemmaConcatThenSecondSubseqOfTot(witnessBeforeConcat.exprs, ct2.exprs) + ListUtils.lemmaContentSubsetPreservesForall(witnessBeforeConcat.exprs ++ ct2.exprs, witnessBeforeConcat.exprs, (r: Regex[C]) => nullable(r)) + ListUtils.lemmaContentSubsetPreservesForall(witnessBeforeConcat.exprs ++ ct2.exprs, ct2.exprs, (r: Regex[C]) => nullable(r)) + assert(nullableContext(witnessBeforeConcat) && nullableContext(ct2)) + SetUtils.lemmaContainsThenExists(z1, witnessBeforeConcat, (c: Context[C]) => nullableContext(c)) + SetUtils.lemmaContainsThenExists(Set(ct2), ct2, (c: Context[C]) => nullableContext(c)) + + assert(matchZipper(z1, Nil())) + assert(matchZipper(Set(ct2), Nil())) + lemmaZ1MatchesS1AndZ2MatchesS2ThenFindSeparationZipperFindsAtLeastThem(z1, Set(ct2), Nil(), Nil(), Nil(), Nil(), Nil()) + } + } + }.ensuring( _ => findConcatSeparationZippers(z1, Set(ct2), Nil(), s, s).isDefined) + // @ghost // @opaque // @inlineOnce diff --git a/lexers/regex/verifiedlexer/stainless.conf b/lexers/regex/verifiedlexer/stainless.conf index 960e9600..8f1a2aa7 100644 --- a/lexers/regex/verifiedlexer/stainless.conf +++ b/lexers/regex/verifiedlexer/stainless.conf @@ -3,7 +3,7 @@ vc-cache = true # debug = ["verification", "smt"] -timeout = 20 +timeout = 10 check-models = false print-ids = false print-types = false From 71aa5f32d98329daddc5f8e047e4c2ddaf39d832 Mon Sep 17 00:00:00 2001 From: Samuel Chassot Date: Sun, 24 Nov 2024 09:23:09 +0100 Subject: [PATCH 57/78] proved lemmaConcatZipperMatchesStringThenFindConcatDefined --- .../src/main/scala/ch/epfl/lexer/Utils.scala | 107 ++++++++++ .../scala/ch/epfl/lexer/VerifiedRegex.scala | 188 ++++++++++++++---- 2 files changed, 251 insertions(+), 44 deletions(-) diff --git a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/Utils.scala b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/Utils.scala index dd346633..0a144803 100644 --- a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/Utils.scala +++ b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/Utils.scala @@ -28,6 +28,113 @@ object SetUtils { } }.ensuring(_ => s.flatMap(f).isEmpty) + @opaque + @ghost + @inlineOnce + def lemmaMapOnEmptySetIsEmpty[A, B](s: Set[A], f: A => B): Unit = { + require(s.isEmpty) + val smap = s.map(f) + if(!smap.isEmpty) { + val hd = smap.toList.head + assert(smap.contains(hd)) + unfold(s.mapPost2(f)(hd)) + check(false) + } + }.ensuring(_ => s.map(f).isEmpty) + + @opaque + @ghost + @inlineOnce + def lemmaMapAssociative[A, B](s1: Set[A], s2: Set[A], f: A => B): Unit = { + val l1 = (s1.map(f) ++ s2.map(f)).toList + val l2 = (s1++s2).map(f).toList + + ListUtils.lemmaSubseqRefl(l1) + ListUtils.lemmaSubseqRefl(l2) + + lemmaMapAssociativeToList2(s1, s2, f, l1, l2) + lemmaMapAssociativeToList1(s1, s2, f, l1, l2) + check(l1.forall(l2.contains)) + check(l2.forall(l1.contains)) + ListSpecs.forallContainsSubset(l1, l2) + ListSpecs.forallContainsSubset(l2, l1) + + }.ensuring(_ => s1.map(f) ++ s2.map(f) == (s1++s2).map(f)) + + @opaque + @ghost + @inlineOnce + def lemmaMapAssociativeElem[A, B](s1: Set[A], s2: Set[A], f: A => B, b: B): Unit = { + if(s1.map(f).contains(b)) { + val witness = s1.mapPost2(f)(b) + assert(s1.contains(witness)) + // assert(witness == a) + assert((s1 ++ s2).contains(witness)) + unfold((s1 ++ s2).mapPost1(f)(witness)) + check((s1 ++ s2).map(f).contains(f(witness))) + } else if(s2.map(f).contains(b)) { + val witness = s2.mapPost2(f)(b) + assert(s2.contains(witness)) + // assert(witness == a) + assert((s1 ++ s2).contains(witness)) + unfold((s1 ++ s2).mapPost1(f)(witness)) + check((s1 ++ s2).map(f).contains(f(witness))) + } else { + if((s1 ++ s2).map(f).contains(b)) { + val witness = (s1 ++ s2).mapPost2(f)(b) + assert(s1.contains(witness) || s2.contains(witness)) + if(s1.contains(witness)) { + unfold(s1.mapPost1(f)(witness)) + check(false) + } else { + unfold(s2.mapPost1(f)(witness)) + check(false) + } + assert(f(witness) == b) + check(false) + } + assert(!(s1 ++ s2).map(f).contains(b)) + } + }.ensuring(_ => (s1.map(f) ++ s2.map(f)).contains(b) == (s1++s2).map(f).contains(b)) + + @opaque + @ghost + @inlineOnce + def lemmaMapAssociativeToList1[A, B](s1: Set[A], s2: Set[A], f: A => B, l1: List[B], l2: List[B]): Unit = { + require(ListSpecs.subseq(l1, (s1.map(f) ++ s2.map(f)).toList)) + require(l2 == (s1++s2).map(f).toList) + decreases(l1.size) + l1 match { + case Cons(hd, tl) => { + lemmaMapAssociativeElem(s1, s2, f, hd) + ListSpecs.subseqTail(l1, (s1.map(f) ++ s2.map(f)).toList) + lemmaMapAssociativeToList1(s1, s2, f, tl, l2) + ListSpecs.subseqContains(l1, (s1.map(f) ++ s2.map(f)).toList, hd) + } + case Nil() => () + } + + }.ensuring(_ => l1.forall(l2.contains)) + + @opaque + @ghost + @inlineOnce + def lemmaMapAssociativeToList2[A, B](s1: Set[A], s2: Set[A], f: A => B, l1: List[B], l2: List[B]): Unit = { + require(l1 == (s1.map(f) ++ s2.map(f)).toList) + require(ListSpecs.subseq(l2, (s1++s2).map(f).toList)) + decreases(l2.size) + l2 match { + case Cons(hd, tl) => { + lemmaMapAssociativeElem(s1, s2, f, hd) + ListSpecs.subseqTail(l2, (s1++s2).map(f).toList) + lemmaMapAssociativeToList2(s1, s2, f, l1, tl) + ListSpecs.subseqContains(l2, (s1++s2).map(f).toList, hd) + } + case Nil() => () + } + + }.ensuring(_ => l2.forall(l1.contains)) + @opaque @ghost @inlineOnce diff --git a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedRegex.scala b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedRegex.scala index a786f75a..07bc3dba 100644 --- a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedRegex.scala +++ b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedRegex.scala @@ -1144,12 +1144,12 @@ object ZipperRegex { }.ensuring(_ => findConcatSeparationZippers(z1, z2, s1Rec, s2Rec, s).isDefined) - @ghost // TODO finish this lemma + @ghost @opaque @inlineOnce def lemmaConcatZipperMatchesStringThenFindConcatDefined[C](z1: Zipper[C], ct2: Context[C], s: List[C]): Unit = { require(matchZipper(appendTo(z1, ct2), s)) - decreases(s) + decreases(s, zipperDepthTotal(z1.toList)) val zipperTot = appendTo(z1, ct2) s match { case Cons(shd, stl) => { @@ -1162,6 +1162,7 @@ object ZipperRegex { assert(matchingContextBeforeConcat.concat(ct2) == matchingContext) val ct1 = matchingContextBeforeConcat + assert(z1.contains(ct1)) val zipper = Set(ct1.concat(ct2)) assert(zipper == Set(matchingContext)) val ctx = ct1.concat(ct2) @@ -1178,43 +1179,56 @@ object ZipperRegex { assert(matchZipper(derivUp, stl) == matchZipper(derivationStepZipperDown(ct1Hd, ct1Tl.concat(ct2), shd), stl) || matchZipper(derivationStepZipperUp(ct1Tl.concat(ct2), shd), stl)) assert(matchZipper(derivUp, stl)) if(matchZipper(derivationStepZipperUp(ct1Tl.concat(ct2), shd), stl)){ // Can be simplified now that we can call on zipper, we'll do - // SetUtils.lemmaFlatMapOnSingletonSet(Set(ct1Tl.concat(ct2)), ct1Tl.concat(ct2), (c: Context[C]) => derivationStepZipperUp(c, shd)) - // assert(derivationStepZipper(Set(ct1Tl.concat(ct2)), shd) == derivationStepZipperUp(ct1Tl.concat(ct2), shd)) - // assert(matchZipper(Set(ct1Tl.concat(ct2)), s)) - // lemmaConcatZipperMatchesStringThenFindConcatDefined(Set(ct1Tl), ct2, s) - // check(findConcatSeparationZippers(Set(ct1Tl), Set(ct2), Nil(), s, s).isDefined) - // val (s1, s2) = findConcatSeparationZippers(Set(ct1Tl), Set(ct2), Nil(), s, s).get - // assert(matchZipper(Set(ct1Tl), s1)) - // assert(matchZipper(Set(ct2), s2)) - // assert(s1 ++ s2 == s) - // if(s1.isEmpty){ - // assert(matchZipper(Set(ct1Tl), s1)) - // assert(Set(ct1Tl).exists(c => nullableContext(c))) - // val witness = SetUtils.getWitness(Set(ct1Tl), (c: Context[C]) => nullableContext(c)) - // assert(nullableContext(ct1Tl)) - // assert(nullableContext(ct1)) - // SetUtils.lemmaContainsThenExists(Set(ct1Tl), ct1Tl, (c: Context[C]) => nullableContext(c)) - // SetUtils.lemmaContainsThenExists(Set(ct1), ct1, (c: Context[C]) => nullableContext(c)) - // assert(matchZipper(Set(ct1Tl), s1)) - // assert(matchZipper(Set(ct1), s1)) - // lemmaZ1MatchesS1AndZ2MatchesS2ThenFindSeparationZipperFindsAtLeastThem(Set(ct1), Set(ct2), s1, s2, s, Nil(), s) - // check(findConcatSeparationZippers(Set(ct1), Set(ct2), Nil(), s, s).isDefined) - // } else { - // SetUtils.lemmaFlatMapOnSingletonSet(Set(ct1), ct1, (c: Context[C]) => derivationStepZipperUp(c, shd)) - // assert(derivationStepZipper(Set(ct1), shd) == derivationStepZipperUp(ct1, shd)) - // assert(derivationStepZipperUp(ct1, shd) == derivationStepZipperDown(ct1Hd, ct1Tl, shd) ++ derivationStepZipperUp(ct1Tl, shd)) - // lemmaZipperConcatMatchesSameAsBothZippers(derivationStepZipperDown(ct1Hd, ct1Tl, shd), derivationStepZipperUp(ct1Tl, shd), s1.tail) - // assert(matchZipper(Set(ct1), s1) == matchZipper(derivationStepZipperDown(ct1Hd, ct1Tl, shd), s1.tail) || matchZipper(derivationStepZipperUp(ct1Tl, shd), s1.tail)) - - // SetUtils.lemmaFlatMapOnSingletonSet(Set(ct1Tl), ct1Tl, (c: Context[C]) => derivationStepZipperUp(c, shd)) - // assert(derivationStepZipper(Set(ct1Tl), s1.head) == derivationStepZipperUp(ct1Tl, s1.head)) - // assert(matchZipper(Set(ct1Tl), s1) == matchZipper(derivationStepZipperUp(ct1Tl, s1.head), s1.tail)) - // assert(matchZipper(derivationStepZipperUp(ct1Tl, s1.head), s1.tail)) - // assert(matchZipper(Set(ct1), s1)) - // lemmaZ1MatchesS1AndZ2MatchesS2ThenFindSeparationZipperFindsAtLeastThem(Set(ct1), Set(ct2), s1, s2, s, Nil(), s) - // check(findConcatSeparationZippers(Set(ct1), Set(ct2), Nil(), s, s).isDefined) - // } + SetUtils.lemmaFlatMapOnSingletonSet(Set(ct1Tl.concat(ct2)), ct1Tl.concat(ct2), (c: Context[C]) => derivationStepZipperUp(c, shd)) + assert(derivationStepZipper(Set(ct1Tl.concat(ct2)), shd) == derivationStepZipperUp(ct1Tl.concat(ct2), shd)) + assert(matchZipper(Set(ct1Tl.concat(ct2)), s)) + SetUtils.lemmaMapOnSingletonSet(Set(ct1Tl), ct1Tl, c => c.concat(ct2)) + SetUtils.lemmaFlatMapOnSingletonSet(Set(ct1Tl), ct1Tl, (c: Context[C]) => derivationStepZipperUp(c, shd)) + assert(derivationStepZipper(Set(ct1Tl.concat(ct2)), shd) == derivationStepZipperUp(ct1Tl.concat(ct2), shd)) + assert(matchZipper(Set(ct1Tl.concat(ct2)), s)) + + lemmaTotalDepthZipperLargerThanOfAnyContext(z1.toList, ct1) + assert(contextDepthTotal(ct1) > contextDepthTotal(ct1Tl)) + assert(zipperDepthTotal(List(ct1Tl)) < zipperDepthTotal(z1.toList)) + lemmaConcatZipperMatchesStringThenFindConcatDefined(Set(ct1Tl), ct2, s) // Recursive call + + check(findConcatSeparationZippers(Set(ct1Tl), Set(ct2), Nil(), s, s).isDefined) + val (s1, s2) = findConcatSeparationZippers(Set(ct1Tl), Set(ct2), Nil(), s, s).get + assert(matchZipper(Set(ct1Tl), s1)) + assert(matchZipper(Set(ct2), s2)) + assert(s1 ++ s2 == s) + if(s1.isEmpty){ + assert(matchZipper(Set(ct1Tl), s1)) + assert(Set(ct1Tl).exists(c => nullableContext(c))) + val witness = SetUtils.getWitness(Set(ct1Tl), (c: Context[C]) => nullableContext(c)) + assert(nullableContext(ct1Tl)) + assert(nullableContext(ct1)) + SetUtils.lemmaContainsThenExists(Set(ct1Tl), ct1Tl, (c: Context[C]) => nullableContext(c)) + SetUtils.lemmaContainsThenExists(Set(ct1), ct1, (c: Context[C]) => nullableContext(c)) + assert(matchZipper(Set(ct1Tl), s1)) + assert(matchZipper(Set(ct1), s1)) + lemmaZ1MatchesS1AndZ2MatchesS2ThenFindSeparationZipperFindsAtLeastThem(Set(ct1), Set(ct2), s1, s2, s, Nil(), s) + check(findConcatSeparationZippers(Set(ct1), Set(ct2), Nil(), s, s).isDefined) + } else { + SetUtils.lemmaFlatMapOnSingletonSet(Set(ct1), ct1, (c: Context[C]) => derivationStepZipperUp(c, shd)) + assert(derivationStepZipper(Set(ct1), shd) == derivationStepZipperUp(ct1, shd)) + assert(derivationStepZipperUp(ct1, shd) == derivationStepZipperDown(ct1Hd, ct1Tl, shd) ++ derivationStepZipperUp(ct1Tl, shd)) + lemmaZipperConcatMatchesSameAsBothZippers(derivationStepZipperDown(ct1Hd, ct1Tl, shd), derivationStepZipperUp(ct1Tl, shd), s1.tail) + assert(matchZipper(Set(ct1), s1) == matchZipper(derivationStepZipperDown(ct1Hd, ct1Tl, shd), s1.tail) || matchZipper(derivationStepZipperUp(ct1Tl, shd), s1.tail)) + + SetUtils.lemmaFlatMapOnSingletonSet(Set(ct1Tl), ct1Tl, (c: Context[C]) => derivationStepZipperUp(c, shd)) + assert(derivationStepZipper(Set(ct1Tl), s1.head) == derivationStepZipperUp(ct1Tl, s1.head)) + assert(matchZipper(Set(ct1Tl), s1) == matchZipper(derivationStepZipperUp(ct1Tl, s1.head), s1.tail)) + assert(matchZipper(derivationStepZipperUp(ct1Tl, s1.head), s1.tail)) + assert(matchZipper(Set(ct1), s1)) + + lemmaZ1MatchesS1AndZ2MatchesS2ThenFindSeparationZipperFindsAtLeastThem(Set(ct1), Set(ct2), s1, s2, s, Nil(), s) + check(findConcatSeparationZippers(Set(ct1), Set(ct2), Nil(), s, s).isDefined) + } + SetUtils.lemmaContainsThenExists(z1, ct1, (c: Context[C]) => matchZipper(Set(c), s1)) + lemmaExistsMatchingContextThenMatchingString(z1.toList, s1) + lemmaZ1MatchesS1AndZ2MatchesS2ThenFindSeparationZipperFindsAtLeastThem(z1, Set(ct2), s1, s2, s, Nil(), s) check(findConcatSeparationZippers(z1, Set(ct2), Nil(), s, s).isDefined) } else { @@ -1222,6 +1236,7 @@ object ZipperRegex { check(matchZipper(derivationStepZipperUp(ct1Tl.concat(ct2), shd), stl) == false) assert(matchZipper(derivationStepZipperDown(ct1Hd, ct1Tl.concat(ct2), shd), stl)) + lemmaDerivativeStepZipperDownConcatCtxSameAsAppendTo(ct1Tl, ct1Hd, shd, ct2) assert(derivationStepZipperDown(ct1Hd, ct1Tl.concat(ct2), shd) == appendTo(derivationStepZipperDown(ct1Hd, ct1Tl, shd), ct2)) // TODO lemma lemmaConcatZipperMatchesStringThenFindConcatDefined(derivationStepZipperDown(ct1Hd, ct1Tl, shd), ct2, stl) @@ -1243,21 +1258,42 @@ object ZipperRegex { lemmaZ1MatchesS1AndZ2MatchesS2ThenFindSeparationZipperFindsAtLeastThem(Set(ct1), Set(ct2), Cons(shd, s1), s2, s, Nil(), s) check(findConcatSeparationZippers(Set(ct1), Set(ct2), Nil(), s, s).isDefined) - SetUtils.lemmaContainsThenExists(z1, ct1, (c: Context[C]) => matchZipper(Set(c), s)) + assert(z1.contains(ct1)) + SetUtils.lemmaContainsThenExists(z1, ct1, (c: Context[C]) => matchZipper(Set(c), Cons(shd, s1))) lemmaExistsMatchingContextThenMatchingString(z1.toList, Cons(shd, s1)) - lemmaZ1MatchesS1AndZ2MatchesS2ThenFindSeparationZipperFindsAtLeastThem(z1, Set(ct2), s1, s2, stl, Nil(), stl) + lemmaZ1MatchesS1AndZ2MatchesS2ThenFindSeparationZipperFindsAtLeastThem(z1, Set(ct2), Cons(shd, s1), s2, s, Nil(), s) check(findConcatSeparationZippers(z1, Set(ct2), Nil(), s, s).isDefined) } } else { + // ct1Hd is NOT nullable val derivDown = derivationStepZipperDown(ct1Hd, ct1Tl.concat(ct2), shd) - - assert(deriv == derivUp) - assert(matchZipper(derivationStepZipperDown(ct1Hd, ct1Tl.concat(ct2), shd), stl)) + assert(matchZipper(derivDown, stl)) + + lemmaDerivativeStepZipperDownConcatCtxSameAsAppendTo(ct1Tl, ct1Hd, shd, ct2) + assert(derivDown == appendTo(derivationStepZipperDown(ct1Hd, ct1Tl, shd), ct2)) + lemmaConcatZipperMatchesStringThenFindConcatDefined(derivationStepZipperDown(ct1Hd, ct1Tl, shd), ct2, stl) + + assert(findConcatSeparationZippers(derivationStepZipperDown(ct1Hd, ct1Tl, shd), Set(ct2), Nil(), stl, stl).isDefined) + val (s1, s2) = findConcatSeparationZippers(derivationStepZipperDown(ct1Hd, ct1Tl, shd), Set(ct2), Nil(), stl, stl).get + assume(matchZipper(derivationStepZipperDown(ct1Hd, ct1Tl, shd), s1)) + assume(matchZipper(Set(ct2), s2)) + assert(s1 ++ s2 == stl) - // TODO + val derivOnly1 = derivationStepZipper(Set(ct1), shd) + val deriOnly1Up = derivationStepZipperUp(ct1, shd) + SetUtils.lemmaFlatMapOnSingletonSet(Set(ct1), ct1, (c: Context[C]) => derivationStepZipperUp(c, shd)) + assert(derivOnly1 == deriOnly1Up) + assert(derivOnly1 == derivationStepZipperDown(ct1Hd, ct1Tl, shd)) + assert(matchZipper(Set(ct1), Cons(shd, s1)) == matchZipper(derivationStepZipperDown(ct1Hd, ct1Tl, shd), s1)) + assert(matchZipper(Set(ct1), Cons(shd, s1))) + lemmaZ1MatchesS1AndZ2MatchesS2ThenFindSeparationZipperFindsAtLeastThem(Set(ct1), Set(ct2), Cons(shd, s1), s2, s, Nil(), s) check(findConcatSeparationZippers(Set(ct1), Set(ct2), Nil(), s, s).isDefined) + assert(z1.contains(ct1)) + SetUtils.lemmaContainsThenExists(z1, ct1, (c: Context[C]) => matchZipper(Set(c), Cons(shd, s1))) + lemmaExistsMatchingContextThenMatchingString(z1.toList, Cons(shd, s1)) + lemmaZ1MatchesS1AndZ2MatchesS2ThenFindSeparationZipperFindsAtLeastThem(z1, Set(ct2), Cons(shd, s1), s2, s, Nil(), s) check(findConcatSeparationZippers(z1, Set(ct2), Nil(), s, s).isDefined) } } @@ -1268,6 +1304,9 @@ object ZipperRegex { assert(matchZipper(Set(ct1), Nil())) lemmaZ1MatchesS1AndZ2MatchesS2ThenFindSeparationZipperFindsAtLeastThem(Set(ct1), Set(ct2), Nil(), s, s, Nil(), s) + check(findConcatSeparationZippers(Set(ct1), Set(ct2), Nil(), s, s).isDefined) + SetUtils.lemmaContainsThenExists(z1, ct1, (c: Context[C]) => matchZipper(Set(c), Nil())) + lemmaExistsMatchingContextThenMatchingString(z1.toList, Nil()) check(findConcatSeparationZippers(z1, Set(ct2), Nil(), s, s).isDefined) } } @@ -1509,6 +1548,67 @@ object ZipperRegex { SetUtils.lemmaFlatMapAssociative(z1, z2, (c: Context[C]) => derivationStepZipperUp(c, a)) }.ensuring(_ => derivationStepZipper(z, a) == derivationStepZipper(z1, a) ++ derivationStepZipper(z2, a)) + @ghost + @opaque + @inlineOnce + def lemmaTotalDepthZipperLargerThanOfAnyContext[C](zl: List[Context[C]], c: Context[C]): Unit = { + require(zl.contains(c)) + + zl match { + case Cons(hd, tl) => { + if(tl.contains(c)){ + lemmaTotalDepthZipperLargerThanOfAnyContext(tl, c) + } + } + case Nil() => check(false) + } + + + }.ensuring(_ => contextDepthTotal(c) <= zipperDepthTotal(zl)) + + @ghost + @opaque + @inlineOnce + def lemmaDerivativeStepZipperDownConcatCtxSameAsAppendTo[C](c: Context[C], r: Regex[C], a: C, auxCtx: Context[C]): Unit = { + require(validRegex(r)) + decreases(regexDepth(r)) + val f: Context[C] => Context[C] = (cz: Context[C]) => cz.concat(auxCtx) + r match { + case ElementMatch(cc) if cc == a => { + SetUtils.lemmaMapOnSingletonSet(Set(c), c, f) + check(derivationStepZipperDown(r, c.concat(auxCtx), a) == appendTo(derivationStepZipperDown(r, c, a), auxCtx)) + } + case Union(rOne, rTwo) => { + assert(derivationStepZipperDown(r, c.concat(auxCtx), a) == derivationStepZipperDown(rOne, c.concat(auxCtx), a) ++ derivationStepZipperDown(rTwo, c.concat(auxCtx), a)) + lemmaDerivativeStepZipperDownConcatCtxSameAsAppendTo(c, rOne, a, auxCtx) + lemmaDerivativeStepZipperDownConcatCtxSameAsAppendTo(c, rTwo, a, auxCtx) + SetUtils.lemmaMapAssociative(derivationStepZipperDown(rOne, c, a), derivationStepZipperDown(rTwo, c, a), f) + check(derivationStepZipperDown(r, c.concat(auxCtx), a) == appendTo(derivationStepZipperDown(r, c, a), auxCtx)) + } + case Concat(rOne, rTwo) if nullable(rOne) => { + assert(derivationStepZipperDown(r, c.concat(auxCtx), a) == derivationStepZipperDown(rOne, c.concat(auxCtx).prepend(rTwo), a) ++ derivationStepZipperDown(rTwo, c.concat(auxCtx), a)) + lemmaDerivativeStepZipperDownConcatCtxSameAsAppendTo(c.prepend(rTwo), rOne, a, auxCtx) + lemmaDerivativeStepZipperDownConcatCtxSameAsAppendTo(c, rTwo, a, auxCtx) + SetUtils.lemmaMapAssociative(derivationStepZipperDown(rOne, c.prepend(rTwo), a), derivationStepZipperDown(rTwo, c, a), f) + check(derivationStepZipperDown(r, c.concat(auxCtx), a) == appendTo(derivationStepZipperDown(r, c, a), auxCtx)) + } + case Concat(rOne, rTwo) => { + assert(derivationStepZipperDown(r, c.concat(auxCtx), a) == derivationStepZipperDown(rOne, c.concat(auxCtx).prepend(rTwo), a)) + lemmaDerivativeStepZipperDownConcatCtxSameAsAppendTo(c.prepend(rTwo), rOne, a, auxCtx) + check(derivationStepZipperDown(r, c.concat(auxCtx), a) == appendTo(derivationStepZipperDown(r, c, a), auxCtx)) + } + case Star(rInner) => { + assert(derivationStepZipperDown(r, c.concat(auxCtx), a) == derivationStepZipperDown(rInner, c.concat(auxCtx).prepend(Star(rInner)), a)) + lemmaDerivativeStepZipperDownConcatCtxSameAsAppendTo(c.prepend(Star(rInner)), rInner, a, auxCtx) + check(derivationStepZipperDown(r, c.concat(auxCtx), a) == appendTo(derivationStepZipperDown(r, c, a), auxCtx)) + } + case _ => { + SetUtils.lemmaMapOnEmptySetIsEmpty(Set(), f) + check(derivationStepZipperDown(r, c.concat(auxCtx), a) == appendTo(derivationStepZipperDown(r, c, a), auxCtx)) + } + } + }.ensuring(_ => derivationStepZipperDown(r, c.concat(auxCtx), a) == appendTo(derivationStepZipperDown(r, c, a), auxCtx)) + @ghost @opaque From 0a1c7665d52205706f2e2ba4124ef1fe96896d5c Mon Sep 17 00:00:00 2001 From: Samuel Chassot Date: Sun, 24 Nov 2024 14:27:31 +0100 Subject: [PATCH 58/78] working on zipper proof, almost done --- .../src/main/scala/ch/epfl/lexer/Utils.scala | 10 + .../scala/ch/epfl/lexer/VerifiedRegex.scala | 458 +++++++++++++++--- lexers/regex/verifiedlexer/stainless.conf | 4 +- lexers/regex/verifiedlexer/verify_dev.sh | 2 +- 4 files changed, 415 insertions(+), 59 deletions(-) diff --git a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/Utils.scala b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/Utils.scala index 0a144803..754293ea 100644 --- a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/Utils.scala +++ b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/Utils.scala @@ -1197,4 +1197,14 @@ object ListUtils { case Nil() => check(false) } }.ensuring (_ => (l - e).size < l.size) + + @opaque + @inlineOnce + @ghost + def lemmaTailOfListWithoutDuplicatesContentIsContentMinusHead[B](l: List[B], s: Set[B]): Unit = { + require(ListOps.noDuplicate(l)) + require(s.toList == l) + require(!l.isEmpty) + + }.ensuring(_ => (l.tail).content == s - l.head) } diff --git a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedRegex.scala b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedRegex.scala index 07bc3dba..af091312 100644 --- a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedRegex.scala +++ b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedRegex.scala @@ -353,7 +353,7 @@ object ZipperRegex { c.exprs match { case Cons(hd, tl) => regexDepthTotal(hd) + contextDepthTotal(Context(tl)) - case Nil() => BigInt(0) + case Nil() => BigInt(1) } }.ensuring(res => res >= 0) @@ -545,15 +545,7 @@ object ZipperRegex { require(validRegex(r)) require(z.toList == zl) require(r == unfocusZipper(zl)) - decreases( - // regexDepth(r) - zipperDepth(zl) * 2 - + zipperDepthTotal(zl) - // + (if zl.isEmpty || zl.head.isEmpty then BigInt(0) else zl.head.exprs.size) - + zl.size - + s.size - // + (if zl.isEmpty || zl.head.isEmpty then BigInt(0) else regexDepth(zl.head.exprs.head)) - ) // Pretty insane but so beautiful haha + decreases(zipperDepthTotal(zl), s.size) mainMatchTheorem(r, s) zl match { @@ -679,7 +671,7 @@ object ZipperRegex { theoremZipperRegexEquivInductZ(zR1, List(Context(List(r1, r2))), Concat(r1, r2), s) theoremZipperRegexEquivInductZ(zR2, List(Context(List(r2))), r2, s) } - } else + } else { // Here, we are in the case where the Concat is the result of generalisedConcat val zDeriv = derivationStepZipper(z, shd) val zDerivUp = derivationStepZipperUp(Context(Cons(hExp, tlExp)), shd) @@ -764,6 +756,24 @@ object ZipperRegex { assert(zipperDepthTotal(List(Context(Cons(rTwo, tlExp)))) < zipperDepthTotal(zl)) // Measure decreases theoremZipperRegexEquivInductZ(zVirt1, List(Context(Cons(rOne, tlExp))), generalisedConcat(Cons(rOne, tlExp)), s) theoremZipperRegexEquivInductZ(zVirt2, List(Context(Cons(rTwo, tlExp))), generalisedConcat(Cons(rTwo, tlExp)), s) + mainMatchTheorem(generalisedConcat(Cons(rOne, tlExp)), s) + mainMatchTheorem(generalisedConcat(Cons(rTwo, tlExp)), s) + + assert(matchZipper(z, s) == matchR(generalisedConcat(Cons(rOne, tlExp)), s) || matchR(generalisedConcat(Cons(rTwo, tlExp)), s)) + + assert(r == Concat(Union(rOne, rTwo), r2)) + assert(generalisedConcat(Cons(rOne, tlExp)) == Concat(rOne, generalisedConcat(tlExp))) + assert(generalisedConcat(Cons(rTwo, tlExp)) == Concat(rTwo, generalisedConcat(tlExp))) + assert(r2 == generalisedConcat(tlExp)) + + // We want + lemmaConcatDistributesInUnion(rOne, rTwo, r2, s) + assert(matchR(Concat(Union(rOne, rTwo), r2), s) == matchR(Union(Concat(rOne, r2), Concat(rTwo, r2)), s)) + mainMatchTheorem(Union(Concat(rOne, r2), Concat(rTwo, r2)), s) + mainMatchTheorem(Concat(Union(rOne, rTwo), r2), s) + assert(matchR(Concat(Union(rOne, rTwo), r2), s) == matchR(generalisedConcat(Cons(rOne, tlExp)), s) || matchR(generalisedConcat(Cons(rTwo, tlExp)), s)) + + check(matchR(r, s) == matchZipper(z, s)) } case Concat(rOne, rTwo) if nullable(rOne) => { assert(zDerivDown == derivationStepZipperDown(rOne, Context(Cons(rTwo, tlExp)), shd) ++ derivationStepZipperDown(rTwo, Context(tlExp), shd)) @@ -812,7 +822,7 @@ object ZipperRegex { lemmaConcatAssociative(rOne, rTwo, generalisedConcat(tlExp), s) assert(matchR(Concat(Concat(rOne, rTwo), generalisedConcat(tlExp)), s) == matchR(Concat(rOne, Concat(rTwo, generalisedConcat(tlExp))), s)) - assert(matchR(r, s) == matchZipper(z, s)) + check(matchR(r, s) == matchZipper(z, s)) } case Concat(rOne, rTwo) => { assert(zDerivDown == derivationStepZipperDown(rOne, Context(Cons(rTwo, tlExp)), shd)) @@ -846,19 +856,210 @@ object ZipperRegex { lemmaConcatAssociative(rOne, rTwo, generalisedConcat(tlExp), s) assert(matchR(Concat(Concat(rOne, rTwo), generalisedConcat(tlExp)), s) == matchR(Concat(rOne, Concat(rTwo, generalisedConcat(tlExp))), s)) - assert(matchR(r, s) == matchZipper(z, s)) + check(matchR(r, s) == matchZipper(z, s)) } case Star(rInner) => { assert(zDerivDown == derivationStepZipperDown(rInner, Context(Cons(Star(rInner), tlExp)), shd)) - assume(matchR(r, s) == matchZipper(z, s)) + val zR1 = Set(Context(Cons(rInner, Cons(Star(rInner), tlExp)))) + val derivZR1 = derivationStepZipper(zR1, shd) + + val derivUpZR1 = derivationStepZipperUp(Context(Cons(rInner, Cons(Star(rInner), tlExp))), shd) + + SetUtils.lemmaFlatMapOnSingletonSet(zR1, Context(Cons(rInner, Cons(Star(rInner), tlExp))), (c: Context[C]) => derivationStepZipperUp(c, shd)) + val derivDownZR1 = derivationStepZipperDown(rInner, Context(Cons(Star(rInner), tlExp)), shd) + + assert(derivZR1 == derivDownZR1) + assert(zDerivDown == derivDownZR1) + assert(matchZipper(zR1, s) == matchZipper(derivationStepZipper(zR1, shd), stl)) + assert(matchZipper(zR1, s) == matchZipper(derivZR1, stl)) + + assert(unfocusZipper(zl) == r) + assert(r == Concat(Star(rInner), r2)) + val rR = Concat(rInner, Concat(Star(rInner), r2)) + assert(unfocusZipper(List(Context(Cons(rInner, Cons(Star(rInner), tlExp))))) == rR) + + val subZR1 = Set(Context(List(rInner))) + val subZR2 = Set(Context(Cons(Star(rInner), tlExp))) + + val derivSubZR1 = derivationStepZipper(subZR1, shd) + val derivSubZR2 = derivationStepZipper(subZR2, shd) + + val derivUpSubZR1 = derivationStepZipperUp(Context(List(rInner)), shd) + val derivUpSubZR2 = derivationStepZipperUp(Context(Cons(Star(rInner), tlExp)), shd) + + SetUtils.lemmaFlatMapOnSingletonSet(subZR1, Context(List(rInner)), (c: Context[C]) => derivationStepZipperUp(c, shd)) + SetUtils.lemmaFlatMapOnSingletonSet(subZR2, Context(Cons(Star(rInner), tlExp)), (c: Context[C]) => derivationStepZipperUp(c, shd)) + + assert(unfocusZipper(List(Context(List(rInner)))) == rInner) + assert(unfocusZipper(List(Context(Cons(Star(rInner), tlExp)))) == Concat(Star(rInner), r2)) + mainMatchTheorem(rR, s) + + if(matchZipper(zDerivDown, stl)){ + if(matchR(rR, s)){ + val (s1, s2) = findConcatSeparation(rInner, Concat(Star(rInner), r2), Nil(), s, s).get + assert(s == s1 ++ s2) + assert(matchR(rInner, s1)) + assert(matchR(Concat(Star(rInner), r2), s2)) + + theoremZipperRegexEquivInductZ(subZR1, List(Context(List(rInner))), rInner, s1) + theoremZipperRegexEquivInductZ(subZR2, List(Context(Cons(Star(rInner), tlExp))), Concat(Star(rInner), r2), s2) + assert(matchZipper(subZR1, s1)) + assert(matchZipper(subZR2, s2)) + lemmaConcatenateContextMatchesConcatOfStrings(Context(List(rInner)), Context(Cons(Star(rInner), tlExp)), s1, s2) + + assert(r == Concat(Star(rInner), r2)) + assert(rR == Concat(rInner, Concat(Star(rInner), r2))) + assert(!s.isEmpty) + + mainMatchTheorem(Concat(Star(rInner), r2), s2) + val (ss1, ss2) = findConcatSeparation(Star(rInner), r2, Nil(), s2, s2).get + assert(s2 == ss1 ++ ss2) + assert(matchR(Star(rInner), ss1)) + assert(matchR(r2, ss2)) + + + lemmaTwoRegexMatchThenConcatMatchesConcatString(rInner, Star(rInner), s1, ss1) + lemmaStarApp(rInner, s1, ss1) + assert(matchR(Star(rInner), s1 ++ ss1)) + + ListUtils.lemmaTwoListsConcatAssociativity(s1, ss1, ss2) + assert(s1 ++ ss1 ++ ss2 == s) + lemmaTwoRegexMatchThenConcatMatchesConcatString(Star(rInner), r2, s1 ++ ss1, ss2) + assert(matchR(r, s)) + + check(matchR(r, s) == matchR(rR, s)) + check(matchR(r, s) == matchZipper(z, s)) + + } else { + assert(!matchR(rR, s)) + if(matchZipper(z, s)){ + if(matchZipper(zDerivDown, stl)){ + assert(matchZipper(zR1, s)) + SetUtils.lemmaMapOnSingletonSet(subZR1, Context(List(rInner)), c => c.concat(Context(Cons(Star(rInner), tlExp)))) + assert(appendTo(subZR1, Context(Cons(Star(rInner), tlExp))) == Set(Context(Cons(rInner, Cons(Star(rInner), tlExp))))) + assert(appendTo(subZR1, Context(Cons(Star(rInner), tlExp))) == zR1) + lemmaConcatZipperMatchesStringThenFindConcatDefined(subZR1, Context(Cons(Star(rInner), tlExp)), s) + val (s1, s2) = findConcatSeparationZippers(subZR1, subZR2, Nil(), s, s).get + assert(s == s1 ++ s2) + assert(matchZipper(subZR1, s1)) + assert(matchZipper(subZR2, s2)) + lemmaConcatenateContextMatchesConcatOfStrings(Context(List(rInner)), Context(Cons(Star(rInner), tlExp)), s1, s2) + theoremZipperRegexEquivInductZ(subZR1, List(Context(List(rInner))), rInner, s1) + theoremZipperRegexEquivInductZ(subZR2, List(Context(Cons(Star(rInner), tlExp))), Concat(Star(rInner), r2), s2) + assert(matchR(rInner, s1)) + assert(matchR(Concat(Star(rInner), r2), s2)) + lemmaTwoRegexMatchThenConcatMatchesConcatString(rInner, Concat(Star(rInner), r2), s1, s2) + check(matchR(rR, s)) + check(false) + } else { + assert(matchZipper(zDerivUpUp, stl)) + + val zTail = Set(Context(tlExp)) + val zDerivTail = derivationStepZipper(zTail, shd) + SetUtils.lemmaFlatMapOnSingletonSet(zTail, Context(tlExp), (c: Context[C]) => derivationStepZipperUp(c, shd)) + assert(zDerivUpUp == zDerivTail) + assert(r2 == generalisedConcat(tlExp)) + theoremZipperRegexEquivInductZ(zTail, List(Context(tlExp)), generalisedConcat(tlExp), s) + assert(matchR(generalisedConcat(tlExp), s) == matchZipper(zTail, s)) + assert(nullable(r1)) + assert(matchZipper(z, s) == matchZipper(zDerivUpUp, stl) || matchZipper(zDerivDown, stl)) + assert(matchZipper(z, s)) + check(false) + } + + } + check(!matchZipper(z, s)) + if(matchR(r, s)){ + assert(r == Concat(Star(rInner), r2)) + assert(rR == Concat(rInner, Concat(Star(rInner), r2))) + val (ss1, ss2) = findConcatSeparation(Star(rInner), r2, Nil(), s, s).get + assert(s == ss1 ++ ss2) + assert(matchR(Star(rInner), ss1)) + assert(matchR(r2, ss2)) + mainMatchTheorem(Star(rInner), ss1) + assert(!s.isEmpty) + if(!ss1.isEmpty){ + mainMatchTheorem(r2, ss2) + assert(findConcatSeparation(rInner, Star(rInner), Nil(), ss1, ss1).isDefined) + val (sss1, sss2) = findConcatSeparation(rInner, Star(rInner), Nil(), ss1, ss1).get + assert(sss1 ++ sss2 == ss1) + assert(matchR(rInner, sss1)) + assert(matchR(Star(rInner), sss2)) + lemmaTwoRegexMatchThenConcatMatchesConcatString(Star(rInner), r2, sss2, ss2) + assert(matchR(Concat(Star(rInner), r2), sss2 ++ ss2)) + lemmaTwoRegexMatchThenConcatMatchesConcatString(rInner, Concat(Star(rInner), r2), sss1, sss2 ++ ss2) + ListUtils.lemmaTwoListsConcatAssociativity(sss1, sss2, ss2) + check(false) + } else { + assert(ss2 == s) + assert(matchR(r2, s)) + val zTail = Set(Context(tlExp)) + val zDerivTail = derivationStepZipper(zTail, shd) + SetUtils.lemmaFlatMapOnSingletonSet(zTail, Context(tlExp), (c: Context[C]) => derivationStepZipperUp(c, shd)) + assert(zDerivUpUp == zDerivTail) + assert(r2 == generalisedConcat(tlExp)) + theoremZipperRegexEquivInductZ(zTail, List(Context(tlExp)), generalisedConcat(tlExp), s) + assert(matchR(generalisedConcat(tlExp), s) == matchZipper(zTail, s)) + assert(nullable(r1)) + assert(matchZipper(z, s) == matchZipper(zDerivUpUp, stl) || matchZipper(zDerivDown, stl)) + assert(matchZipper(z, s)) + check(false) + + } + } + check(!matchR(r, s)) + } + check(matchR(r, s) == matchZipper(z, s)) + } else { + val zTail = Set(Context(tlExp)) + val zDerivTail = derivationStepZipper(zTail, shd) + SetUtils.lemmaFlatMapOnSingletonSet(zTail, Context(tlExp), (c: Context[C]) => derivationStepZipperUp(c, shd)) + assert(zDerivUpUp == zDerivTail) + assert(r2 == generalisedConcat(tlExp)) + theoremZipperRegexEquivInductZ(zTail, List(Context(tlExp)), generalisedConcat(tlExp), s) + assert(matchR(generalisedConcat(tlExp), s) == matchZipper(zTail, s)) + assert(nullable(r1)) + assert(matchZipper(z, s) == matchZipper(zDerivUpUp, stl) || matchZipper(zDerivDown, stl)) + assert(matchZipper(zTail, s) == matchR(generalisedConcat(tlExp), s)) + check(matchR(r, s) == matchZipper(z, s)) + } + check(matchR(r, s) == matchZipper(z, s)) + + } + + case EmptyExpr() => { + lemmaEmptyZipperMatchesNothing(zDerivDown, stl) + assert(zDerivDown == Set()) + mainMatchTheorem(r, s) + assert(r == Concat(r1, r2)) + assert(r1 == EmptyExpr[C]()) + mainMatchTheorem(r1, s) + mainMatchTheorem(r2, s) + assert(matchR(r, s) == matchR(r2, s)) + val zTail = Set(Context(tlExp)) + val zDerivTail = derivationStepZipper(zTail, shd) + SetUtils.lemmaFlatMapOnSingletonSet(zTail, Context(tlExp), (c: Context[C]) => derivationStepZipperUp(c, shd)) + assert(zDerivUpUp == zDerivTail) + assert(zDeriv == zDerivDown ++ zDerivUpUp) + lemmaZipperConcatMatchesSameAsBothZippers(zDerivDown, zDerivUpUp, stl) + assert(matchZipper(z, s) == matchZipper(zDerivUpUp, stl)) + theoremZipperRegexEquivInductZ(zTail, List(Context(tlExp)), generalisedConcat(tlExp), s) + assert(matchR(generalisedConcat(tlExp), s) == matchZipper(zTail, s)) + assert(r2 == generalisedConcat(tlExp)) + + check(matchR(r, s) == matchZipper(z, s)) } case _ => { - assume(matchR(r, s) == matchZipper(z, s)) lemmaEmptyZipperMatchesNothing(zDerivDown, stl) assert(zDerivDown == Set()) - assert(matchR(r, s) == matchZipper(z, s)) + mainMatchTheorem(r, s) + assert(r == Concat(r1, r2)) + mainMatchTheorem(r1, s) + mainMatchTheorem(r2, s) + + check(matchR(r, s) == matchZipper(z, s)) } } @@ -874,14 +1075,13 @@ object ZipperRegex { theoremZipperRegexEquivInductZ(zTail, List(Context(tlExp)), generalisedConcat(tlExp), s) assert(matchR(generalisedConcat(tlExp), s) == matchZipper(zTail, s)) - - - assume(matchR(r, s) == matchZipper(z, s)) + check(matchR(r, s) == matchZipper(z, s)) } } } + } case Star(rInner) => { assert(matchR(r, s) == s.isEmpty || findConcatSeparation(rInner, Star(rInner), Nil(), s, s).isDefined) s match { @@ -934,36 +1134,6 @@ object ZipperRegex { assert(unfocusZipper(List(Context(List(Star(rInner))))) == Star(rInner)) mainMatchTheorem(rR1, s) - // if(findConcatSeparationZippers(subZR1, subZR2, Nil(), s, s).isDefined){ - // val (s1, s2) = findConcatSeparationZippers(subZR1, subZR2, Nil(), s, s).get - // assert(s == s1 ++ s2) - // assert(matchZipper(subZR1, s1)) - // assert(matchZipper(subZR2, s2)) - // theoremZipperRegexEquivInductZ(subZR1, List(Context(List(rInner))), rInner, s1) - // theoremZipperRegexEquivInductZ(subZR2, List(Context(List(Star(rInner)))), Star(rInner), s2) - // lemmaConcatenateContextMatchesConcatOfStrings(Context(List(rInner)), Context(List(Star(rInner))), s1, s2) - // lemmaTwoRegexMatchThenConcatMatchesConcatString(rInner, Star(rInner), s1, s2) - // check(matchR(r, s) == matchZipper(z, s)) - // } else{ - // if(matchR(rR1, s)){ - // val (s1, s2) = findConcatSeparation(rInner, Star(rInner), Nil(), s, s).get - // assert(s == s1 ++ s2) - // assert(matchR(rInner, s1)) - // assert(matchR(Star(rInner), s2)) - // theoremZipperRegexEquivInductZ(subZR1, List(Context(List(rInner))), rInner, s1) - // theoremZipperRegexEquivInductZ(subZR2, List(Context(List(Star(rInner)))), Star(rInner), s2) - // assert(matchZipper(subZR1, s1)) - // assert(matchZipper(subZR2, s2)) - // lemmaZ1MatchesS1AndZ2MatchesS2ThenFindSeparationZipperFindsAtLeastThem(subZR1, subZR2, s1, s2, s, Nil(), s) - // check(false) - // } - // check(!matchR(rR1, s)) - // assert(appendTo(subZR1, Context(List(Star(rInner)))) == Set(Context(List(rInner, Star(rInner))))) - // lemmaConcatZipperMatchesStringThenFindConcatDefined(subZR1, Context(List(Star(rInner))), s) - // // I'm stuck here, I need to prove that if the findConcatSeparationZippers returns None, then the matchZipper(z, s) is false - // check(!matchZipper(z, s)) - // } - if(matchR(rR1, s)){ val (s1, s2) = findConcatSeparation(rInner, Star(rInner), Nil(), s, s).get assert(s == s1 ++ s2) @@ -976,7 +1146,7 @@ object ZipperRegex { assert(matchZipper(subZR2, s2)) lemmaConcatenateContextMatchesConcatOfStrings(Context(List(rInner)), Context(List(Star(rInner))), s1, s2) check(matchR(r, s) == matchZipper(z, s)) - } else{ + } else { assert(!matchR(rR1, s)) if(matchZipper(z, s)){ SetUtils.lemmaMapOnSingletonSet(subZR1, Context(List(rInner)), c => c.concat(Context(List(Star(rInner))))) @@ -1015,7 +1185,59 @@ object ZipperRegex { assert(nullableZipper(z) == nullable(r)) check(matchZipper(z, s) == matchR(r, s)) } - case Cons(shd, stl) => assume(matchR(r, s) == matchZipper(z, s)) + case Cons(shd, stl) => { + assert(zl == Cons(hd, tl)) + assert(!tl.isEmpty) + matchRGenUnionSpec(r, unfocusZipperList(zl), s) + if(matchR(r, s)){ + assert(unfocusZipperList(zl).exists(rr => validRegex(rr) && matchR(rr, s))) + val witnessR = ListUtils.getWitness(unfocusZipperList(zl), (rr: Regex[C]) => validRegex(rr) && matchR(rr, s)) + assert(unfocusZipperList(zl).contains(witnessR)) + assert(validRegex(witnessR)) + assert(matchR(witnessR, s)) + lemmaUnfocusListContainsConcatThenZipperExistsCorrespondingContext(zl, witnessR) + assert(zl.exists((cc: Context[C]) => generalisedConcat(cc.exprs) == witnessR)) + val witnessC = ListUtils.getWitness(zl, (cc: Context[C]) => generalisedConcat(cc.exprs) == witnessR) + lemmaTotalDepthZipperLargerThanOfAnyContextMoreThanOne(zl, witnessC) + assert(zipperDepthTotal(zl) > zipperDepthTotal(List(witnessC))) + theoremZipperRegexEquivInductZ(Set(witnessC), List(witnessC), witnessR, s) + assert(matchZipper(Set(witnessC), s)) + SetUtils.lemmaContainsThenExists(z, witnessC, (c: Context[C]) => matchZipper(Set(c), s)) + lemmaExistsMatchingContextThenMatchingString(zl, s) + check(matchR(r, s) == matchZipper(z, s)) + }else{ + assert(!unfocusZipperList(zl).exists(rr => validRegex(rr) && matchR(rr, s))) + if(matchZipper(z, s)){ + lemmaZipperMatchesExistsMatchingContext(zl, s) + assert(zl.exists(cc => matchZipper(Set(cc), s))) + val witnessC: Context[C] = ListUtils.getWitness(zl, (cc: Context[C]) => matchZipper(Set(cc), s)) + lemmaContextForallValidExprs(witnessC, witnessC.exprs) + assert(witnessC.exprs.forall(validRegex)) + lemmaTotalDepthZipperLargerThanOfAnyContextMoreThanOne(zl, witnessC) + assert(zipperDepthTotal(zl) > zipperDepthTotal(List(witnessC))) + theoremZipperRegexEquivInductZ(Set(witnessC), List(witnessC), generalisedConcat(witnessC.exprs), s) + lemmaZipperContainsContextUnfocusListContainsConcat(zl, witnessC) + assert(unfocusZipperList(zl).contains(generalisedConcat(witnessC.exprs))) + ListUtils.lemmaContainsThenExists(unfocusZipperList(zl), generalisedConcat(witnessC.exprs), (rr: Regex[C]) => validRegex(rr) && matchR(rr, s)) + check(false) + } + check(!matchZipper(z, s)) + check(matchR(r, s) == matchZipper(z, s)) + } + // lemmaZipperConcatMatchesSameAsBothZippers(Set(hd), tl.content, s) + // theoremZipperRegexEquivInductZ(Set(hd), List(hd), generalisedConcat(hd.exprs), s) + // theoremZipperRegexEquivInductZ(tl.content, tl.content.toList, unfocusZipper(tl.content.toList), s) + + // assert(matchZipper(z, s) == matchR(generalisedConcat(hd.exprs), s) || matchR(unfocusZipper(tl), s)) + // r match { + // case Union(r1, r2) => { + // assert(r1 == generalisedConcat(hd.exprs)) + // assert(r2 == unfocusZipper(tl)) + // } + // case _ => check(false) + // } + // check(matchR(r, s) == matchZipper(z, s)) + } } } case Nil() => { @@ -1027,6 +1249,46 @@ object ZipperRegex { }.ensuring(_ => matchR(r, s) == matchZipper(z, s)) + @ghost + @opaque + @inlineOnce + def lemmaZipperContainsContextUnfocusListContainsConcat[C](zl: List[Context[C]], c: Context[C]): Unit = { + require(zl.contains(c)) + decreases(zl.size) + zl match { + case Cons(hd, tl) => + if(tl.contains(c)){ + lemmaZipperContainsContextUnfocusListContainsConcat(tl, c) + } + case Nil() => check(false) + } + }.ensuring(_ => unfocusZipperList(zl).contains(generalisedConcat(c.exprs))) + + + @ghost + @opaque + @inlineOnce + def lemmaUnfocusListContainsConcatThenZipperExistsCorrespondingContext[C](zl: List[Context[C]], r: Regex[C]): Unit = { + require(unfocusZipperList(zl).contains(r)) + decreases(zl.size) + zl match { + case Cons(hd, tl) => + if(unfocusZipperList(tl).contains(r)){ + lemmaUnfocusListContainsConcatThenZipperExistsCorrespondingContext(tl, r) + } + case Nil() => check(false) + } + + }.ensuring(_ => zl.exists(c => generalisedConcat(c.exprs) == r)) + + @ghost + @opaque + @inlineOnce + def lemmaContextForallValidExprs[C](c: Context[C], l: List[Regex[C]]): Unit = { + require(l == c.exprs) + }.ensuring(_ => l.forall(validRegex)) + + /** Enumerate all cuts in s and returns one that works, i.e., z1 matches s1 and z2 matches s2 Specifically, it is the right most one, i.e., s2 is the largest, if multiple exist * Returns None if no valid cut exists @@ -1242,8 +1504,8 @@ object ZipperRegex { assert(findConcatSeparationZippers(derivationStepZipperDown(ct1Hd, ct1Tl, shd), Set(ct2), Nil(), stl, stl).isDefined) val (s1, s2) = findConcatSeparationZippers(derivationStepZipperDown(ct1Hd, ct1Tl, shd), Set(ct2), Nil(), stl, stl).get - assume(matchZipper(derivationStepZipperDown(ct1Hd, ct1Tl, shd), s1)) - assume(matchZipper(Set(ct2), s2)) + assert(matchZipper(derivationStepZipperDown(ct1Hd, ct1Tl, shd), s1)) + assert(matchZipper(Set(ct2), s2)) assert(s1 ++ s2 == stl) val derivOnly1 = derivationStepZipper(Set(ct1), shd) @@ -1275,8 +1537,8 @@ object ZipperRegex { assert(findConcatSeparationZippers(derivationStepZipperDown(ct1Hd, ct1Tl, shd), Set(ct2), Nil(), stl, stl).isDefined) val (s1, s2) = findConcatSeparationZippers(derivationStepZipperDown(ct1Hd, ct1Tl, shd), Set(ct2), Nil(), stl, stl).get - assume(matchZipper(derivationStepZipperDown(ct1Hd, ct1Tl, shd), s1)) - assume(matchZipper(Set(ct2), s2)) + assert(matchZipper(derivationStepZipperDown(ct1Hd, ct1Tl, shd), s1)) + assert(matchZipper(Set(ct2), s2)) assert(s1 ++ s2 == stl) val derivOnly1 = derivationStepZipper(Set(ct1), shd) @@ -1553,7 +1815,7 @@ object ZipperRegex { @inlineOnce def lemmaTotalDepthZipperLargerThanOfAnyContext[C](zl: List[Context[C]], c: Context[C]): Unit = { require(zl.contains(c)) - + decreases(zl.size) zl match { case Cons(hd, tl) => { if(tl.contains(c)){ @@ -1566,6 +1828,25 @@ object ZipperRegex { }.ensuring(_ => contextDepthTotal(c) <= zipperDepthTotal(zl)) + @ghost + @opaque + @inlineOnce + def lemmaTotalDepthZipperLargerThanOfAnyContextMoreThanOne[C](zl: List[Context[C]], c: Context[C]): Unit = { + require(zl.contains(c)) + require(zl.size > 1) + decreases(zl.size) + zl match { + case Cons(hd, tl) => { + if(tl.contains(c) && tl.size > 1){ + lemmaTotalDepthZipperLargerThanOfAnyContextMoreThanOne(tl, c) + } + } + case Nil() => check(false) + } + + + }.ensuring(_ => contextDepthTotal(c) < zipperDepthTotal(zl)) + @ghost @opaque @inlineOnce @@ -2980,6 +3261,71 @@ object VerifiedRegexMatcher { // ---------------------------------------------------- Lemmas ---------------------------------------------------- + @ghost + @inlineOnce + @opaque + def lemmaConcatDistributesInUnion[C](r1: Regex[C], r2: Regex[C], rTail: Regex[C], s: List[C]): Unit = { + require(validRegex(r1)) + require(validRegex(r2)) + require(validRegex(rTail)) + val rLeft = Concat(Union(r1, r2), rTail) + val rRight = Union(Concat(r1, rTail), Concat(r2, rTail)) + mainMatchTheorem(rLeft, s) + mainMatchTheorem(rRight, s) + if(matchR(rLeft, s)){ + val (s1, s2) = findConcatSeparation(Union(r1, r2), rTail, Nil(), s, s).get + assert(matchR(Union(r1, r2), s1)) + assert(matchR(rTail, s2)) + mainMatchTheorem(Union(r1, r2), s1) + mainMatchTheorem(rTail, s2) + mainMatchTheorem(r1, s1) + mainMatchTheorem(r2, s1) + + assert(matchR(r1, s1) || matchR(r2, s1)) + + mainMatchTheorem(Concat(r1, rTail), s) + mainMatchTheorem(Concat(r2, rTail), s) + assert(matchR(rRight, s) == (matchR(Concat(r1, rTail), s) || matchR(Concat(r2, rTail), s))) + if(matchR(r1, s1)){ + lemmaTwoRegexMatchThenConcatMatchesConcatString(r1, rTail, s1, s2) + } else{ + lemmaTwoRegexMatchThenConcatMatchesConcatString(r2, rTail, s1, s2) + } + check(matchR(rRight, s)) + } else { + assert(!findConcatSeparation(Union(r1, r2), rTail, Nil(), s, s).isDefined) + if(matchR(rRight, s)){ + mainMatchTheorem(Concat(r1, rTail), s) + mainMatchTheorem(Concat(r2, rTail), s) + assert(matchR(Concat(r1, rTail), s) || matchR(Concat(r2, rTail), s)) + if(matchR(Concat(r1, rTail), s)){ + val (s1, s2) = findConcatSeparation(r1, rTail, Nil(), s, s).get + assert(matchR(r1, s1)) + assert(matchR(rTail, s2)) + mainMatchTheorem(Union(r1, r2), s1) + mainMatchTheorem(r1, s1) + mainMatchTheorem(r2, s1) + assert(matchR(Union(r1, r2), s1)) + assert(matchR(r1, s1)) + lemmaTwoRegexMatchThenConcatMatchesConcatString(Union(r1, r2), rTail, s1, s2) + check(false) + } else { + val (s1, s2) = findConcatSeparation(r2, rTail, Nil(), s, s).get + assert(matchR(r2, s1)) + assert(matchR(rTail, s2)) + mainMatchTheorem(Union(r1, r2), s1) + mainMatchTheorem(r1, s1) + mainMatchTheorem(r2, s1) + assert(matchR(Union(r1, r2), s1)) + assert(matchR(r2, s1)) + lemmaTwoRegexMatchThenConcatMatchesConcatString(Union(r1, r2), rTail, s1, s2) + check(false) + } + } + } + }.ensuring(_ => matchR(Concat(Union(r1, r2), rTail), s) == matchR(Union(Concat(r1, rTail), Concat(r2, rTail)), s)) + + @ghost def lemmaIfMatchRThenLongestMatchFromThereReturnsAtLeastThis[C](baseR: Regex[C], r: Regex[C], input: List[C], testedP: List[C]): Unit = { require(validRegex(baseR)) diff --git a/lexers/regex/verifiedlexer/stainless.conf b/lexers/regex/verifiedlexer/stainless.conf index 8f1a2aa7..74b6c9d4 100644 --- a/lexers/regex/verifiedlexer/stainless.conf +++ b/lexers/regex/verifiedlexer/stainless.conf @@ -3,13 +3,13 @@ vc-cache = true # debug = ["verification", "smt"] -timeout = 10 +timeout = 40 check-models = false print-ids = false print-types = false batched = true strict-arithmetic = false -solvers = "smt-cvc5,smt-z3,smt-cvc4" +solvers = "smt-cvc5,smt-z3" check-measures = yes infer-measures = true simplifier = "bland" diff --git a/lexers/regex/verifiedlexer/verify_dev.sh b/lexers/regex/verifiedlexer/verify_dev.sh index 139b274e..85427209 100755 --- a/lexers/regex/verifiedlexer/verify_dev.sh +++ b/lexers/regex/verifiedlexer/verify_dev.sh @@ -4,5 +4,5 @@ stainless-dotty\ src/main/scala/ch/epfl/lexer/Utils.scala\ src/main/scala/ch/epfl/map/*\ --config-file=stainless.conf\ - -D-parallel=12\ + -D-parallel=16\ $1 From 5cbe9eeb751aaa67e108e56c8c6e5b2082c9c7df Mon Sep 17 00:00:00 2001 From: Samuel Chassot Date: Sun, 24 Nov 2024 14:31:43 +0100 Subject: [PATCH 59/78] working on zipper proof, almost done --- .../scala/ch/epfl/lexer/VerifiedRegex.scala | 131 +----------------- 1 file changed, 2 insertions(+), 129 deletions(-) diff --git a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedRegex.scala b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedRegex.scala index af091312..c71db562 100644 --- a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedRegex.scala +++ b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedRegex.scala @@ -895,135 +895,8 @@ object ZipperRegex { assert(unfocusZipper(List(Context(Cons(Star(rInner), tlExp)))) == Concat(Star(rInner), r2)) mainMatchTheorem(rR, s) - if(matchZipper(zDerivDown, stl)){ - if(matchR(rR, s)){ - val (s1, s2) = findConcatSeparation(rInner, Concat(Star(rInner), r2), Nil(), s, s).get - assert(s == s1 ++ s2) - assert(matchR(rInner, s1)) - assert(matchR(Concat(Star(rInner), r2), s2)) - - theoremZipperRegexEquivInductZ(subZR1, List(Context(List(rInner))), rInner, s1) - theoremZipperRegexEquivInductZ(subZR2, List(Context(Cons(Star(rInner), tlExp))), Concat(Star(rInner), r2), s2) - assert(matchZipper(subZR1, s1)) - assert(matchZipper(subZR2, s2)) - lemmaConcatenateContextMatchesConcatOfStrings(Context(List(rInner)), Context(Cons(Star(rInner), tlExp)), s1, s2) - - assert(r == Concat(Star(rInner), r2)) - assert(rR == Concat(rInner, Concat(Star(rInner), r2))) - assert(!s.isEmpty) - - mainMatchTheorem(Concat(Star(rInner), r2), s2) - val (ss1, ss2) = findConcatSeparation(Star(rInner), r2, Nil(), s2, s2).get - assert(s2 == ss1 ++ ss2) - assert(matchR(Star(rInner), ss1)) - assert(matchR(r2, ss2)) - - - lemmaTwoRegexMatchThenConcatMatchesConcatString(rInner, Star(rInner), s1, ss1) - lemmaStarApp(rInner, s1, ss1) - assert(matchR(Star(rInner), s1 ++ ss1)) - - ListUtils.lemmaTwoListsConcatAssociativity(s1, ss1, ss2) - assert(s1 ++ ss1 ++ ss2 == s) - lemmaTwoRegexMatchThenConcatMatchesConcatString(Star(rInner), r2, s1 ++ ss1, ss2) - assert(matchR(r, s)) - - check(matchR(r, s) == matchR(rR, s)) - check(matchR(r, s) == matchZipper(z, s)) - - } else { - assert(!matchR(rR, s)) - if(matchZipper(z, s)){ - if(matchZipper(zDerivDown, stl)){ - assert(matchZipper(zR1, s)) - SetUtils.lemmaMapOnSingletonSet(subZR1, Context(List(rInner)), c => c.concat(Context(Cons(Star(rInner), tlExp)))) - assert(appendTo(subZR1, Context(Cons(Star(rInner), tlExp))) == Set(Context(Cons(rInner, Cons(Star(rInner), tlExp))))) - assert(appendTo(subZR1, Context(Cons(Star(rInner), tlExp))) == zR1) - lemmaConcatZipperMatchesStringThenFindConcatDefined(subZR1, Context(Cons(Star(rInner), tlExp)), s) - val (s1, s2) = findConcatSeparationZippers(subZR1, subZR2, Nil(), s, s).get - assert(s == s1 ++ s2) - assert(matchZipper(subZR1, s1)) - assert(matchZipper(subZR2, s2)) - lemmaConcatenateContextMatchesConcatOfStrings(Context(List(rInner)), Context(Cons(Star(rInner), tlExp)), s1, s2) - theoremZipperRegexEquivInductZ(subZR1, List(Context(List(rInner))), rInner, s1) - theoremZipperRegexEquivInductZ(subZR2, List(Context(Cons(Star(rInner), tlExp))), Concat(Star(rInner), r2), s2) - assert(matchR(rInner, s1)) - assert(matchR(Concat(Star(rInner), r2), s2)) - lemmaTwoRegexMatchThenConcatMatchesConcatString(rInner, Concat(Star(rInner), r2), s1, s2) - check(matchR(rR, s)) - check(false) - } else { - assert(matchZipper(zDerivUpUp, stl)) - - val zTail = Set(Context(tlExp)) - val zDerivTail = derivationStepZipper(zTail, shd) - SetUtils.lemmaFlatMapOnSingletonSet(zTail, Context(tlExp), (c: Context[C]) => derivationStepZipperUp(c, shd)) - assert(zDerivUpUp == zDerivTail) - assert(r2 == generalisedConcat(tlExp)) - theoremZipperRegexEquivInductZ(zTail, List(Context(tlExp)), generalisedConcat(tlExp), s) - assert(matchR(generalisedConcat(tlExp), s) == matchZipper(zTail, s)) - assert(nullable(r1)) - assert(matchZipper(z, s) == matchZipper(zDerivUpUp, stl) || matchZipper(zDerivDown, stl)) - assert(matchZipper(z, s)) - check(false) - } - - } - check(!matchZipper(z, s)) - if(matchR(r, s)){ - assert(r == Concat(Star(rInner), r2)) - assert(rR == Concat(rInner, Concat(Star(rInner), r2))) - val (ss1, ss2) = findConcatSeparation(Star(rInner), r2, Nil(), s, s).get - assert(s == ss1 ++ ss2) - assert(matchR(Star(rInner), ss1)) - assert(matchR(r2, ss2)) - mainMatchTheorem(Star(rInner), ss1) - assert(!s.isEmpty) - if(!ss1.isEmpty){ - mainMatchTheorem(r2, ss2) - assert(findConcatSeparation(rInner, Star(rInner), Nil(), ss1, ss1).isDefined) - val (sss1, sss2) = findConcatSeparation(rInner, Star(rInner), Nil(), ss1, ss1).get - assert(sss1 ++ sss2 == ss1) - assert(matchR(rInner, sss1)) - assert(matchR(Star(rInner), sss2)) - lemmaTwoRegexMatchThenConcatMatchesConcatString(Star(rInner), r2, sss2, ss2) - assert(matchR(Concat(Star(rInner), r2), sss2 ++ ss2)) - lemmaTwoRegexMatchThenConcatMatchesConcatString(rInner, Concat(Star(rInner), r2), sss1, sss2 ++ ss2) - ListUtils.lemmaTwoListsConcatAssociativity(sss1, sss2, ss2) - check(false) - } else { - assert(ss2 == s) - assert(matchR(r2, s)) - val zTail = Set(Context(tlExp)) - val zDerivTail = derivationStepZipper(zTail, shd) - SetUtils.lemmaFlatMapOnSingletonSet(zTail, Context(tlExp), (c: Context[C]) => derivationStepZipperUp(c, shd)) - assert(zDerivUpUp == zDerivTail) - assert(r2 == generalisedConcat(tlExp)) - theoremZipperRegexEquivInductZ(zTail, List(Context(tlExp)), generalisedConcat(tlExp), s) - assert(matchR(generalisedConcat(tlExp), s) == matchZipper(zTail, s)) - assert(nullable(r1)) - assert(matchZipper(z, s) == matchZipper(zDerivUpUp, stl) || matchZipper(zDerivDown, stl)) - assert(matchZipper(z, s)) - check(false) - - } - } - check(!matchR(r, s)) - } - check(matchR(r, s) == matchZipper(z, s)) - } else { - val zTail = Set(Context(tlExp)) - val zDerivTail = derivationStepZipper(zTail, shd) - SetUtils.lemmaFlatMapOnSingletonSet(zTail, Context(tlExp), (c: Context[C]) => derivationStepZipperUp(c, shd)) - assert(zDerivUpUp == zDerivTail) - assert(r2 == generalisedConcat(tlExp)) - theoremZipperRegexEquivInductZ(zTail, List(Context(tlExp)), generalisedConcat(tlExp), s) - assert(matchR(generalisedConcat(tlExp), s) == matchZipper(zTail, s)) - assert(nullable(r1)) - assert(matchZipper(z, s) == matchZipper(zDerivUpUp, stl) || matchZipper(zDerivDown, stl)) - assert(matchZipper(zTail, s) == matchR(generalisedConcat(tlExp), s)) - check(matchR(r, s) == matchZipper(z, s)) - } + + check(matchR(r, s) == matchZipper(z, s)) check(matchR(r, s) == matchZipper(z, s)) From 1e6b9730eb5094f31862433b7eb39a2972e0e6af Mon Sep 17 00:00:00 2001 From: Samuel Chassot Date: Sun, 24 Nov 2024 15:35:20 +0100 Subject: [PATCH 60/78] done proving ZIPERRR :D --- .../scala/ch/epfl/lexer/VerifiedRegex.scala | 131 +++++++++++++++++- 1 file changed, 129 insertions(+), 2 deletions(-) diff --git a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedRegex.scala b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedRegex.scala index c71db562..7c72a672 100644 --- a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedRegex.scala +++ b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedRegex.scala @@ -895,8 +895,135 @@ object ZipperRegex { assert(unfocusZipper(List(Context(Cons(Star(rInner), tlExp)))) == Concat(Star(rInner), r2)) mainMatchTheorem(rR, s) - - check(matchR(r, s) == matchZipper(z, s)) + if(matchR(r, s)){ // r == Concat(Star(rInner), r2) + // We have 2 cases: + // - Star(rInner) matches a non-empty string + // - r2 matches the entire string + mainMatchTheorem(r, s) + val (starMatched, r2Matched) = findConcatSeparation(Star(rInner), r2, Nil(), s, s).get + assert(starMatched ++ r2Matched == s) + assert(matchR(Star(rInner), starMatched)) + assert(matchR(r2, r2Matched)) + if(starMatched.isEmpty){ + // r2 matches the entire string + // So in Zipper term, the matched part is the 2nd deriveUp (bypassing the head of the context) + val zTail = Set(Context(tlExp)) + val zDerivTail = derivationStepZipper(zTail, shd) + SetUtils.lemmaFlatMapOnSingletonSet(zTail, Context(tlExp), (c: Context[C]) => derivationStepZipperUp(c, shd)) + assert(zDerivUpUp == zDerivTail) + + assert(r == generalisedConcat(hd.exprs)) + assert(regexDepth(r) >= regexDepth(generalisedConcat(tlExp))) + assert(zipperDepth(zl) >= zipperDepth(List(Context(tlExp)))) + + theoremZipperRegexEquivInductZ(zTail, List(Context(tlExp)), generalisedConcat(tlExp), s) + assert(matchR(generalisedConcat(tlExp), s) == matchZipper(zTail, s)) + check(matchR(r, s) == matchZipper(z, s)) + } else { + // Star(rInner) matches a non-empty string + // So here, we can use the rR regex to express r + mainMatchTheorem(Star(rInner), starMatched) + val (starS1, starS2) = findConcatSeparation(rInner, Star(rInner), Nil(), starMatched, starMatched).get + assert(starMatched == starS1 ++ starS2) + ListUtils.lemmaTwoListsConcatAssociativity(starS1, starS2, r2Matched) + assert(starS1 ++ starS2 ++ r2Matched == s) + assert(matchR(rInner, starS1)) + assert(matchR(Star(rInner), starS2)) + lemmaStarApp(rInner, starS1, starS2) + val s2 = starS2 ++ r2Matched + lemmaTwoRegexMatchThenConcatMatchesConcatString(Star(rInner), r2, starS2, r2Matched) + assert(matchR(Concat(Star(rInner), r2), s2)) + + theoremZipperRegexEquivInductZ(subZR1, List(Context(List(rInner))), rInner, starS1) + theoremZipperRegexEquivInductZ(subZR2, List(Context(Cons(Star(rInner), tlExp))), Concat(Star(rInner), r2), s2) + assert(matchZipper(subZR1, starS1)) + assert(matchZipper(subZR2, s2)) + lemmaConcatenateContextMatchesConcatOfStrings(Context(List(rInner)), Context(Cons(Star(rInner), tlExp)), starS1, s2) + + assert(matchZipper(subZR1, starS1) == matchR(rInner, starS1)) + assert(matchZipper(subZR2, s2) == matchR(Concat(Star(rInner), r2), s2)) + assert(rR == Concat(rInner, Concat(Star(rInner), r2))) + lemmaTwoRegexMatchThenConcatMatchesConcatString(rInner, Concat(Star(rInner), r2), starS1, s2) + check(matchR(r, s) == matchR(rR, s)) + + lemmaConcatenateContextMatchesConcatOfStrings(Context(List(rInner)), Context(Cons(Star(rInner), tlExp)), starS1, s2) + assert(matchZipper(Set(Context(Cons(rInner, Cons(Star(rInner), tlExp)))), starS1 ++ s2) == matchR(rR, s)) + + check(matchR(r, s) == matchZipper(z, s)) + } + + check(matchR(r, s) == matchZipper(z, s)) + } else { + if(matchZipper(z, s)){ + assert(nullable(hExp)) + assert(matchZipper(z, s) == (matchZipper(zDerivDown, stl) || matchZipper(zDerivUpUp, stl))) + if(matchZipper(zDerivDown, stl)){ + // Then we know that + assert(matchZipper(zR1, s)) + assert(zR1 == Set(Context(Cons(rInner, Cons(Star(rInner), tlExp))))) + assert(zR1 == Set(Context(Cons(rInner, Cons(Star(rInner), tlExp))))) + + SetUtils.lemmaMapOnSingletonSet(subZR1, Context(List(rInner)), c => c.concat(Context(Cons(Star(rInner), tlExp)))) + assert(appendTo(subZR1, Context(Cons(Star(rInner), tlExp))) == Set(Context(Cons(rInner, Cons(Star(rInner), tlExp))))) + assert(appendTo(subZR1, Context(Cons(Star(rInner), tlExp))) == zR1) + + lemmaConcatZipperMatchesStringThenFindConcatDefined(subZR1, Context(Cons(Star(rInner), tlExp)), s) + + val (s1, s2) = findConcatSeparationZippers(subZR1, subZR2, Nil(), s, s).get + assert(s == s1 ++ s2) + assert(matchZipper(subZR1, s1)) + assert(matchZipper(subZR2, s2)) + lemmaConcatenateContextMatchesConcatOfStrings(Context(List(rInner)), Context(Cons(Star(rInner), tlExp)), s1, s2) + + theoremZipperRegexEquivInductZ(subZR1, List(Context(List(rInner))), rInner, s1) + theoremZipperRegexEquivInductZ(subZR2, List(Context(Cons(Star(rInner), tlExp))), Concat(Star(rInner), r2), s2) + + assert(matchR(rInner, s1)) + assert(matchR(Concat(Star(rInner), r2), s2)) + lemmaTwoRegexMatchThenConcatMatchesConcatString(rInner, Concat(Star(rInner), r2), s1, s2) + check(matchR(rR, s)) + assert(rR == Concat(rInner, Concat(Star(rInner), r2))) + lemmaConcatAssociative(rInner, Star(rInner), r2, s) + assert(matchR(Concat(Concat(rInner, Star(rInner)), r2), s) == matchR(Concat(rInner, Concat(Star(rInner), r2)), s)) + assert(matchR(Concat(Concat(rInner, Star(rInner)), r2), s)) + mainMatchTheorem(Concat(Concat(rInner, Star(rInner)), r2), s) + val (starS, r2S) = findConcatSeparation(Concat(rInner, Star(rInner)), r2, Nil(), s, s).get + assert(matchR(Concat(rInner, Star(rInner)), starS)) + mainMatchTheorem(Concat(rInner, Star(rInner)), starS) + val (sInner, sStarInner) = findConcatSeparation(rInner, Star(rInner), Nil(), starS, starS).get + ListUtils.lemmaTwoListsConcatAssociativity(sInner, sStarInner, r2S) + assert(matchR(rInner, sInner)) + assert(matchR(Star(rInner), sStarInner)) + lemmaStarApp(rInner, sInner, sStarInner) + assert(matchR(Star(rInner), sInner ++ sStarInner)) + assert(matchR(r2, r2S)) + lemmaTwoRegexMatchThenConcatMatchesConcatString(Star(rInner), r2, sInner ++ sStarInner, r2S) + assert(s == sInner ++ sStarInner ++ r2S) + assert(matchR(r, s)) + check(false) + + }else{ + assert(matchZipper(zDerivUpUp, stl)) + val zTail = Set(Context(tlExp)) + val zDerivTail = derivationStepZipper(zTail, shd) + SetUtils.lemmaFlatMapOnSingletonSet(zTail, Context(tlExp), (c: Context[C]) => derivationStepZipperUp(c, shd)) + assert(zDerivUpUp == zDerivTail) + + assert(r == generalisedConcat(hd.exprs)) + assert(regexDepth(r) >= regexDepth(generalisedConcat(tlExp))) + assert(zipperDepth(zl) >= zipperDepth(List(Context(tlExp)))) + + theoremZipperRegexEquivInductZ(zTail, List(Context(tlExp)), generalisedConcat(tlExp), s) + assert(matchR(generalisedConcat(tlExp), s) == matchZipper(zTail, s)) + assert(matchR(r2, s)) + mainMatchTheorem(Star(rInner), Nil()) + lemmaTwoRegexMatchThenConcatMatchesConcatString(Star(rInner), r2, Nil(), s) + assert(matchR(r, s)) + check(false) + } + } + check(matchR(r, s) == matchZipper(z, s)) + } check(matchR(r, s) == matchZipper(z, s)) From f7f1dca24a08ff0fd8812ee830bc3b2391e43333 Mon Sep 17 00:00:00 2001 From: Samuel Chassot Date: Sun, 24 Nov 2024 15:36:22 +0100 Subject: [PATCH 61/78] clean up --- .../scala/ch/epfl/lexer/VerifiedRegex.scala | 85 ------------------- 1 file changed, 85 deletions(-) diff --git a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedRegex.scala b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedRegex.scala index 7c72a672..9e35cc69 100644 --- a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedRegex.scala +++ b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedRegex.scala @@ -466,78 +466,6 @@ object ZipperRegex { } }.ensuring(_ => zl.exists(c => VerifiedRegex.generalisedConcat(c.exprs) == r)) - - // @ghost - // @opaque - // @inlineOnce - // def theoremZipperRegexEquiv[C](z: Zipper[C], zl: List[Context[C]], r: Regex[C], s: List[C]): Unit = { - // require(validRegex(r)) - // require(z.toList == zl) - // require(r == unfocusZipper(zl)) - // decreases(regexDepth(r)) - // mainMatchTheorem(r, s) - // r match { - // case EmptyExpr() => lemmaZipperOfEmptyExprMatchesOnlyEmptyString(z, s) - // case EmptyLang() => - // if(z.isEmpty){ - // lemmaEmptyZipperMatchesNothing(z, s) - // } else { - // lemmaZipperStartingWithEmptyLangMatchesNothing(z, Context(List(r)), s) - // } - // case ElementMatch(a) => lemmaElementMatchZipperAcceptsOnlyThisChar(z, Context(List(ElementMatch(a))), a, s) - // case Union(r1, r2) => - // { - // mainMatchTheorem(r1, s) - // mainMatchTheorem(r2, s) - // assert(matchR(r, s) == (matchR(r1, s) || matchR(r2, s))) - // s match { - // case Nil() => { - // lemmaUnfocusPreservesNullability(r, z) - // assert(nullableZipper(z) == nullable(r)) - // check(matchZipper(z, s) == matchR(r, s)) - // } - // case Cons(shd, stl) => { - // assert(!z.toList.isEmpty) - // val zHd: Context[C] = z.toList.head - // val zTl: List[Context[C]] = z.toList.tail - - // assert(matchZipper(z, s) == matchZipper(derivationStepZipper(z, shd), stl)) - // assert(r == generalisedUnion(unfocusZipperList(z.toList))) - // matchRGenUnionSpec(r, unfocusZipperList(z.toList), s) - // assert(matchR(r, s) == unfocusZipperList(z.toList).exists(rr => validRegex(rr) && matchR(rr, s))) - - // if(zTl.isEmpty) { - // assert(r == generalisedConcat(zHd.exprs)) - // assert(zHd.exprs == List(r)) - // // Now let's dive in the derivative computation - // val deriv = derivationStepZipper(z, shd) - // val derivUp = derivationStepZipperUp(Context(List(r)), shd) - // zHd.exprs match { - // case Cons(right, parent) if nullable(right) => assert(derivationStepZipperDown(right, Context(parent), shd) ++ derivationStepZipperUp(Context(parent),shd) == derivationStepZipperUp(Context(List(r)), shd)) - // case Cons(right, parent) => assert(derivationStepZipperDown(right, Context(parent), shd) == derivationStepZipperUp(Context(List(r)), shd)) - // case Nil() => assert(Set[Context[C]]() == derivationStepZipperUp(Context(List(r)), shd)) - // } - - // } else { - // unfold(generalisedUnion(unfocusZipperList(z.toList))) - // assert(r2 == unfocusZipper(zTl)) - // } - - - - - - - // } - // } - // } - // case Star(rInner) => assume(matchR(r, s) == matchZipper(z, s)) - // case Concat(r1, r2) => assume(matchR(r, s) == matchZipper(z, s)) - // } - - - // }.ensuring(_ => matchR(r, s) == matchZipper(z, s)) - @ghost @opaque @inlineOnce // type Zipper[C] = Set[Context[C]] @@ -1224,19 +1152,6 @@ object ZipperRegex { check(!matchZipper(z, s)) check(matchR(r, s) == matchZipper(z, s)) } - // lemmaZipperConcatMatchesSameAsBothZippers(Set(hd), tl.content, s) - // theoremZipperRegexEquivInductZ(Set(hd), List(hd), generalisedConcat(hd.exprs), s) - // theoremZipperRegexEquivInductZ(tl.content, tl.content.toList, unfocusZipper(tl.content.toList), s) - - // assert(matchZipper(z, s) == matchR(generalisedConcat(hd.exprs), s) || matchR(unfocusZipper(tl), s)) - // r match { - // case Union(r1, r2) => { - // assert(r1 == generalisedConcat(hd.exprs)) - // assert(r2 == unfocusZipper(tl)) - // } - // case _ => check(false) - // } - // check(matchR(r, s) == matchZipper(z, s)) } } } From 09efd338a37b5d203906c364fff15e656a464c38 Mon Sep 17 00:00:00 2001 From: Samuel Chassot Date: Sun, 24 Nov 2024 16:10:51 +0100 Subject: [PATCH 62/78] Add main regex matching function to use zipper, rename theorem --- .../scala/ch/epfl/lexer/VerifiedRegex.scala | 64 +++++++++++-------- 1 file changed, 37 insertions(+), 27 deletions(-) diff --git a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedRegex.scala b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedRegex.scala index 9e35cc69..9e70217d 100644 --- a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedRegex.scala +++ b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedRegex.scala @@ -466,10 +466,13 @@ object ZipperRegex { } }.ensuring(_ => zl.exists(c => VerifiedRegex.generalisedConcat(c.exprs) == r)) + /** + * Main theorem to prove the equivalence between a regex and its corresponding zipper + */ @ghost @opaque @inlineOnce // type Zipper[C] = Set[Context[C]] - def theoremZipperRegexEquivInductZ[C](z: Zipper[C], zl: List[Context[C]], r: Regex[C], s: List[C]): Unit = { + def theoremZipperRegexEquiv[C](z: Zipper[C], zl: List[Context[C]], r: Regex[C], s: List[C]): Unit = { require(validRegex(r)) require(z.toList == zl) require(r == unfocusZipper(zl)) @@ -528,8 +531,8 @@ object ZipperRegex { assert(matchZipper(zR1, s) == matchZipper(zR1Deriv, stl)) assert(matchZipper(zR2, s) == matchZipper(zR2Deriv, stl)) assert((matchZipper(zR1, s) || matchZipper(zR2, s)) == matchZipper(z, s)) - theoremZipperRegexEquivInductZ(zR1, List(Context(List(r1))), r1, s) - theoremZipperRegexEquivInductZ(zR2, List(Context(List(r2))), r2, s) + theoremZipperRegexEquiv(zR1, List(Context(List(r1))), r1, s) + theoremZipperRegexEquiv(zR2, List(Context(List(r2))), r2, s) } } } @@ -585,8 +588,8 @@ object ZipperRegex { assert(contextDepth(Context(List(r2))) < contextDepth(Context(List(r)))) assert(zipperDepth(List(Context(List(r2)))) < zipperDepth(List(Context(List(r))))) assert(regexDepth(r) == regexDepth(Concat(r1, r2))) - theoremZipperRegexEquivInductZ(zR1, List(Context(List(r1, r2))), Concat(r1, r2), s) - theoremZipperRegexEquivInductZ(zR2, List(Context(List(r2))), r2, s) + theoremZipperRegexEquiv(zR1, List(Context(List(r1, r2))), Concat(r1, r2), s) + theoremZipperRegexEquiv(zR2, List(Context(List(r2))), r2, s) } else { @@ -596,8 +599,8 @@ object ZipperRegex { assert(hd.exprs == List(r)) assert(zipperDepth(List(Context(List(r1, r2)))) < zipperDepth(List(Context(List(r))))) // Measure assert(zipperDepth(List(Context(List(r1, r2)))) < zipperDepth(List(Context(List(r))))) // Measure - theoremZipperRegexEquivInductZ(zR1, List(Context(List(r1, r2))), Concat(r1, r2), s) - theoremZipperRegexEquivInductZ(zR2, List(Context(List(r2))), r2, s) + theoremZipperRegexEquiv(zR1, List(Context(List(r1, r2))), Concat(r1, r2), s) + theoremZipperRegexEquiv(zR2, List(Context(List(r2))), r2, s) } } else { // Here, we are in the case where the Concat is the result of generalisedConcat @@ -625,7 +628,7 @@ object ZipperRegex { case ElementMatch(c) if c == shd => { assert(zDerivDown == Set(Context(tlExp))) val zVirt = Set(Context(tlExp)) - theoremZipperRegexEquivInductZ(zVirt, List(Context(tlExp)), generalisedConcat(tlExp), stl) + theoremZipperRegexEquiv(zVirt, List(Context(tlExp)), generalisedConcat(tlExp), stl) assert(matchR(r, s) == matchZipper(z, s)) } case Union(rOne, rTwo) => { @@ -682,8 +685,8 @@ object ZipperRegex { assert(zipperDepthTotal(List(Context(Cons(rOne, tlExp)))) < zipperDepthTotal(zl)) // Measure decreases assert(zipperDepthTotal(List(Context(Cons(rTwo, tlExp)))) < zipperDepthTotal(zl)) // Measure decreases - theoremZipperRegexEquivInductZ(zVirt1, List(Context(Cons(rOne, tlExp))), generalisedConcat(Cons(rOne, tlExp)), s) - theoremZipperRegexEquivInductZ(zVirt2, List(Context(Cons(rTwo, tlExp))), generalisedConcat(Cons(rTwo, tlExp)), s) + theoremZipperRegexEquiv(zVirt1, List(Context(Cons(rOne, tlExp))), generalisedConcat(Cons(rOne, tlExp)), s) + theoremZipperRegexEquiv(zVirt2, List(Context(Cons(rTwo, tlExp))), generalisedConcat(Cons(rTwo, tlExp)), s) mainMatchTheorem(generalisedConcat(Cons(rOne, tlExp)), s) mainMatchTheorem(generalisedConcat(Cons(rTwo, tlExp)), s) @@ -733,8 +736,8 @@ object ZipperRegex { assert(contextDepthTotal(Context(Cons(rOne, Cons(rTwo, tlExp)))) < contextDepthTotal(Context(hd.exprs))) assert(zipperDepthTotal(List(Context(Cons(rOne, Cons(rTwo, tlExp))))) < zipperDepthTotal(zl)) // Measure decreases assert(zipperDepth(List(Context(Cons(rOne, Cons(rTwo, tlExp))))) <= zipperDepth(zl)) // Measure decreases - theoremZipperRegexEquivInductZ(zVirt1, List(Context(Cons(rOne, Cons(rTwo, tlExp)))), generalisedConcat(Cons(rOne, Cons(rTwo, tlExp))), s) - theoremZipperRegexEquivInductZ(zVirt2, List(Context(Cons(rTwo, tlExp))), generalisedConcat(Cons(rTwo, tlExp)), s) + theoremZipperRegexEquiv(zVirt1, List(Context(Cons(rOne, Cons(rTwo, tlExp)))), generalisedConcat(Cons(rOne, Cons(rTwo, tlExp))), s) + theoremZipperRegexEquiv(zVirt2, List(Context(Cons(rTwo, tlExp))), generalisedConcat(Cons(rTwo, tlExp)), s) mainMatchTheorem(generalisedConcat(Cons(rOne, Cons(rTwo, tlExp))), s) mainMatchTheorem(generalisedConcat(Cons(rTwo, tlExp)), s) @@ -768,7 +771,7 @@ object ZipperRegex { assert(contextDepthTotal(Context(Cons(rOne, Cons(rTwo, tlExp)))) < contextDepthTotal(Context(hd.exprs))) assert(zipperDepthTotal(List(Context(Cons(rOne, Cons(rTwo, tlExp))))) < zipperDepthTotal(zl)) // Measure decreases assert(zipperDepth(List(Context(Cons(rOne, Cons(rTwo, tlExp))))) <= zipperDepth(zl)) // Measure decreases - theoremZipperRegexEquivInductZ(zVirt1, List(Context(Cons(rOne, Cons(rTwo, tlExp)))), generalisedConcat(Cons(rOne, Cons(rTwo, tlExp))), s) + theoremZipperRegexEquiv(zVirt1, List(Context(Cons(rOne, Cons(rTwo, tlExp)))), generalisedConcat(Cons(rOne, Cons(rTwo, tlExp))), s) mainMatchTheorem(generalisedConcat(Cons(rOne, Cons(rTwo, tlExp))), s) mainMatchTheorem(generalisedConcat(Cons(rTwo, tlExp)), s) @@ -844,7 +847,7 @@ object ZipperRegex { assert(regexDepth(r) >= regexDepth(generalisedConcat(tlExp))) assert(zipperDepth(zl) >= zipperDepth(List(Context(tlExp)))) - theoremZipperRegexEquivInductZ(zTail, List(Context(tlExp)), generalisedConcat(tlExp), s) + theoremZipperRegexEquiv(zTail, List(Context(tlExp)), generalisedConcat(tlExp), s) assert(matchR(generalisedConcat(tlExp), s) == matchZipper(zTail, s)) check(matchR(r, s) == matchZipper(z, s)) } else { @@ -862,8 +865,8 @@ object ZipperRegex { lemmaTwoRegexMatchThenConcatMatchesConcatString(Star(rInner), r2, starS2, r2Matched) assert(matchR(Concat(Star(rInner), r2), s2)) - theoremZipperRegexEquivInductZ(subZR1, List(Context(List(rInner))), rInner, starS1) - theoremZipperRegexEquivInductZ(subZR2, List(Context(Cons(Star(rInner), tlExp))), Concat(Star(rInner), r2), s2) + theoremZipperRegexEquiv(subZR1, List(Context(List(rInner))), rInner, starS1) + theoremZipperRegexEquiv(subZR2, List(Context(Cons(Star(rInner), tlExp))), Concat(Star(rInner), r2), s2) assert(matchZipper(subZR1, starS1)) assert(matchZipper(subZR2, s2)) lemmaConcatenateContextMatchesConcatOfStrings(Context(List(rInner)), Context(Cons(Star(rInner), tlExp)), starS1, s2) @@ -903,8 +906,8 @@ object ZipperRegex { assert(matchZipper(subZR2, s2)) lemmaConcatenateContextMatchesConcatOfStrings(Context(List(rInner)), Context(Cons(Star(rInner), tlExp)), s1, s2) - theoremZipperRegexEquivInductZ(subZR1, List(Context(List(rInner))), rInner, s1) - theoremZipperRegexEquivInductZ(subZR2, List(Context(Cons(Star(rInner), tlExp))), Concat(Star(rInner), r2), s2) + theoremZipperRegexEquiv(subZR1, List(Context(List(rInner))), rInner, s1) + theoremZipperRegexEquiv(subZR2, List(Context(Cons(Star(rInner), tlExp))), Concat(Star(rInner), r2), s2) assert(matchR(rInner, s1)) assert(matchR(Concat(Star(rInner), r2), s2)) @@ -941,7 +944,7 @@ object ZipperRegex { assert(regexDepth(r) >= regexDepth(generalisedConcat(tlExp))) assert(zipperDepth(zl) >= zipperDepth(List(Context(tlExp)))) - theoremZipperRegexEquivInductZ(zTail, List(Context(tlExp)), generalisedConcat(tlExp), s) + theoremZipperRegexEquiv(zTail, List(Context(tlExp)), generalisedConcat(tlExp), s) assert(matchR(generalisedConcat(tlExp), s) == matchZipper(zTail, s)) assert(matchR(r2, s)) mainMatchTheorem(Star(rInner), Nil()) @@ -973,7 +976,7 @@ object ZipperRegex { assert(zDeriv == zDerivDown ++ zDerivUpUp) lemmaZipperConcatMatchesSameAsBothZippers(zDerivDown, zDerivUpUp, stl) assert(matchZipper(z, s) == matchZipper(zDerivUpUp, stl)) - theoremZipperRegexEquivInductZ(zTail, List(Context(tlExp)), generalisedConcat(tlExp), s) + theoremZipperRegexEquiv(zTail, List(Context(tlExp)), generalisedConcat(tlExp), s) assert(matchR(generalisedConcat(tlExp), s) == matchZipper(zTail, s)) assert(r2 == generalisedConcat(tlExp)) @@ -1000,7 +1003,7 @@ object ZipperRegex { assert(regexDepth(r) >= regexDepth(generalisedConcat(tlExp))) assert(zipperDepth(zl) >= zipperDepth(List(Context(tlExp)))) - theoremZipperRegexEquivInductZ(zTail, List(Context(tlExp)), generalisedConcat(tlExp), s) + theoremZipperRegexEquiv(zTail, List(Context(tlExp)), generalisedConcat(tlExp), s) assert(matchR(generalisedConcat(tlExp), s) == matchZipper(zTail, s)) @@ -1068,8 +1071,8 @@ object ZipperRegex { assert(matchR(rInner, s1)) assert(matchR(Star(rInner), s2)) - theoremZipperRegexEquivInductZ(subZR1, List(Context(List(rInner))), rInner, s1) - theoremZipperRegexEquivInductZ(subZR2, List(Context(List(Star(rInner)))), Star(rInner), s2) + theoremZipperRegexEquiv(subZR1, List(Context(List(rInner))), rInner, s1) + theoremZipperRegexEquiv(subZR2, List(Context(List(Star(rInner)))), Star(rInner), s2) assert(matchZipper(subZR1, s1)) assert(matchZipper(subZR2, s2)) lemmaConcatenateContextMatchesConcatOfStrings(Context(List(rInner)), Context(List(Star(rInner))), s1, s2) @@ -1085,8 +1088,8 @@ object ZipperRegex { assert(matchZipper(subZR1, s1)) assert(matchZipper(subZR2, s2)) lemmaConcatenateContextMatchesConcatOfStrings(Context(List(rInner)), Context(List(Star(rInner))), s1, s2) - theoremZipperRegexEquivInductZ(subZR1, List(Context(List(rInner))), rInner, s1) - theoremZipperRegexEquivInductZ(subZR2, List(Context(List(Star(rInner)))), Star(rInner), s2) + theoremZipperRegexEquiv(subZR1, List(Context(List(rInner))), rInner, s1) + theoremZipperRegexEquiv(subZR2, List(Context(List(Star(rInner)))), Star(rInner), s2) assert(matchR(rInner, s1)) assert(matchR(Star(rInner), s2)) lemmaTwoRegexMatchThenConcatMatchesConcatString(rInner, Star(rInner), s1, s2) @@ -1128,7 +1131,7 @@ object ZipperRegex { val witnessC = ListUtils.getWitness(zl, (cc: Context[C]) => generalisedConcat(cc.exprs) == witnessR) lemmaTotalDepthZipperLargerThanOfAnyContextMoreThanOne(zl, witnessC) assert(zipperDepthTotal(zl) > zipperDepthTotal(List(witnessC))) - theoremZipperRegexEquivInductZ(Set(witnessC), List(witnessC), witnessR, s) + theoremZipperRegexEquiv(Set(witnessC), List(witnessC), witnessR, s) assert(matchZipper(Set(witnessC), s)) SetUtils.lemmaContainsThenExists(z, witnessC, (c: Context[C]) => matchZipper(Set(c), s)) lemmaExistsMatchingContextThenMatchingString(zl, s) @@ -1143,7 +1146,7 @@ object ZipperRegex { assert(witnessC.exprs.forall(validRegex)) lemmaTotalDepthZipperLargerThanOfAnyContextMoreThanOne(zl, witnessC) assert(zipperDepthTotal(zl) > zipperDepthTotal(List(witnessC))) - theoremZipperRegexEquivInductZ(Set(witnessC), List(witnessC), generalisedConcat(witnessC.exprs), s) + theoremZipperRegexEquiv(Set(witnessC), List(witnessC), generalisedConcat(witnessC.exprs), s) lemmaZipperContainsContextUnfocusListContainsConcat(zl, witnessC) assert(unfocusZipperList(zl).contains(generalisedConcat(witnessC.exprs))) ListUtils.lemmaContainsThenExists(unfocusZipperList(zl), generalisedConcat(witnessC.exprs), (rr: Regex[C]) => validRegex(rr) && matchR(rr, s)) @@ -2379,6 +2382,13 @@ object VerifiedRegexMatcher { if (input.isEmpty) nullable(r) else matchRMem(derivativeStepMem(r, input.head)(cache: Cache[C]), input.tail) }.ensuring (res => res == matchR(r, input)) + def matchZipper[C](r: Regex[C], input: List[C]): Boolean = { + require(validRegex(r)) + decreases(input.size) + ghostExpr(ZipperRegex.theoremZipperRegexEquiv(ZipperRegex.focus(r), ZipperRegex.focus(r).toList, r, input)) + ZipperRegex.matchZipper(ZipperRegex.focus(r), input) + }.ensuring (res => res == matchR(r, input)) + // COMMENTED OUT BECAUSE NOT VERIFIED THROUGHOUT YET // def matchRMemSimp[C](r: Regex[C], input: List[C])(implicit cache: Cache[C]): Boolean = { // require(validRegex(r)) From a6b4cc416079f51a11f30e4cf1c747536dbbaadd Mon Sep 17 00:00:00 2001 From: Samuel Chassot Date: Tue, 26 Nov 2024 18:15:02 +0100 Subject: [PATCH 63/78] add benchmark + memoisation as extern for flatmap --- .../ch/epfl/benchmark/LexerBenchmark.scala | 3 - .../src/main/scala/ch/epfl/lexer/Main.scala | 8 +- .../src/main/scala/ch/epfl/lexer/Utils.scala | 102 +++++++++ .../scala/ch/epfl/lexer/VerifiedRegex.scala | 197 +++++++++++++++++- .../src/main/scala/test.worksheet.sc | 187 ++--------------- lexers/regex/verifiedlexer/stainless.conf | 2 +- 6 files changed, 308 insertions(+), 191 deletions(-) delete mode 100644 lexers/regex/verifiedlexer/src/main/scala/ch/epfl/benchmark/LexerBenchmark.scala diff --git a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/benchmark/LexerBenchmark.scala b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/benchmark/LexerBenchmark.scala deleted file mode 100644 index 67167205..00000000 --- a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/benchmark/LexerBenchmark.scala +++ /dev/null @@ -1,3 +0,0 @@ -object LexerBenchmark { - -} diff --git a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/Main.scala b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/Main.scala index 586bcef5..d563a60a 100644 --- a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/Main.scala +++ b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/Main.scala @@ -3,7 +3,7 @@ package ch.epfl.lexer import ch.epfl.map.MutableHashMap import ch.epfl.lexer.VerifiedRegex._ import ch.epfl.lexer.VerifiedRegexMatcher._ -import ch.epfl.lexer.Memoisation._ +import ch.epfl.lexer.MemoisationRegex._ import ch.epfl.lexer.ZipperRegex._ import ch.epfl.benchmark.RegexUtils._ import stainless.annotation._ @@ -74,7 +74,7 @@ def testZipperMatch(): Unit = { val s1 = "abababababababababbbababbababbbabab" println(s"Matching against '$s1'") val matchResR1 = matchR(r1, s1.toStainless) - val matchResZ1 = matchZipper(z1, s1.toStainless) + val matchResZ1 = ZipperRegex.matchZipper(z1, s1.toStainless) println(s"matchResR1 = $matchResR1") println(s"matchResZ1 = $matchResZ1") assert(matchResR1 == matchResZ1) @@ -87,7 +87,7 @@ def testZipperMatch(): Unit = { val s2 = "samuel.chassot@epfl.ch" println(s"Matching against '$s2'") val matchResR2 = matchR(r2, s2.toStainless) - val matchResZ2 = matchZipper(z2, s2.toStainless) + val matchResZ2 = ZipperRegex.matchZipper(z2, s2.toStainless) println(s"matchResR2 = $matchResR2") println(s"matchResZ2 = $matchResZ2") assert(matchResR2 == matchResZ2) @@ -96,7 +96,7 @@ def testZipperMatch(): Unit = { val s22 = "samuel.chassot@epfl" println(s"Matching against '$s22'") val matchResR22 = matchR(r2, s22.toStainless) - val matchResZ22 = matchZipper(z2, s22.toStainless) + val matchResZ22 = ZipperRegex.matchZipper(z2, s22.toStainless) println(s"matchResR22 = $matchResR22") println(s"matchResZ22 = $matchResZ22") assert(matchResR22 == matchResZ22) diff --git a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/Utils.scala b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/Utils.scala index 754293ea..a245ce63 100644 --- a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/Utils.scala +++ b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/Utils.scala @@ -342,6 +342,41 @@ object SetUtils { } }.ensuring(_ => lRes.forall(s.flatMap(f).toList.contains)) + @opaque + @ghost + @inlineOnce + def lemmaFlatMapWithExtEqualFunctionsOnSetThenSame[A, B](s: Set[A], f: A => Set[B], g: A => Set[B]): Unit = { + require(s.forall(a => f(a) == g(a))) + decreases(s.toList.size) + s.toList match { + case Cons(h, t) => { + assert(s == Set(h) ++ t.content) + lemmaFlatMapAssociative(Set(h), t.content, f) + lemmaFlatMapAssociative(Set(h), t.content, g) + assert(t.forall(a => f(a) == g(a))) + ListUtils.lemmaForallThenOnContent(t, a => f(a) == g(a)) + assert(f(h) == g(h)) + + assert(t.content.forall(a => f(a) == g(a))) + ListSpecs.forallContained(s.toList, a => f(a) == g(a), h) + + lemmaToListSizeBiggerThanTailContentSize(s) + assert(t.content.toList.size < s.toList.size) + lemmaFlatMapWithExtEqualFunctionsOnSetThenSame(t.content, f, g) + assert(t.content.flatMap(f) == t.content.flatMap(g)) + lemmaFlatMapOnSingletonSet(Set(h), h, f) + lemmaFlatMapOnSingletonSet(Set(h), h, g) + assert(Set(h).flatMap(f) == Set(h).flatMap(g)) + } + case Nil() => { + lemmaFlatMapOnEmptySetIsEmpty(s, f) + lemmaFlatMapOnEmptySetIsEmpty(s, g) + assert(s.flatMap(f).isEmpty) + assert(s.flatMap(g).isEmpty) + assert(s.flatMap(f) == s.flatMap(g)) + } + } + }.ensuring(_ => s.flatMap(f) == s.flatMap(g)) @ghost def getWitness[A](s: Set[A], p: A => Boolean): A = { @@ -439,6 +474,31 @@ object SetUtils { }.ensuring (_ => s.flatMap(f).forall(p)) + @inlineOnce + @opaque + @ghost + def lemmaToListSizeBiggerThanTailContentSize[B](s: Set[B]): Unit = { + require(!s.isEmpty) + s.toList match { + case Cons(hd, tl) if tl.isEmpty => () + case Cons(hd, tl) => { + val l = s.toList + ListUtils.lemmaNoDuplicateMinusHeadSameAsTail(l, hd) + assert(l == Cons(hd, tl)) + assert(l - hd == tl) + assert(tl == s.toList - hd) + ListUtils.lemmaRemoveElmtContainedSizeSmaller(s.toList, hd) + } + case Nil() => check(false) + } + + }.ensuring(_ => s.toList.size > s.toList.tail.size) + + // @inlineOnce + // @opaque + // @ghost + // def lemmaSubsetContent + } object ListUtils { @@ -713,6 +773,43 @@ object ListUtils { }.ensuring (_ => (l1 ++ l2) ++ l3 == l1 ++ (l2 ++ l3)) + @inlineOnce + @opaque + @ghost + def lemmaNoDuplicateMinusHeadSameAsTail[B](l: List[B], e: B): Unit = { + require(ListSpecs.noDuplicate(l)) + require(l.contains(e)) + require(l.head == e) + l match { + case Cons(h, t) => { + assert(h == e) + assert(!t.contains(e)) + lemmaNotContainedThenRemoveSameList(t, e) + assert(t - e == t) + + } + case Nil() => check(false) + } + + }.ensuring(_ => l - e == l.tail) + + @inlineOnce + @opaque + @ghost + def lemmaNotContainedThenRemoveSameList[B](l: List[B], e: B): Unit = { + require(ListSpecs.noDuplicate(l)) + require(!l.contains(e)) + decreases(l) + l match { + case Cons(h, t) => { + lemmaNotContainedThenRemoveSameList(t, e) + assert(!t.contains(e)) + assert(t - e == t) + } + case Nil() => () + } + }.ensuring(_ => l == l - e) + @inlineOnce @opaque @ghost @@ -1180,6 +1277,11 @@ object ListUtils { } }.ensuring (_ => lIn.size <= l.size) + // @inlineOnce + // @opaque + // @ghost + // def lemmaSameContentNoDuplicateThenSameSize + @inlineOnce @opaque @ghost diff --git a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedRegex.scala b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedRegex.scala index 9e70217d..3b8d739a 100644 --- a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedRegex.scala +++ b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedRegex.scala @@ -21,7 +21,7 @@ import stainless.lang.StaticChecks._ import stainless.annotation.isabelle.lemma // import ch.epfl.map.OptimisedChecks.* -object Memoisation { +object MemoisationRegex { import VerifiedRegex._ import VerifiedRegexMatcher._ @@ -35,6 +35,8 @@ object Memoisation { ) } + def empty[C](hashF: Hashable[(Regex[C], C)]): Cache[C] = Cache(MutableHashMap.getEmptyHashMap[(Regex[C], C), Regex[C]](k => EmptyLang[C](), hashF)) + @mutable final case class Cache[C](private val cache: HashMap[(Regex[C], C), Regex[C]]) { require(validCacheMap(cache)) @@ -97,6 +99,145 @@ object Memoisation { } } +object MemoisationZipper { + import ZipperRegex._ + import VerifiedRegex.Regex + + @ghost def validCacheMapUp[C](m: HashMap[(Context[C], C), Zipper[C]]): Boolean = { + m.valid && + TupleListOpsGenK.invariantList(m.map.toList) && // Why is this needed? Without it does not verify in update... + m.map.forall(_ match { + case ((ctx, a), res) => + res == derivationStepZipperUp(ctx, a) + } + ) + } + + @ghost def validCacheMapDown[C](m: HashMap[(Regex[C], Context[C], C), Zipper[C]]): Boolean = { + m.valid && + TupleListOpsGenK.invariantList(m.map.toList) && // Why is this needed? Without it does not verify in update... + m.map.forall(_ match { + case ((r, ctx, a), res) => + res == derivationStepZipperDown(r, ctx, a) + } + ) + } + + @mutable + final case class CacheUp[C](private val cache: HashMap[(Context[C], C), Zipper[C]]) { + require(validCacheMapUp(cache)) + + @ghost def valid: Boolean = validCacheMapUp(cache) + + @ghost + def lemmaIfInCacheThenValid(ctx: Context[C], a: C): Unit = { + require(validCacheMapUp(cache)) + if (cache.contains((ctx, a))) { + ghostExpr({ + MutableHashMap.lemmaForallPairsThenForLookup( + cache, + (ctx, a), { + _ match { + case ((ctxx, aa), res) => + res == derivationStepZipperUp(ctxx, aa) + } + } + ) + }) + } + }.ensuring (_ => cache.contains((ctx, a)) ==> (derivationStepZipperUp(ctx, a) == cache((ctx, a)))) + + def contains(ctx: Context[C], a: C): Boolean = { + require(validCacheMapUp(cache)) + cache.contains((ctx, a)) + } + + def get(ctx: Context[C], a: C): Option[Zipper[C]] = { + require(validCacheMapUp(cache)) + + if (cache.contains((ctx, a))) { + ghostExpr(lemmaIfInCacheThenValid(ctx, a)) + Some(cache((ctx, a))) + } else { + None() + } + }.ensuring (res => res.isEmpty || res.get == derivationStepZipperUp(ctx, a)) + + def update(ctx: Context[C], a: C, res: Zipper[C]): Unit = { + require(validCacheMapUp(cache)) + require(res == derivationStepZipperUp(ctx, a)) + + ghostExpr(MutableHashMap.lemmaUpdatePreservesForallPairs(cache, (ctx, a), res, { + _ match { + case ((ctxx, aa), res) => + res == derivationStepZipperUp(ctxx, aa) + } + })) + + val _ = cache.update((ctx, a), res) + () + + }.ensuring (_ => validCacheMapUp(this.cache)) + } + + @mutable + final case class CacheDown[C](private val cache: HashMap[(Regex[C], Context[C], C), Zipper[C]]) { + require(validCacheMapDown(cache)) + + @ghost def valid: Boolean = validCacheMapDown(cache) + + @ghost + def lemmaIfInCacheThenValid(r: Regex[C], ctx: Context[C], a: C): Unit = { + require(validCacheMapDown(cache)) + if (cache.contains((r, ctx, a))) { + ghostExpr({ + MutableHashMap.lemmaForallPairsThenForLookup( + cache, + (r, ctx, a), { + _ match { + case ((rr, ctxx, aa), res) => + res == derivationStepZipperDown(rr, ctxx, aa) + } + } + ) + }) + } + }.ensuring (_ => cache.contains((r, ctx, a)) ==> (derivationStepZipperDown(r, ctx, a) == cache((r, ctx, a)))) + + def contains(r: Regex[C], ctx: Context[C], a: C): Boolean = { + require(validCacheMapDown(cache)) + cache.contains((r, ctx, a)) + } + + def get(r: Regex[C], ctx: Context[C], a: C): Option[Zipper[C]] = { + require(validCacheMapDown(cache)) + + if (cache.contains((r, ctx, a))) { + ghostExpr(lemmaIfInCacheThenValid(r, ctx, a)) + Some(cache((r, ctx, a))) + } else { + None() + } + }.ensuring (res => res.isEmpty || res.get == derivationStepZipperDown(r, ctx, a)) + + def update(r: Regex[C], ctx: Context[C], a: C, res: Zipper[C]): Unit = { + require(validCacheMapDown(cache)) + require(res == derivationStepZipperDown(r, ctx, a)) + + ghostExpr(MutableHashMap.lemmaUpdatePreservesForallPairs(cache, (r, ctx, a), res, { + _ match { + case ((rr, ctxx, aa), res) => + res == derivationStepZipperDown(rr, ctxx, aa) + } + })) + + val _ = cache.update((r, ctx, a), res) + () + + }.ensuring (_ => validCacheMapDown(this.cache)) + } +} + object VerifiedRegex { sealed trait Regex[C] case class ElementMatch[C](c: C) extends Regex[C] @@ -269,6 +410,7 @@ object ZipperRegex { import VerifiedRegex.* import VerifiedRegexMatcher.* import stainless.lang.Set + import MemoisationZipper.* /** * Context[C] represent sequences of expressions @@ -426,15 +568,54 @@ object ZipperRegex { z.map(cz => cz.concat(c)) } + // MEMOISED ----------------------------------------------------------------------------------------------------- + def derivationStepZipperUpMem[C](context: Context[C], a: C)(implicit cacheUp: CacheUp[C], cacheDown: CacheDown[C]): Zipper[C] = { + decreases(context.exprs.size) + cacheUp.get(context, a) match { + case Some(res) => res + case None() => { + val res: Zipper[C] = context.exprs match { + case Cons(right, parent) if nullable(right) => derivationStepZipperDownMem(right, Context(parent), a) ++ derivationStepZipperUpMem(Context(parent), a) + case Cons(right, parent) => derivationStepZipperDownMem(right, Context(parent), a) + case Nil() => Set() + } + cacheUp.update(context, a, res) + res + } + } + }.ensuring(res => res == derivationStepZipperUp(context, a)) - // PROOFS ----------------------------------------------------------------------------------------------------- + def derivationStepZipperDownMem[C](expr: Regex[C], context: Context[C], a: C)(implicit cacheDown: CacheDown[C]): Zipper[C] = { + require(validRegex(expr)) + decreases(regexDepth(expr)) + cacheDown.get(expr, context, a) match { + case Some(res) => res + case None() => { + val res: Zipper[C] = expr match { + case ElementMatch(c) if c == a => Set(context) + case Union(rOne, rTwo) => derivationStepZipperDownMem(rOne, context, a) ++ derivationStepZipperDownMem(rTwo, context, a) + case Concat(rOne, rTwo) if nullable(rOne) => derivationStepZipperDownMem(rOne, context.prepend(rTwo), a) ++ derivationStepZipperDownMem(rTwo, context, a) + case Concat(rOne, rTwo) => derivationStepZipperDownMem(rOne, context.prepend(rTwo), a) + case Star(rInner) => derivationStepZipperDownMem(rInner, context.prepend(Star(rInner)), a) + case _ => Set() + } + cacheDown.update(expr, context, a, res) + res + } + } + }.ensuring(res => res == derivationStepZipperDown(expr, context, a)) @extern - @ghost - @opaque - def assume(c: Boolean): Unit = { - () - }.ensuring(_ => c) + def derivationStepZipperMem[C](z: Zipper[C], a: C)(implicit cacheUp: CacheUp[C], cacheDown: CacheDown[C]): Zipper[C] = { + ghostExpr(SetUtils.lemmaFlatMapWithExtEqualFunctionsOnSetThenSame(z, (c: Context[C]) => derivationStepZipperUpMem(c, a)(snapshot(cacheUp), snapshot(cacheDown)), (c: Context[C]) => derivationStepZipperUp(c, a))) + + def derivUpMem(c: Context[C]): Zipper[C] = derivationStepZipperUpMem(c, a) + + z.flatMap(derivUpMem) // rejected by stainless because of effects in the lambda's body + }.ensuring(res => res == derivationStepZipper(z, a)) + + + // PROOFS ----------------------------------------------------------------------------------------------------- @ghost @opaque @@ -2269,7 +2450,7 @@ object ZipperRegex { object VerifiedRegexMatcher { import VerifiedRegex._ import ListUtils._ - import Memoisation._ + import MemoisationRegex._ def derivativeStep[C](r: Regex[C], a: C): Regex[C] = { require(validRegex(r)) diff --git a/lexers/regex/verifiedlexer/src/main/scala/test.worksheet.sc b/lexers/regex/verifiedlexer/src/main/scala/test.worksheet.sc index b028c0a9..68ed72d5 100644 --- a/lexers/regex/verifiedlexer/src/main/scala/test.worksheet.sc +++ b/lexers/regex/verifiedlexer/src/main/scala/test.worksheet.sc @@ -1,178 +1,15 @@ -import scala.collection.immutable.LazyList.cons -object Utils { - def maxBigInt(a: BigInt, b: BigInt): BigInt = if (a >= b) a else b - def maxLong(a: Long, b: Long): Long = if (a >= b) a else b -} +import ch.epfl.benchmark.RegexUtils.* +import ch.epfl.lexer.VerifiedRegexMatcher.* +import ch.epfl.lexer.VerifiedRegex.* +import scala.util.Random +import benchmark.RegexBenchmarkUtil -trait IDGiver[C] { - def id(c: C): Long - val MAX_ID = Int.MaxValue - // @law def smallEnough(c: C): Boolean = id(c) >= 0 && id(c) <= MAX_ID - // @law def uniqueness(c1: C, c2: C): Boolean = if (id(c1) == id(c2)) then c1 == c2 else true -} +RegexBenchmarkUtil.abStar -abstract sealed class Regex[C] {} -case class ElementMatch[C](c: C) extends Regex[C] -case class Star[C](reg: Regex[C]) extends Regex[C] -case class Union[C](regOne: Regex[C], regTwo: Regex[C]) extends Regex[C] -case class Concat[C](regOne: Regex[C], regTwo: Regex[C]) extends Regex[C] +RegexBenchmarkUtil.abStar.asString() -/** Regex that accepts only the empty string: represents the language {""} - */ -case class EmptyExpr[C]() extends Regex[C] - -/** Regex that accepts nothing: represents the empty language - */ -case class EmptyLang[C]() extends Regex[C] - -val INT_MAX_VALUE: BigInt = 2147483647 -val INT_MAX_VALUE_L: Long = 2147483647L - -def nullable[C](r: Regex[C]): Boolean = { - r match { - case EmptyExpr() => true - case EmptyLang() => false - case ElementMatch(c) => false - case Star(r) => true - case Union(rOne, rTwo) => nullable(rOne) || nullable(rTwo) - case Concat(rOne, rTwo) => nullable(rOne) && nullable(rTwo) - } - } - - def isEmptyExpr[C](r: Regex[C]): Boolean = { - r match { - case EmptyExpr() => true - case _ => false - } - } - def isEmptyLang[C](r: Regex[C]): Boolean = { - r match { - case EmptyLang() => true - case _ => false - } - } - def isElementMatch[C](r: Regex[C]): Boolean = { - r match { - case ElementMatch(_) => true - case _ => false - } - } - def elementMatchIsChar[C](r: Regex[C], c: C): Boolean = { - require(isElementMatch(r)) - r match { - case ElementMatch(cc) => c == cc - } - } - def isStar[C](r: Regex[C]): Boolean = { - r match { - case Star(_) => true - case _ => false - } - } - def isUnion[C](r: Regex[C]): Boolean = { - r match { - case Union(_, _) => true - case _ => false - } - } - def unionInnersEquals[C](r: Regex[C], r1: Regex[C], r2: Regex[C]): Boolean = { - require(isUnion(r)) - r match { - case Union(rOne, rTwo) => r1 == rOne && r2 == rTwo - } - } - - def isConcat[C](r: Regex[C]): Boolean = { - r match { - case Concat(_, _) => true - case _ => false - } - } - -def validRegex[C](r: Regex[C]): Boolean = r match { - case ElementMatch(c) => true - case Star(r) => !nullable(r) && !isEmptyLang(r) && validRegex(r) - case Union(rOne, rTwo) => validRegex(rOne) && validRegex(rTwo) - case Concat(rOne, rTwo) => validRegex(rOne) && validRegex(rTwo) - case EmptyExpr() => true - case EmptyLang() => true -} - -def regexDepth[C](r: Regex[C]): BigInt = { - // decreases(r) - r match { - case ElementMatch(c) => BigInt(1) - case Star(r) => BigInt(1) + regexDepth(r) - case Union(rOne, rTwo) => BigInt(1) + Utils.maxBigInt(regexDepth(rOne), regexDepth(rTwo)) - case Concat(rOne, rTwo) => BigInt(1) + Utils.maxBigInt(regexDepth(rOne), regexDepth(rTwo)) - case EmptyExpr() => BigInt(1) - case EmptyLang() => BigInt(1) - } -} ensuring (res => - res > 0 && (r match { - case Union(rOne, rTwo) => res > regexDepth(rOne) && res > regexDepth(rTwo) - case Concat(rOne, rTwo) => res > regexDepth(rOne) && res > regexDepth(rTwo) - case Star(r) => res > regexDepth(r) - case _ => res == BigInt(1) - }) -) - -def regexDepthLong[C](r: Regex[C]): Long = { - require(regexDepth(r) < INT_MAX_VALUE) - // decreases(r) - r match { - case ElementMatch(c) => 1L - case Star(r) => 1L + regexDepthLong(r) - case Union(rOne, rTwo) => 1L + Utils.maxLong(regexDepthLong(rOne), regexDepthLong(rTwo)) - case Concat(rOne, rTwo) => 1L + Utils.maxLong(regexDepthLong(rOne), regexDepthLong(rTwo)) - case EmptyExpr() => 1L - case EmptyLang() => 1L - } -} ensuring (res => - res > 0 && (r match { - case Union(rOne, rTwo) => res > regexDepthLong(rOne) && res > regexDepthLong(rTwo) - case Concat(rOne, rTwo) => res > regexDepthLong(rOne) && res > regexDepthLong(rTwo) - case Star(r) => res > regexDepthLong(r) - case _ => res == 1L - }) -) - -def getUniqueId[C](r: Regex[C])(implicit idC: IDGiver[C]): Long = { - require(regexDepth(r) <= 30) - // decreases(r) - r match { - case ElementMatch(c) => - // assert(idC.smallEnough(c)) - 2L * idC.id(c) - case Star(r) => 3L + getUniqueId(r) - case Union(rOne, rTwo) => 5L + (getUniqueId(rOne) + getUniqueId(rTwo)) - case Concat(rOne, rTwo) => 7L + (getUniqueId(rOne) + getUniqueId(rTwo)) - case EmptyExpr() => 11L - case EmptyLang() => 13L - } -} ensuring (res => res >= 0) - -object CharIDGiver extends IDGiver[Char] { - def id(c: Char): Long = c.toLong -} - -def constructRegex(l: List[Char]): Regex[Char] = { - l match { - case Nil => EmptyExpr() - case head :: Nil => ElementMatch(head) - case head :: tail => Concat(ElementMatch(head), constructRegex(tail)) - } -} - -getUniqueId(Star(ElementMatch('a')))(CharIDGiver) -// Regexc representing the regex (abcd + gejho)* -val r: Regex[Char] = Concat(Star(Union(Concat(Concat(Concat(ElementMatch('a'), ElementMatch('b')), ElementMatch('c')), ElementMatch('d')), Concat(Concat(ElementMatch('g'), ElementMatch('e')), Concat(ElementMatch('j'), ElementMatch('h'))))), ElementMatch('o')) -getUniqueId(r)(CharIDGiver) -regexDepth(r) - -val r21 = constructRegex(List('a', 'b', 'c', 'd', 'g', 'e', 'j', 'h', 'o')) -val r22 = constructRegex(List('y', 'v', 'n', 'b', 's', 'l', 'u', 't', 'i', 'o', 'n')) -val r23 = constructRegex(List('a', 'b', 'c', 'd', 'g', 'e', 'j', 'h', 'o', 'y', 'v', 'n', 'b', 's', 'l', 'u', 't', 'i', 'o', 'n')) -val r2 = Star(Concat(r23, Star(Union(Union(r21, r22), r23)))) -getUniqueId(r2)(CharIDGiver) -regexDepth(r2) \ No newline at end of file +RegexBenchmarkUtil.abStar_Accepting_strings(5).mkString("") +RegexBenchmarkUtil.abStar_Accepting_strings(10).mkString("") +RegexBenchmarkUtil.abStar_Accepting_strings(15).mkString("") +RegexBenchmarkUtil.abStar_Accepting_strings(20).mkString("") +RegexBenchmarkUtil.abStar_Accepting_strings(25).mkString("") \ No newline at end of file diff --git a/lexers/regex/verifiedlexer/stainless.conf b/lexers/regex/verifiedlexer/stainless.conf index 74b6c9d4..d03588fd 100644 --- a/lexers/regex/verifiedlexer/stainless.conf +++ b/lexers/regex/verifiedlexer/stainless.conf @@ -3,7 +3,7 @@ vc-cache = true # debug = ["verification", "smt"] -timeout = 40 +timeout = 5 check-models = false print-ids = false print-types = false From 0dba43902127ebf7fc779ea2f4bdeb3232d21501 Mon Sep 17 00:00:00 2001 From: Samuel Chassot Date: Wed, 27 Nov 2024 16:49:17 +0100 Subject: [PATCH 64/78] add verification for memoisation --- .../src/main/scala/ch/epfl/lexer/Utils.scala | 120 +++++++++++++++++- lexers/regex/verifiedlexer/stainless.conf | 2 +- lexers/regex/verifiedlexer/verify.sh | 2 +- 3 files changed, 118 insertions(+), 6 deletions(-) diff --git a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/Utils.scala b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/Utils.scala index a245ce63..66e79192 100644 --- a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/Utils.scala +++ b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/Utils.scala @@ -361,6 +361,11 @@ object SetUtils { ListSpecs.forallContained(s.toList, a => f(a) == g(a), h) lemmaToListSizeBiggerThanTailContentSize(s) + + assert(s.toList.size > s.toList.tail.size) + assert(s.toList.tail.size == t.size) + ListUtils.lemmaNoDuplicateThenContentToListSameSize(t) + assert(t.size == t.content.toList.size) assert(t.content.toList.size < s.toList.size) lemmaFlatMapWithExtEqualFunctionsOnSetThenSame(t.content, f, g) assert(t.content.flatMap(f) == t.content.flatMap(g)) @@ -793,6 +798,47 @@ object ListUtils { }.ensuring(_ => l - e == l.tail) + @inlineOnce + @opaque + @ghost + def lemmaNoDuplicateThenContentToListSameSize[B](l: List[B]): Unit = { + require(ListSpecs.noDuplicate(l)) + decreases(l) + l match { + case Cons(hd, tl) => { + lemmaNoDuplicateThenContentToListSameSize(tl) + assert(tl.content.toList.size == tl.size) + assert(tl.content.toList.size < l.size) + assert(!tl.content.contains(hd)) + assert(!tl.contains(hd)) + assert(Cons(hd, tl).size == tl.size + 1) + assert(Cons(hd, tl).content == tl.content ++ Set(hd)) + if((tl.content ++ Set(hd)).toList.size < tl.size + 1){ + assert((tl.content ++ Set(hd)) == l.content) + ListSpecs.subsetContains((tl.content ++ Set(hd)).toList, l) + ListUtils.lemmaNoDuplicateSmallerListExistsElmtNotInOther(l, (tl.content ++ Set(hd)).toList) + assert(l.exists(e => !(tl.content ++ Set(hd)).toList.contains(e))) + val witness = ListUtils.getWitness(l, e => !(tl.content ++ Set(hd)).toList.contains(e)) + assert(l.contains(witness)) + assert(!(tl.content ++ Set(hd)).toList.contains(witness)) + check(false) + } + if((tl.content ++ Set(hd)).toList.size > tl.size + 1){ + assert((tl.content ++ Set(hd)) == l.content) + ListSpecs.subsetContains(l, (tl.content ++ Set(hd)).toList) + ListUtils.lemmaNoDuplicateSmallerListExistsElmtNotInOther((tl.content ++ Set(hd)).toList, l) + assert((tl.content ++ Set(hd)).toList.exists(e => !l.contains(e))) + val witness = ListUtils.getWitness((tl.content ++ Set(hd)).toList, e => !l.contains(e)) + assert(!l.contains(witness)) + assert((tl.content ++ Set(hd)).toList.contains(witness)) + check(false) + } + assert((tl.content ++ Set(hd)).toList.size == tl.size + 1) + } + case Nil() => () + } + }.ensuring(_ => l.content.toList.size == l.size) + @inlineOnce @opaque @ghost @@ -1277,10 +1323,76 @@ object ListUtils { } }.ensuring (_ => lIn.size <= l.size) - // @inlineOnce - // @opaque - // @ghost - // def lemmaSameContentNoDuplicateThenSameSize + @inlineOnce + @opaque + @ghost + def lemmaNoDuplicateSmallerListExistsElmtNotInOther[B](l1: List[B], l2: List[B]): Unit = { + require(ListOps.noDuplicate(l1)) + require(ListOps.noDuplicate(l2)) + require(l1.size > l2.size) + require(l2.forall(e => l1.contains(e))) + decreases(l1.size) + + l1 match { + case Cons(hd, tl) if l2.contains(hd) => { + lemmaRemoveElmtMaintainsNoDuplicate(l2, hd) + lemmaRemoveElmtMaintainsForall(l2, hd, e => l1.contains(e)) + lemmaRemoveElmtNoDuplicateRemoveOne(l2, hd) + assert((l2 - hd).forall(e => l1.contains(e))) + lemmaForallContainsThenForTailIfContainsNotHead((l2 - hd), l1, hd) + assert((l2 - hd).forall(e => tl.contains(e))) + lemmaNoDuplicateSmallerListExistsElmtNotInOther(tl, l2 - hd) + assert(tl.exists(e => !(l2 - hd).contains(e))) + val witness = ListUtils.getWitness(tl, e => !(l2 - hd).contains(e)) + assert(l1.contains(witness)) + assert(witness != hd) + assert(!l2.contains(witness)) + ListUtils.lemmaContainsThenExists(l1, witness, e => !l2.contains(e)) + + } + case Cons(hd, tl) if !l2.contains(hd) => () + case Nil() => () + } + }.ensuring (_ => l1.exists(e => !l2.contains(e))) + + @inlineOnce + @opaque + @ghost + def lemmaForallContainsThenForTailIfContainsNotHead[B](@induct l: List[B], refL: List[B], refHd: B): Unit = { + require(!refL.isEmpty) + require(l.forall(e => refL.contains(e))) + require(refHd == refL.head) + require(!l.contains(refHd)) + }.ensuring(_ => l.forall(e => refL.tail.contains(e))) + + @inlineOnce + @opaque + @ghost + def lemmaRemoveElmtMaintainsNoDuplicate[B](@induct l: List[B], e: B): Unit = { + require(ListSpecs.noDuplicate(l)) + }.ensuring(_ => ListSpecs.noDuplicate(l - e)) + + @inlineOnce + @opaque + @ghost + def lemmaRemoveElmtMaintainsForall[B](@induct l: List[B], e: B, p: B => Boolean): Unit = { + require(ListSpecs.noDuplicate(l)) + require(l.forall(p)) + }.ensuring(_ => (l - e).forall(p)) + + @inlineOnce + @opaque + @ghost + def lemmaRemoveElmtNoDuplicateRemoveOne[B](l: List[B], e: B): Unit = { + require(ListSpecs.noDuplicate(l)) + require(l.contains(e)) + decreases(l) + l match { + case Cons(hd, tl) if hd != e => lemmaRemoveElmtNoDuplicateRemoveOne(tl, e) + case Cons(hd, tl) if hd == e => lemmaNoDuplicateMinusHeadSameAsTail(l, e) + case Nil() => check(false) + } + }.ensuring(_ => (l - e).size == l.size - 1) @inlineOnce @opaque diff --git a/lexers/regex/verifiedlexer/stainless.conf b/lexers/regex/verifiedlexer/stainless.conf index d03588fd..74b6c9d4 100644 --- a/lexers/regex/verifiedlexer/stainless.conf +++ b/lexers/regex/verifiedlexer/stainless.conf @@ -3,7 +3,7 @@ vc-cache = true # debug = ["verification", "smt"] -timeout = 5 +timeout = 40 check-models = false print-ids = false print-types = false diff --git a/lexers/regex/verifiedlexer/verify.sh b/lexers/regex/verifiedlexer/verify.sh index 1753a525..6323b4ec 100755 --- a/lexers/regex/verifiedlexer/verify.sh +++ b/lexers/regex/verifiedlexer/verify.sh @@ -4,5 +4,5 @@ stainless-dotty\ src/main/scala/ch/epfl/lexer/Utils.scala\ src/main/scala/ch/epfl/map/*\ --config-file=stainless.conf\ - -D-parallel=12 --functions=Memoisation._,VerifiedRegex_,VerifiedRegexMatcher._,VerifiedLexer._,ListUtils._\ + -D-parallel=12 --functions=Memoisation._,VerifiedRegex_,ZipperRegex._,VerifiedRegexMatcher._,VerifiedLexer._,ListUtils._,SetUtils._\ $1 From 370e77ca818d43f663081e5da936af4f40e39d15 Mon Sep 17 00:00:00 2001 From: Samuel Chassot Date: Wed, 27 Nov 2024 16:49:33 +0100 Subject: [PATCH 65/78] add benchmarks and some results --- .../Regex_benchmark_results.ipynb | 336 ++++++++++++++++++ .../ch/epfl/benchmark/RegexBenchmark.scala | 103 ++++++ 2 files changed, 439 insertions(+) create mode 100644 lexers/regex/verifiedlexer/benchmark_results/Regex_benchmark_results.ipynb create mode 100644 lexers/regex/verifiedlexer/src/main/scala/ch/epfl/benchmark/RegexBenchmark.scala diff --git a/lexers/regex/verifiedlexer/benchmark_results/Regex_benchmark_results.ipynb b/lexers/regex/verifiedlexer/benchmark_results/Regex_benchmark_results.ipynb new file mode 100644 index 00000000..dc8a6f55 --- /dev/null +++ b/lexers/regex/verifiedlexer/benchmark_results/Regex_benchmark_results.ipynb @@ -0,0 +1,336 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# First results" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Textual data" + ] + }, + { + "cell_type": "code", + "execution_count": 37, + "metadata": {}, + "outputs": [], + "source": [ + "# [info] RegexBenchmark.abStar_accepting_regex 5 avgt 2 0.967 us/op\n", + "# [info] RegexBenchmark.abStar_accepting_regex 10 avgt 2 5.389 us/op\n", + "# [info] RegexBenchmark.abStar_accepting_regex 15 avgt 2 15.300 us/op\n", + "# [info] RegexBenchmark.abStar_accepting_regex 20 avgt 2 32.653 us/op\n", + "# [info] RegexBenchmark.abStar_accepting_regex 25 avgt 2 66.284 us/op\n", + "# [info] RegexBenchmark.abStar_accepting_regex 30 avgt 2 128.547 us/op\n", + "# [info] RegexBenchmark.abStar_accepting_regex 35 avgt 2 217.033 us/op\n", + "# [info] RegexBenchmark.abStar_accepting_regex 40 avgt 2 342.390 us/op\n", + "# [info] RegexBenchmark.abStar_accepting_regex 45 avgt 2 501.070 us/op\n", + "# [info] RegexBenchmark.abStar_accepting_regex 50 avgt 2 716.979 us/op\n", + "# [info] RegexBenchmark.abStar_accepting_regex 55 avgt 2 975.483 us/op\n", + "# [info] RegexBenchmark.abStar_accepting_regex 60 avgt 2 1282.785 us/op\n", + "# [info] RegexBenchmark.abStar_accepting_regex 65 avgt 2 1648.838 us/op\n", + "# [info] RegexBenchmark.abStar_accepting_regex 70 avgt 2 2081.577 us/op\n", + "# [info] RegexBenchmark.abStar_accepting_regex 75 avgt 2 2565.625 us/op\n", + "# [info] RegexBenchmark.abStar_accepting_regex 80 avgt 2 3120.272 us/op\n", + "# [info] RegexBenchmark.abStar_accepting_regex_mem 5 avgt 2 4.212 us/op\n", + "# [info] RegexBenchmark.abStar_accepting_regex_mem 10 avgt 2 28.118 us/op\n", + "# [info] RegexBenchmark.abStar_accepting_regex_mem 15 avgt 2 84.303 us/op\n", + "# [info] RegexBenchmark.abStar_accepting_regex_mem 20 avgt 2 189.262 us/op\n", + "# [info] RegexBenchmark.abStar_accepting_regex_mem 25 avgt 2 352.038 us/op\n", + "# [info] RegexBenchmark.abStar_accepting_regex_mem 30 avgt 2 603.719 us/op\n", + "# [info] RegexBenchmark.abStar_accepting_regex_mem 35 avgt 2 914.989 us/op\n", + "# [info] RegexBenchmark.abStar_accepting_regex_mem 40 avgt 2 1349.742 us/op\n", + "# [info] RegexBenchmark.abStar_accepting_regex_mem 45 avgt 2 1858.301 us/op\n", + "# [info] RegexBenchmark.abStar_accepting_regex_mem 50 avgt 2 2569.797 us/op\n", + "# [info] RegexBenchmark.abStar_accepting_regex_mem 55 avgt 2 3370.891 us/op\n", + "# [info] RegexBenchmark.abStar_accepting_regex_mem 60 avgt 2 4253.523 us/op\n", + "# [info] RegexBenchmark.abStar_accepting_regex_mem 65 avgt 2 5330.279 us/op\n", + "# [info] RegexBenchmark.abStar_accepting_regex_mem 70 avgt 2 6681.398 us/op\n", + "# [info] RegexBenchmark.abStar_accepting_regex_mem 75 avgt 2 8247.552 us/op\n", + "# [info] RegexBenchmark.abStar_accepting_regex_mem 80 avgt 2 10033.315 us/op\n", + "# [info] RegexBenchmark.abStar_accepting_zipper 5 avgt 2 0.360 us/op\n", + "# [info] RegexBenchmark.abStar_accepting_zipper 10 avgt 2 0.732 us/op\n", + "# [info] RegexBenchmark.abStar_accepting_zipper 15 avgt 2 1.192 us/op\n", + "# [info] RegexBenchmark.abStar_accepting_zipper 20 avgt 2 1.452 us/op\n", + "# [info] RegexBenchmark.abStar_accepting_zipper 25 avgt 2 2.000 us/op\n", + "# [info] RegexBenchmark.abStar_accepting_zipper 30 avgt 2 2.217 us/op\n", + "# [info] RegexBenchmark.abStar_accepting_zipper 35 avgt 2 2.643 us/op\n", + "# [info] RegexBenchmark.abStar_accepting_zipper 40 avgt 2 2.932 us/op\n", + "# [info] RegexBenchmark.abStar_accepting_zipper 45 avgt 2 3.417 us/op\n", + "# [info] RegexBenchmark.abStar_accepting_zipper 50 avgt 2 3.923 us/op\n", + "# [info] RegexBenchmark.abStar_accepting_zipper 55 avgt 2 4.203 us/op\n", + "# [info] RegexBenchmark.abStar_accepting_zipper 60 avgt 2 4.915 us/op\n", + "# [info] RegexBenchmark.abStar_accepting_zipper 65 avgt 2 4.958 us/op\n", + "# [info] RegexBenchmark.abStar_accepting_zipper 70 avgt 2 5.331 us/op\n", + "# [info] RegexBenchmark.abStar_accepting_zipper 75 avgt 2 5.596 us/op\n", + "# [info] RegexBenchmark.abStar_accepting_zipper 80 avgt 2 6.061 us/op" + ] + }, + { + "cell_type": "code", + "execution_count": 38, + "metadata": {}, + "outputs": [], + "source": [ + "regex_data = [( 5, 0.967),\n", + " (10, 5.389),\n", + " (15, 15.300),\n", + " (20, 32.653),\n", + " (25, 66.284),\n", + " (30, 128.547),\n", + " (35, 217.033),\n", + " (40, 342.390),\n", + " (45, 501.070),\n", + " (50, 716.979),\n", + " (55, 975.483),\n", + " (60, 1282.785),\n", + " (65, 1648.838),\n", + " (70, 2081.577),\n", + " (75, 2565.625),\n", + " (80, 3120.272)]\n", + " \n", + " \n", + "regex_mem_data = [(5, 4.212),\n", + " (10, 28.118),\n", + " (15, 84.303),\n", + " (20, 189.262),\n", + " (25, 352.038),\n", + " (30, 603.719),\n", + " (35, 914.989),\n", + " (40, 1349.742),\n", + " (45, 1858.301),\n", + " (50, 2569.797),\n", + " (55, 3370.891),\n", + " (60, 4253.523),\n", + " (65, 5330.279),\n", + " (70, 6681.398),\n", + " (75, 8247.552),\n", + " (80, 10033.315)]\n", + "\n", + "zipper_data = [ (5 , 0.360),\n", + " (10 , 0.732),\n", + " (15 , 1.192),\n", + " (20 , 1.452),\n", + " (25 , 2.000),\n", + " (30 , 2.217),\n", + " (35 , 2.643),\n", + " (40 , 2.932),\n", + " (45 , 3.417),\n", + " (50 , 3.923),\n", + " (55 , 4.203),\n", + " (60 , 4.915),\n", + " (65 , 4.958),\n", + " (70 , 5.331),\n", + " (75 , 5.596),\n", + " (80 , 6.061)]" + ] + }, + { + "cell_type": "code", + "execution_count": 39, + "metadata": {}, + "outputs": [], + "source": [ + "import matplotlib.pyplot as plt\n", + "import numpy as np" + ] + }, + { + "cell_type": "code", + "execution_count": 40, + "metadata": {}, + "outputs": [], + "source": [ + "def plot_data_comparison3(\n", + " plot_title: str, \n", + " data1: list[tuple[int, float]], \n", + " data1_label: str,\n", + " data2: list[tuple[int, float]], \n", + " data2_label: str,\n", + " data3: list[tuple[int, float]],\n", + " data3_label: str,\n", + " x_label: str, \n", + " y_label: str):\n", + " x = [x for x, _ in data1]\n", + " y1 = [y for _, y in data1]\n", + " y2 = [y for _, y in data2]\n", + " y3 = [y for _, y in data3]\n", + "\n", + " plt.plot(x, y1, label=data1_label, color='tab:blue')\n", + " plt.plot(x, y2, label=data2_label, color='tab:orange')\n", + " plt.plot(x, y3, label=data3_label, color='black')\n", + "\n", + " # Set to log scale\n", + " plt.yscale('log')\n", + " plt.xlabel(x_label)\n", + " plt.ylabel(y_label)\n", + " plt.title(plot_title)\n", + " plt.legend()\n", + " plt.show()\n", + "\n", + "def plot_data_comparison2(\n", + " plot_title: str, \n", + " data1: list[tuple[int, float]], \n", + " data1_label: str,\n", + " data2: list[tuple[int, float]], \n", + " data2_label: str,\n", + " x_label: str, \n", + " y_label: str):\n", + " x = [x for x, _ in data1]\n", + " y1 = [y for _, y in data1]\n", + " y2 = [y for _, y in data2]\n", + "\n", + " plt.plot(x, y1, label=data1_label)\n", + " plt.plot(x, y2, label=data2_label)\n", + " # Set to log scale\n", + " plt.yscale('log')\n", + " plt.xlabel(x_label)\n", + " plt.ylabel(y_label)\n", + " plt.title(plot_title)\n", + " plt.legend()\n", + " plt.show()\n", + " \n", + "\n", + "def plot_data(plot_title, data: list[tuple[int, float]], data_label: str, x_label: str, y_label: str):\n", + " x = [x for x, _ in data]\n", + " y = [y for _, y in data]\n", + " plt.plot(x, y, label=data_label)\n", + " plt.xlabel(x_label)\n", + " plt.ylabel(y_label)\n", + " plt.title(plot_title)\n", + " plt.legend()\n", + " plt.show()\n" + ] + }, + { + "cell_type": "code", + "execution_count": 41, + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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k9SqVQs3Nze+//57g3mEli5d+kyXxh7H3Nyczp07s2rVqkd+YDw4PL5Vq1Zcv36dP/74Q78tOTmZ77//3uAxNWvWpEyZMnz11Vf6eWQed8ywsDDee+89OnfuzIcffshXX33FH3/8kWVk06PUq1cPpRSHDh3K8pzA8Puzb9++557DJiQkxOD27NmzAfQj/jIdOnQIjUZDvXr1nuv4D3tU3ampqVmWmHien4Wcei2eJHNY/YMyW/JSUlIA3e8XkOV37HHMzc2z/H7Nnj07yzD7zp07c+zYMX777bcsx8h8fOfOnYmMjGTOnDmP3adbt25kZGTw6aefZtknPT09S903btwwOGdsbCxLliyhWrVq+tbI533Oz+JF3wdy43v1op7nOd25c8fgtpWVFQEBASilSEtLy5N68wtp0SkEHneZ5UFt27Zl+vTptG7dml69enHr1i1CQkIoW7Ysx48fN9i3Zs2a/P3330yfPh1vb29KlSpFnTp1mDx5Mps2bSIoKIg333yTihUrEh4ezq+//srOnTsfe9ksp1lZWTFhwgSGDx9Os2bN6NatG5cuXWLRokWP7DfzPD7//HO2bt1KnTp1eOONNwgICCAqKorDhw/z999/698cBw8ezJw5c+jZsycjR46kWLFiLF26VP9XVGYNZmZmzJ8/nzZt2lCpUiUGDBhA8eLFuX79Olu3bsXR0ZE///wTpRSvv/46tra2zJ07V3+OVatWMXLkSFq0aIG3t/dj627YsCFubm78/fffNGvWTL+9Xbt2rF69mo4dO9K2bVvCwsKYN28eAQEBjwxejxMWFsarr75K69at2bNnDz/99BO9evWiatWqBvtt3ryZBg0a4Obm9szHfpT69evj4uJCcHAwI0aMQKPR8OOPP2b5wH+en4Wcei2eZOLEiezYsYO2bdvi5+fHrVu3+Oabb/Dx8aFhw4aArsXE2dmZefPmUaRIEezt7alTp06WPmMP1v3jjz/i5OREQEAAe/bs4e+//87yGr/33nusXLmSrl278vrrr1OzZk2ioqL4448/mDdvHlWrVqVfv34sWbKE0aNHs3//fho1akRCQgJ///03Q4YM4bXXXiMoKIjBgwczZcoUjh49yssvv4ylpSXnz5/n119/ZdasWQZ9AcuVK8fAgQM5cOAAXl5eLFiwgIiICIPW4mrVqmFubs7UqVOJiYnB2tqa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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "plot_data_comparison3(\n", + " plot_title = \"Matching regex (a|b)* against accepting strings\", \n", + " data1 = regex_data,\n", + " data1_label = \"Regex\",\n", + " data2 = zipper_data,\n", + " data2_label = \"Zipper\",\n", + " data3 = regex_mem_data,\n", + " data3_label = \"Regex with memoization\",\n", + " x_label = \"String length\",\n", + " y_label = \"Time (us)\")" + ] + }, + { + "cell_type": "code", + "execution_count": 42, + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAjoAAAHHCAYAAAC2rPKaAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjkuMiwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8hTgPZAAAACXBIWXMAAA9hAAAPYQGoP6dpAABzJ0lEQVR4nO3dd1hT1/8H8HfC3iB7gzhREUWw7oW11rpn1YqzVrHa2lq1/Vat/Vm7tEvUtrZaq12OWqt11611oLhwoYiDvfdKzu+PSDSCCghcSN6v5+GB3Htz87kJJG/OPedcmRBCgIiIiEgLyaUugIiIiKi6MOgQERGR1mLQISIiIq3FoENERERai0GHiIiItBaDDhEREWktBh0iIiLSWgw6REREpLUYdIiIiEhrMehQjVqzZg1kMhlOnz791G27du2Krl27Vn9ROmTq1Kno2bNnpe67YMECeHl5lVomk8mQnJz8xPsWFRXB3d0dy5cvr9Rj1zYymQwLFiyQugydoI3vA9p4TLUZg46WKQkSMpkMR44cKbVeCAF3d3fIZDK89NJLlXqMjz76CFu2bHnGSqmmRUdHY9WqVXj33Xdr/LENDAwwc+ZMLFq0CPn5+WVuc+DAAYwdO7ZmC6uFYmNjsWDBAkREREhdSo2JjIzEggULcOvWLalLqRBdfK3qIgYdLWVsbIxffvml1PKDBw/i7t27MDIyqvS+ayro7N69G7t37672x9EVX331Fby9vdGtWzdJHn/cuHFITk7W+L3MyMjAf//9V2rb9PR0nDhxoibLq5C8vDz873//q5Z9x8bG4oMPPtCpD8/IyEh88MEHZQad2vw+UNnXqjYfkzZi0NFSL774IjZs2IDi4mKN5b/88gsCAgLg5OQkUWXlZ2hoCENDQ6nLAAAolcrHtkTUBUVFRVi/fj2GDRsmWQ3W1tZ4/vnnsWbNGvWymJgYhISEYMaMGcjOzgYAbNq0Ca1bt8bx48clqvTpjI2Noa+vL3UZOqE2vQ88q9zcXADadUx1AYOOlnr55ZeRkpKCPXv2qJcVFhZi48aNGDlyZJn3+fzzz9G+fXvY2trCxMQEAQEB2Lhxo8Y2MpkMOTk5+Omnn9SnyB4+3XDv3j1MmDABLi4uMDIygre3N6ZMmYLCwkKN/RQUFGDmzJmwt7eHmZkZBg4ciKSkJI1tHj2PfeDAAchkMvzxxx9YtGgR3NzcYGxsjB49eiAqKqrU8YSFhaF+/fowMTFBUFAQDh8+XO5z4zKZDNOmTcP69evRrFkzGBkZYefOnepjHD9+PBwdHWFkZIRmzZrhxx9/LLWPmJgY9OvXD2ZmZnBwcMCbb76JXbt2QSaT4cCBAxrbnjhxAi+88AKsrKxgamqKLl264OjRo+r1ly9fhomJCcaMGaNxvyNHjkBPTw+zZ89+4vEcOXIEycnJCA4O1lheWFiIefPmISAgAFZWVjAzM0OnTp2wf//+pz5HD0tOTsawYcNgaWkJW1tbzJgxo8xg2LNnTxw5cgSpqakAAD8/P1y4cAHu7u6YPHkyNm7ciD/++AP79+/HG2+88cTH/Ouvv9CnTx/175qPjw8+/PBDKBSKUtuW53ehIs/Fo310SvoqRUVFYezYsbC2toaVlRXGjRun/nArsWfPHnTs2BHW1tYwNzdH48aN1acTDxw4gMDAQACqFrCSv7GHw+GjYmJiMHXqVDRu3BgmJiawtbXF0KFDy2wdSU9Px5tvvgkvLy8YGRnBzc0NY8aM0ehjlZ+fjwULFqBRo0YwNjaGs7MzBg0ahBs3bqi3USqV+PLLL9GsWTMYGxvD0dERkydPRlpamsbjeXl54aWXXsLu3bvh7+8PY2Nj+Pr6YvPmzept1qxZg6FDhwIAunXrpj7mkr8RKd8HnuW16tq1K5o3b47w8HB07twZpqam6vvW1DF98803aNasGUxNTWFjY4M2bdqU2dKv7fgviZby8vJCu3bt8Ouvv6J3794AgB07diAjIwMjRozA119/Xeo+X331Ffr164dRo0ahsLAQv/32G4YOHYpt27ahT58+AICff/4ZEydORFBQEF599VUAgI+PDwBVM25QUBDS09Px6quvokmTJrh37x42btyI3Nxcjf9gXn/9ddjY2GD+/Pm4desWvvzyS0ybNg2///77U4/t448/hlwux9tvv42MjAx8+umnGDVqlMapjhUrVmDatGno1KkT3nzzTdy6dQsDBgyAjY0N3NzcyvUc/vvvv/jjjz8wbdo02NnZwcvLCwkJCXjuuefUQcje3h47duzAhAkTkJmZqf5wzsnJQffu3REXF4cZM2bAyckJv/zyS5kfmv/++y969+6NgIAAzJ8/H3K5HKtXr0b37t1x+PBhBAUFoWnTpvjwww8xa9YsDBkyBP369UNOTg7Gjh2LJk2aYOHChU88lmPHjkEmk6FVq1YayzMzM7Fq1Sq8/PLLmDRpErKysvDDDz+gV69eOHnyJPz9/cv1XA0bNgxeXl5YvHgx/vvvP3z99ddIS0vD2rVrNbYLCAiAEALHjh1T9xGTyWSQy+WQyWTq2yU/P8maNWtgbm6OmTNnwtzcHP/++y/mzZuHzMxMfPbZZ+rtyvu7UBXPxbBhw+Dt7Y3FixfjzJkzWLVqFRwcHPDJJ58AAC5duoSXXnoJfn5+WLhwIYyMjBAVFaUOtU2bNsXChQsxb948vPrqq+jUqRMAoH379o99zFOnTuHYsWMYMWIE3NzccOvWLaxYsQJdu3ZFZGQkTE1NAQDZ2dno1KkTLl++jPHjx6N169ZITk7G1q1bcffuXdjZ2UGhUOCll17Cvn37MGLECMyYMQNZWVnYs2cPLl68qP5bnzx5MtasWYNx48Zh+vTpiI6OxrJly3D27FkcPXoUBgYG6vquX7+O4cOH47XXXkNISAhWr16NoUOHYufOnejZsyc6d+6M6dOn4+uvv8a7776Lpk2bqp+LJ6nu94GqeK1SUlLQu3dvjBgxAqNHj4ajo2ONHdP333+P6dOnY8iQIep/PM6fP48TJ0489p9drSVIq6xevVoAEKdOnRLLli0TFhYWIjc3VwghxNChQ0W3bt2EEEJ4enqKPn36aNy3ZLsShYWFonnz5qJ79+4ay83MzERISEipxx4zZoyQy+Xi1KlTpdYplUqN+oKDg9XLhBDizTffFHp6eiI9PV29rEuXLqJLly7q2/v37xcARNOmTUVBQYF6+VdffSUAiAsXLgghhCgoKBC2trYiMDBQFBUVqbdbs2aNAKCxz8cBIORyubh06ZLG8gkTJghnZ2eRnJyssXzEiBHCyspK/RwuWbJEABBbtmxRb5OXlyeaNGkiAIj9+/ern5eGDRuKXr16aTwfubm5wtvbW/Ts2VO9TKFQiI4dOwpHR0eRnJwsQkNDhb6+fpnP96NGjx4tbG1tSy0vLi7WeC6FECItLU04OjqK8ePHayyfP3++8PT0LLUMgOjXr5/G8qlTpwoA4ty5cxrLY2NjBQDxySefCCGEOH/+vGjSpIl4/fXXxd9//y1CQkLEhg0bhLe3t/jyyy+feEyP/r4KIcTkyZOFqampyM/PF0JU7HehIs8FADF//vxSz8Oj2w0cOFDjef/iiy8EAJGUlPTY4zp16pQAIFavXv3YbR5W1vNw/PhxAUCsXbtWvWzevHkCgNi8eXOp7Ut+93788UcBQCxduvSx2xw+fFgAEOvXr9dYv3PnzlLLPT09BQCxadMm9bKMjAzh7OwsWrVqpV62YcMGjb+Lh0n1PvCsr1WXLl0EALFy5UpJjql///6iWbNmTzxGXcFTV1ps2LBhyMvLw7Zt25CVlYVt27Y9McmbmJiof05LS0NGRgY6deqEM2fOPPWxlEoltmzZgr59+6JNmzal1j/6H/qrr76qsaxTp05QKBSIiYl56mONGzdOo3Wo5D+pmzdvAgBOnz6NlJQUTJo0SaMfxahRo2BjY/PU/Zfo0qULfH191beFENi0aRP69u0LIQSSk5PVX7169UJGRob6udq5cydcXV3Rr18/9f2NjY0xadIkjceIiIjA9evXMXLkSKSkpKj3l5OTgx49euDQoUNQKpUAALlcjjVr1iA7Oxu9e/fG8uXLMXfu3DKf70elpKSUeex6enrq51KpVCI1NRXFxcVo06ZNuV73EqGhoRq3X3/9dQDAP//8o7G8pIaSUyUeHh5YvXo1vv76a5ibmwMAhgwZgjNnzuC555574mM+/PualZWF5ORkdOrUCbm5ubhy5QqAiv0uVMVz8dprr2nc7tSpE1JSUpCZmQlA1U8JUJ12K3ldn9XDz0NRURFSUlLQoEEDWFtba9S9adMmtGzZEgMHDiy1j5K/xU2bNsHOzk79+pW1zYYNG2BlZYWePXtq/A0EBATA3Ny8VKuli4uLxmNaWlpizJgxOHv2LOLj4yt93NX9PlAVr5WRkRHGjRtX7u2r8pisra1x9+5dnDp1qlK1axMGHS1mb2+P4OBg/PLLL9i8eTMUCgWGDBny2O23bduG5557DsbGxqhXrx7s7e2xYsUKZGRkPPWxkpKSkJmZiebNm5erNg8PD43bJX+kj57jr8x9S8JSgwYNNLbT19cvNQ/Mk3h7e2vcTkpKQnp6Or777jvY29trfJW8mSUmJqpr8PHxKRXwHq3p+vXrAICQkJBS+1y1ahUKCgo0nn8fHx8sWLAAp06dQrNmzfD++++X+3iEEGUu/+mnn+Dn5wdjY2PY2trC3t4e27dvL9frXqJhw4Yat318fCCXy0v1EympoeR5sbKyKjPQWFtbo23btk98zEuXLmHgwIGwsrKCpaUl7O3tMXr0aABQ117R34VnfS6e9rs5fPhwdOjQARMnToSjoyNGjBiBP/7445lCT15eHubNmwd3d3cYGRnBzs4O9vb2SE9P16j7xo0bT/37vHHjBho3bvzEjtbXr19HRkYGHBwcSv3OZmdnq/8GSjRo0KDU30GjRo0A4JmGk1f3+0BVvFaurq4V6nRclcc0e/ZsmJubIygoCA0bNkRoaKhGvz9dwj46Wm7kyJGYNGkS4uPj0bt3b/V/KY86fPgw+vXrh86dO2P58uVwdnaGgYEBVq9eXS2d1/T09Mpc/rgP46q6b0U8/J8yAPUb3OjRoxESElLmffz8/Cr0GCX7/Oyzzx7bB6SkpaNEybDU2NhYpKSklGsEna2tbZkhct26dRg7diwGDBiAWbNmwcHBAXp6eli8eLFG59OKelwfm5Ia7OzsSq2ryCRq6enp6NKlCywtLbFw4UL4+PjA2NgYZ86cwezZsysVHKriuXja76aJiQkOHTqE/fv3Y/v27di5cyd+//13dO/eHbt3737s/Z/k9ddfx+rVq/HGG2+gXbt2sLKygkwmw4gRI6qs1ehhSqUSDg4OWL9+fZnr7e3tq/wxy1Ld7wNV8Vo9+h7yNFV5TE2bNsXVq1exbds27Ny5E5s2bcLy5csxb948fPDBBxXeX13GoKPlBg4ciMmTJ+O///57YkffTZs2wdjYGLt27dKYY2f16tWlti3rQ8ze3h6Wlpa4ePFi1RT+DDw9PQEAUVFRGnPGFBcX49atWxUOIyXs7e1hYWEBhUJRavRSWTVERkZCCKHxfD06gqKkc6elpeVT9wkAK1euxJ49e7Bo0SIsXrwYkydPxl9//fXU+zVp0gTr169HRkYGrKys1Ms3btyI+vXrY/PmzRp1zp8//6n7fNj169c1WsCioqKgVCpL/ZcZHR0N4OkdTZ/mwIEDSElJwebNm9G5c+dS+y9Rkd+FqnounkYul6NHjx7o0aMHli5dio8++gjvvfce9u/fj+Dg4HJ1xH7Yxo0bERISgiVLlqiX5efnIz09XWM7Hx+fp/59+vj44MSJEygqKtLoUPzoNnv37kWHDh3K9UEeFRVV6u/g2rVrAKD+/ajoMZdHVbwPVPVr9awqekxmZmYYPnw4hg8fjsLCQgwaNAiLFi3C3LlzYWxsXKO1S4mnrrScubk5VqxYgQULFqBv376P3U5PTw8ymUxjaO6tW7fKnBjQzMys1JuoXC7HgAED8Pfff5d5eYeqbm15kjZt2sDW1hbff/+9xjxC69evL9epscfR09PD4MGDsWnTpjI/MB4eHt+rVy/cu3cPW7duVS/Lz8/H999/r3GfgIAA+Pj44PPPP1fPI/O4fUZHR2PWrFkYPHgw3n33XXz++efYunVrqZFNZWnXrh2EEAgPDy91TIDm63PixIkKz2ETFhamcfubb74BAPWIvxLh4eGQyWRo165dhfb/qLLqLiwsLHWJiYr8LlTVc/EkJcPqH1bSkldQUABA9fcFoNTf2OPo6emV+vv65ptvSg2zHzx4MM6dO4c///yz1D5K7j948GAkJydj2bJlj91m2LBhUCgU+PDDD0ttU1xcXKru2NhYjcfMzMzE2rVr4e/vr26NrOgxl8ezvg9Ux2v1rCpyTCkpKRq3DQ0N4evrCyEEioqKaqTe2oItOjrgcadZHtanTx8sXboUL7zwAkaOHInExESEhYWhQYMGOH/+vMa2AQEB2Lt3L5YuXQoXFxd4e3ujbdu2+Oijj7B792506dIFr776Kpo2bYq4uDhs2LABR44ceexps6pmaGiIBQsW4PXXX0f37t0xbNgw3Lp1C2vWrCmz30xFfPzxx9i/fz/atm2LSZMmwdfXF6mpqThz5gz27t2rfnOcPHkyli1bhpdffhkzZsyAs7Mz1q9fr/4vqqQGuVyOVatWoXfv3mjWrBnGjRsHV1dX3Lt3D/v374elpSX+/vtvCCEwfvx4mJiYYMWKFerH2LRpE2bMmIHg4GC4uLg8tu6OHTvC1tYWe/fuRffu3dXLX3rpJWzevBkDBw5Enz59EB0djZUrV8LX17fM4PU40dHR6NevH1544QUcP34c69atw8iRI9GyZUuN7fbs2YMOHTrA1ta23PsuS/v27WFjY4OQkBBMnz4dMpkMP//8c6kP/Ir8LlTVc/EkCxcuxKFDh9CnTx94enoiMTERy5cvh5ubGzp27AhA1WJibW2NlStXwsLCAmZmZmjbtm2pPmMP1/3zzz/DysoKvr6+OH78OPbu3VvqOZ41axY2btyIoUOHYvz48QgICEBqaiq2bt2KlStXomXLlhgzZgzWrl2LmTNn4uTJk+jUqRNycnKwd+9eTJ06Ff3790eXLl0wefJkLF68GBEREXj++edhYGCA69evY8OGDfjqq680+gI2atQIEyZMwKlTp+Do6Igff/wRCQkJGq3F/v7+0NPTwyeffIKMjAwYGRmhe/fucHBwqPRz/azvA9XxWj2rihzT888/DycnJ3To0AGOjo64fPkyli1bhj59+sDCwqJa6qu1anKIF1W/h4eXP0lZw8t/+OEH0bBhQ2FkZCSaNGkiVq9erR42+7ArV66Izp07CxMTEwFAY6h5TEyMGDNmjLC3txdGRkaifv36IjQ0VD1k8nH1lQyvfHh46eOGYG7YsEHjvtHR0WUO8fz666+Fp6enMDIyEkFBQeLo0aMiICBAvPDCC098boRQDR8ODQ0tc11CQoIIDQ0V7u7uwsDAQDg5OYkePXqI7777TmO7mzdvij59+ggTExNhb28v3nrrLbFp0yYBQPz3338a2549e1YMGjRI2NraCiMjI+Hp6SmGDRsm9u3bJ4R4MMz04WG6Qghx+/ZtYWlpKV588cWnHtP06dNFgwYNNJYplUrx0UcfqZ+nVq1aiW3btomQkJAyh5I/bnh5ZGSkGDJkiLCwsBA2NjZi2rRpIi8vT2Pb9PR0YWhoKFatWvXUWsvj6NGj4rnnnhMmJibCxcVFvPPOO2LXrl1lDlMuz+9CRZ4LPGZ4+aNDkUt+36Ojo4UQQuzbt0/0799fuLi4CENDQ+Hi4iJefvllce3aNY37/fXXX8LX11fo6+s/dah5WlqaGDdunLCzsxPm5uaiV69e4sqVK8LT07PUNBApKSli2rRpwtXVVRgaGgo3NzcREhKiMV1Cbm6ueO+994S3t7f693vIkCHixo0bGvv67rvvREBAgDAxMREWFhaiRYsW4p133hGxsbHqbUreZ3bt2iX8/PzU7y2P/g0LIcT3338v6tevL/T09DReQ6neB571terSpctjh3fXxDF9++23onPnzur3FB8fHzFr1iyRkZHxxOPWRgw6pDMUCoWoV6+emDhxomQ1lMzNcffu3Rp/7Bs3bggDAwOxd+/eSt2/rKBTEV988YVwdnYuc96XmlYbfhd0QVn/UElNG197bTymqsQ+OqSV8vPzS53GWLt2LVJTU8s9sudZ5eXllarp22+/RcOGDeHq6lojNTysfv36mDBhAj7++OMaf+yioiIsXboU//vf/yo8EuVZ1YbfBZKGNr722nhM1Y19dEgr/ffff3jzzTcxdOhQ2Nra4syZM/jhhx/QvHlz9XV1qtugQYPg4eEBf39/ZGRkYN26dbhy5cpjh+XWhJL+PTXNwMAAt2/fluSxa8PvAklDG197bTym6sagQ1rJy8sL7u7u+Prrr5Gamop69ephzJgx+Pjjj2vsqsG9evXCqlWrsH79eigUCvj6+uK3337D8OHDa+TxSaU2/C6QNLTxtdfGY6puMvFoGxgRERGRlmAfHSIiItJaDDpERESktXS+j45SqURsbCwsLCxqfDpvIiIiqhwhBLKysuDi4gK5/PHtNjofdGJjY+Hu7i51GURERFQJd+7cgZub22PX63zQKZkK+86dO7C0tJS4GiIiIiqPzMxMuLu7P/WSFjofdEpOV1laWjLoEBER1TFP63bCzshERESktRh0iIiISGsx6BAREZHW0vk+OuWhVCpRWFgodRk6w8DAAHp6elKXQUREWoBB5ykKCwsRHR0NpVIpdSk6xdraGk5OTpzbiIiInonOBp2wsDCEhYVBoVA8dhshBOLi4qCnpwd3d/cnTkhEVUMIgdzcXCQmJgIAnJ2dJa6IiIjqMp2/qGdmZiasrKyQkZFRanh5UVERoqKi4OLiAisrK4kq1E0pKSlITExEo0aNeBqLiIhKedLn98PYRPEEJa09hoaGEleie0xNTQGowiYREVFlMeiUA/uJ1Dw+50REVBUYdIiIiEhrMegQERGR1mLQ0UJjx46FTCaDTCaDgYEBvL298c477yA/P1/q0oiIiGqUzg4v13YvvPACVq9ejaKiIoSHhyMkJAQymQyffPKJ1KUREZGOyCkoRlRiNlq6W0tWA1t0tJSRkRGcnJzg7u6OAQMGIDg4GHv27AGgmul58eLF8Pb2homJCVq2bImNGzdq3H/r1q1o2LAhjI2N0a1bN/z000+QyWRIT09Xb3PkyBF06tQJJiYmcHd3x/Tp05GTkwMAWLt2LczNzXH9+nX19lOnTkWTJk2Qm5tb/U8AERHVuPTcQuyNTMBH/1xG/7Cj8PtgN4Z+exz5RY+fs666sUWnAoQQyJPoxTIx0Kv0SKSLFy/i2LFj8PT0BAAsXrwY69atw8qVK9GwYUMcOnQIo0ePhr29Pbp06YLo6GgMGTIEM2bMwMSJE3H27Fm8/fbbGvu8ceMGXnjhBfzf//0ffvzxRyQlJWHatGmYNm0aVq9ejTFjxmDbtm0YNWoUjh07hl27dmHVqlU4fvy4eug4ERHVbYmZ+Th5KxUno1VfV+KzSm3jaGGEe+l58LE3l6BCHZ4w8OGZka9du1bmhEP5+fmIjo6Gt7c3jI2NkVtYDN95uySpN3JhL5gali+Xjh07FuvWrYOxsTGKi4tRUFAAuVyOP/74Ay+99BLq1auHvXv3ol27dur7TJw4Ebm5ufjll18wZ84cbN++HRcuXFCv/9///odFixYhLS0N1tbWmDhxIvT09PDtt9+qtzly5Ai6dOmCnJwcGBsbIy0tDX5+fujbty82b96M6dOn49133y3XMTz63BMRkbSEELiblocT0ak4GZ2Ck9GpuJVSuoW+vr0Z2nrXQ5B3PQR61YObTfX8c1veCQN1tkUnNDQUoaGh6idK23Tr1g0rVqxATk4OvvjiC+jr62Pw4MG4dOkScnNz0bNnT43tCwsL0apVKwDA1atXERgYqLE+KChI4/a5c+dw/vx5rF+/Xr1MCAGlUono6Gg0bdoUNjY2+OGHH9CrVy+0b98ec+bMqaajJSKiqiaEQFRi9v1go/qKz9Qc1CKTAU2dLBHkXQ9tveuhjVc92FsYSVRx2XQ26FSGiYEeIhf2kuyxK8LMzAwNGjQAAPz4449o2bIlfvjhBzRv3hwAsH37dri6umrcx8io/L+c2dnZmDx5MqZPn15qnYeHh/rnQ4cOQU9PD3FxccjJyYGFhUWFjoOIiGpGsUKJy3FZOHG/tebUrVSk5WrOTq8vl8HPzQpB3rZo610PrT1tYGViIFHF5cOgUwEymazcp49qE7lcjnfffRczZ87EtWvXYGRkhNu3b6NLly5lbt+4cWP8888/GstOnTqlcbt169aIjIxUh6myHDt2DJ988gn+/vtvzJ49G9OmTcNPP/307AdERETPrKBYgfN3M3AyOhUnolMRfisVOYWa/VCNDeRo7WGDoPunolq528DEsG5df7DufWpTpQwdOhSzZs3Ct99+i7fffhtvvvkmlEolOnbsiIyMDBw9ehSWlpYICQnB5MmTsXTpUsyePRsTJkxAREQE1qxZA+DBpRlmz56N5557DtOmTcPEiRNhZmaGyMhI7NmzB8uWLUNWVhZeeeUVTJ8+Hb1794abmxsCAwPRt29fDBkyRMJngohIN+UUFOPM7TR1sIm4k47CYqXGNhbG+gj0qqcONs1drGCoX7cHaDPo6Ah9fX1MmzYNn376KaKjo2Fvb4/Fixfj5s2bsLa2RuvWrdUdhb29vbFx40a89dZb+Oqrr9CuXTu89957mDJlivr0lp+fHw4ePIj33nsPnTp1ghACPj4+GD58OABgxowZMDMzw0cffQQAaNGiBT766CNMnjwZ7dq1K3XajIiIqlZeoQKnY1Jx/EYKjt1IwYV7GVAoNccf2ZkbIcjbBkFe9RDkbYvGThbQk2vXtQZ1dtRViSf12ubInwcWLVqElStX4s6dOzXyeHzuiYgqprBYiYg76Th2IxnHbqQg4nY6ChWaLTZuNib3Q43qy9vOrM5eRJmjruiZLF++HIGBgbC1tcXRo0fx2WefYdq0aVKXRURE9ymUAhfvZeDYjRQcu5GM07fSSs315mxljPY+dmjvY4vnfGzham0iUbXSYdChMl2/fh3/93//h9TUVHh4eOCtt97C3LlzpS6LiEhnKZUC1xKzcCxKdSrqRHQKsvKLNbaxNTNEOx9bdbjxtDWtsy02VYVBh8r0xRdf4IsvvpC6DCIinSWEwK2UXPWpqP9upCAlp1BjGwtjfTxX3xbt74ebRo7mOh9sHsWgQ0REVEvEpuepT0Udv5GCuAzNCfpMDPQQ6F3vfrCxRTMXK63rPFzVGHSIiIgkkpxdoB4VdfxGcqlLKhjqydHKw1p1KqqBLVq6Wdf54d41jUGHiIiohmTkFeHEzZJgk4KrCZoXwdS7P/Nwex9btKtvhwDPujdBX23DoENERFRNhBCIjMvEgatJOHA1EWdup5eay8bX2VJ1KqqBLQK96sHCuHZfUqGuYdAhIiKqQpn5RTh6PVkVbq4lIiGzQGN9fXszdLg/KqptfVvUMzOUqFLdoLNBJywsDGFhYVAoFE/fmIiI6DGEELiakIUDV5Ow/0oiwmPSUPxQq42JgR46NLBF18YO6NrYHm42phJWq3t0NuiEhoYiNDRUPbOirjlw4AC6deuGtLQ0WFtbS10OEVGdkl1QjKNRyThwNREHriaVGh1V394MXRs5oFsTewR61YOxAfvZSEVng442Kwkxj9O1a1fs2rULcXFxOhnyiIgqSgiBqMRs7L8fbE7dSkWR4kGrjbGBHO3q26JbEwd0beQAD1u22tQWDDpaqH379oiLiyu1fOvWrXjttdcwdepUGBoawsnJSYLqSisqKoKBATvfEVHtklNQjGM3UtStNvfS8zTWe9qaotv901HP1bdlq00txcH4WqgkxDz8lZaWhrfffhvvvvsuhg4digMHDkAmkyE9PR0AsGbNGlhbW2PLli1o2LAhjI2N0atXL42LeC5YsAD+/v749ttv4e7uDlNTUwwbNgwZGRkaj79q1So0bdoUxsbGaNKkCZYvX65ed+vWLchkMvz+++/o0qULjI2NsX79+hp5XoiInqSk1WbV4ZsYveoEWi3cg0lrT2P9idu4l54HQ305Ojeyx/y+vtj/dlccnNUNC/o1Q9fGDgw5tRhbdCpCCKAo9+nbVQcDU6CS03qnp6ejf//+6Nq1Kz788MPHbpebm4tFixZh7dq1MDQ0xNSpUzFixAgcPXpUvU1UVBT++OMP/P3338jMzMSECRMwdepUdVhZv3495s2bh2XLlqFVq1Y4e/YsJk2aBDMzM4SEhKj3M2fOHCxZsgStWrXi1cmJSDJ5hQocv5mM/VdUI6TupGq22rjZmKBbY1Vfm3b17TinTR3EoFMRRbnARy7SPPa7sYChWYXvplQqMXLkSOjr62P9+vVPvAZKUVERli1bhrZt2wIAfvrpJzRt2hQnT55EUFAQACA/Px9r166Fq6srAOCbb75Bnz59sGTJEjg5OWH+/PlYsmQJBg0aBADw9vZGZGQkvv32W42g88Ybb6i3ISKqSXfTcrE3MgH/Xk3CfzdTUFisVK8z0JOhrbctuja2R9fGDvCxN+O1o+o4Bh0t9+677+L48eM4efIkLCwsnritvr4+AgMD1bebNGkCa2trXL58WR10PDw81CEHANq1awelUomrV6/CwsICN27cwIQJEzBp0iT1NsXFxaU6Pbdp06YqDo+I6KmEELgUm4ndkQnYE5mAy3GZGutdrU3QpbE9ujV2QHsfW5gZ8aNRm/DVrAgDU1XLilSPXUG//fYbPv/8c2zfvh0NGzashqI0ZWdnAwC+//57datQCT09zeZeM7OKt04REZVXkUKJk9Gp2H0pHnsvJ2p0JJbLgDae9dCjqQO6NXFAQwde8VubMehUhExWqdNHUoiIiMCECRPw8ccfo1evXuW6T3FxMU6fPq1uvbl69SrS09PRtGlT9Ta3b99GbGwsXFxUp/D+++8/yOVyNG7cGI6OjnBxccHNmzcxatSoqj8oIqInyMovwsFrSdgTmYD9VxKRmV+sXmdsIEfnhvbo6euI7k0cYGtuJGGlVJMYdLRQcnIyBgwYgK5du2L06NGIj4/XWP9o60oJAwMDvP766/j666+hr6+PadOm4bnnnlMHHwAwNjZGSEgIPv/8c2RmZmL69OkYNmyYeqj6Bx98gOnTp8PKygovvPACCgoKcPr0aaSlpWHmzJnVd9BEpJMSMvOx5/4pqeM3UlCoeNDfxtbMED2aOqCnrxM6NmBHYl3FoKOFtm/fjpiYGMTExMDZ2bnUek9PT6xZs6bUclNTU8yePRsjR47EvXv30KlTJ/zwww8a2zRo0ACDBg3Ciy++iNTUVLz00ksaw8cnTpwIU1NTfPbZZ5g1axbMzMzQokULvPHGG1V9mESkg4QQuJ6YjT2RCdgdmYBzd9I11nvbmaGnryOe93VEKw8b6Ml5SkrXyYQQ4umbaa+SS0BkZGTA0tJSY11+fj6io6Ph7e2t9UOg16xZgzfeeEM9r05ZFixYgC1btiAiIqLa69Gl556InkyhFAiPScOeyHjsjkxATIrmNB+tPKzV4cbHnv1tdMWTPr8fxhYdIiKqdfIKFTh0XdXf5t8riUjNKVSvM9SXo4OPLXr6OiG4qQMcLPnPED0egw4REdUKKdkF2Hc5EbsjE3AkKgn5RQ/621iZGKB7Ewc87+uIzo3sOQScyo2nrnjqqlbic0+kG24l52B3ZDz2RCbgdEwaHv5EcrMxQU9fR/T0dUSgVz0Y6PGqRfQAT10REVGtdDslF9suxGL7+ThcitWcvK+5qyV6NnVCT19HNHW2YH8bemYMOuWg441ekuBzTqRd7qblYvv5OGy/EIfzdx9cCFhPLkO7+rZ4vpkjgps6wsXaRMIqSRsx6DxByXwzhYWFMDHhH19Nys1VjaowMDCQuBIiqqzY9Dz8cyEO287HIeKhYeByGdDexw4v+TmjVzMn2JgZSlckaT0GnSfQ19eHqakpkpKSYGBgALmc54ermxACubm5SExMhLW19WMnNySi2ikhM18dbsJj0tTL5TKgrbct+vg544XmTrDjzMRUQ+p80ElPT0dwcDCKi4tRXFyMGTNmaFxQ8lnIZDI4OzsjOjoaMTExVbJPKh9ra2v1bMtEVLslZuVj58V4bDsXh1MxqeoOxTIZEOhVDy/dDzcOFhxYQDWvzo+6UigUKCgogKmpKXJyctC8eXOcPn0atra25bp/eXptK5VKFBYWlrmOqp6BgQFbcohqueTsAlW4OR+LE9GpGqOlAjxt8JKfM3o3d4aTFcMNVQ+dGXWlp6cHU1PVlb0LCgoghKjyjqxyuZxDnIlI56XlFGLnpXhsPx+HYzeSoXzordbf3Rov+TnjxRbO7FBMtYrkQefQoUP47LPPEB4ejri4OPz5558YMGCAxjZhYWH47LPPEB8fj5YtW+Kbb77RuNBkeno6unTpguvXr+Ozzz6DnZ1dDR8FEZF2ysgtwq7IeGw7H4ejUclQPJRu/Nys0KeFKty41zOVsEqix5M86OTk5KBly5YYP348Bg0aVGr977//jpkzZ2LlypVo27YtvvzyS/Tq1QtXr16Fg4MDAFV/jnPnziEhIQGDBg3CkCFD4OjoWNOHQkSkFTLzi7DnUgK2nY/FkahkFCkehJtmLpbo4+eMPi2c4WlrJmGVROVTq/royGSyUi06bdu2RWBgIJYtWwZA1V/G3d0dr7/+OubMmVNqH1OnTkX37t0xZMiQMh+joKAABQUF6tuZmZlwd3d/6jk+IiJtlpVfhH2XE7HtfBwOXUtCoeLB5ReaOFmoT0vVtzeXsEqiB7Sij05hYSHCw8Mxd+5c9TK5XI7g4GAcP34cAJCQkABTU1NYWFggIyMDhw4dwpQpUx67z8WLF+ODDz6o9tqJiGo7pVLg2I0UbAi/g50X41FQ/CDcNHQwx0t+Lujj54QGDhYSVkn0bGp10ElOToZCoSh1GsrR0RFXrlwBAMTExODVV19Vd0J+/fXX0aJFi8fuc+7cuZg5c6b6dkmLDhGRrridkouN4Xew6cw93EvPUy+vb2eGl/yc8VJLFzRyZLgh7VCrg055BAUFISIiotzbGxkZwciIE1URkW7JLSzGjgvx2BB+B//dTFUvtzTWRz9/FwwNcIefmxWvLUVap1YHHTs7O+jp6SEhIUFjeUJCAieTIyJ6CiEEwmPSsOH0XWy/EIfsgmIAqon8Ojaww9A27nje1xHGBpy3irRXrQ46hoaGCAgIwL59+9QdlJVKJfbt24dp06Y9077DwsIQFhYGhUJRBZUSEdUeCZn52HTmLjaevoubyTnq5Z62phjS2g2DAtzgyrluSEdIHnSys7MRFRWlvh0dHY2IiAjUq1cPHh4emDlzJkJCQtCmTRsEBQXhyy+/RE5ODsaNG/dMjxsaGorQ0FB1r20iorqsoFiBvZGJ2BB+B4euJakn8zMx0EMfP2cMDXBDkHc9npoinSN50Dl9+jS6deumvl3SUTgkJARr1qzB8OHDkZSUhHnz5iE+Ph7+/v7YuXMn58khIgJw8V4GNpy+g7/OxSI9t0i9PNDLBkMD3PGinzPMjSR/qyeSTK2aR0cK5R2HT0RUW6TmFGLL2XvYEH4Xl+My1cudLI0xOMAVQwLc4W3HyfxIu2nFPDrViX10iKguKVYocfBaEjacvot9VxLUsxUb6snRs5kjhga4oVNDe+jJeWqK6GFs0WGLDhHVYlGJ2dgQfgebz9xDUtaDWd1buFphaBs39GvpAmtTQwkrJJIGW3SIiOqozPwibDsXhw3hd3D2drp6eT0zQwzwd8XQNm5o6sx/zIjKg0GHiKiWiLiTjp+O3cKOi3HIL1JdjkFPLkO3xvYYEuCO7k0cYKgvl7hKorqFQYeISELFCiV2RybghyPRCI9JUy9v4GCOYW3cMKCVKxwsjCWskKhu09mgw87IRCSlzPwi/HHqDlYfvaW+3pSBngx9W7rglec84e9uzTlviKoAOyOzMzIR1aDbKblYfSwaG07fVV+SwcbUAKOf88Qrz3nCwZKtN0Tlwc7IRES1hBACp26l4YcjN7EnMkE9a3EDB3NM6OiNga1ceb0pomrCoENEVE2KFEpsPx+HH49G4/zdDPXyzo3sMb6DF7o0sufpKaJqxqBDRFTF0nML8cvJ21h7LAbxmfkAAEN9OQa1csX4jt5o5GghcYVEuoNBh4ioitxIysbqo9HYFH4PeUWqgQ525kYY084To9p6wNbcSOIKiXSPzgYdjroioqoghMCxGyn44Ug0/r2SqF7e1NkSEzp6o29LZxjps/8NkVQ46oqjroioEgqKFfgrIhY/HonGlfgsAIBMBvRo4oDxHb3Rrr4t+98QVSOOuiIiqgbJ2QVY918M1v0Xg+TsQgCAiYEehrZxw7gO3rxqOFEtw6BDRFQOV+Oz8MORm9gSEYvCYtXlGZwsjTG2gxdeDvSAlamBxBUSUVkYdIiIHkOpFDh4PQk/HonG4evJ6uUt3awwvqM3XmzhDAM9XnuKqDZj0CEiekR+kQKbztzFj0eicSMpBwAglwG9mjlhQkdvBHjasP8NUR3BoENEdF9GXhHW/ReD1Uej1f1vzI30MTzQHWPbe8G9nqnEFRJRRels0OHwciIqkZiZjx+P3sL6/2KQdf/6U67WJhjXwQvDA91hYcz+N0R1FYeXc3g5kc66lZyD7w7fxMbwu+oOxo0czfFaFx/0benC/jdEtRiHlxMRPcbFexlYefAG/rkQp77AZmsPa0zt2gDdmzhALmf/GyJtwaBDRDpBCIET0alYceAGDl5LUi/v2tgeU7r4IMi7HjsYE2khBh0i0mpKpcDeywlYcfAGzt5OB6AaQfWSnwte6+IDXxeesibSZgw6RKSVihRK/BURi5UHbyAqMRuA6griw9q44dVOPvCw5QgqIl3AoENEWiW3sBi/n7qD7w/dRGxGPgDAwkgfo9t5YlwHLzhYGEtcIRHVJAYdItIK6bmF+OlYDNYci0ZabhEAwM7cCBM6emPUcx6w5BBxIp2ks0GH8+gQaYe4jDysOhyNX0/eRm6h6u/Zo54pJnepj8Gt3WBsoCdxhUQkJc6jw3l0iOqkqMRsfHfoBv48ew9FCtXbWFNnS0zp6oMXmztBn3PgEGk1zqNDRFrp3J10rDhwA7si41Hyb1pb73qY0tUHXRrZc4g4EWlg0CGiWk8IgaNRKVh+IArHbqSolwc3dcSUrj4I8LSRsDoiqs0YdIio1lIqBXZeiseKAzdw4V4GAEBfLkM/f9UcOI0cLSSukIhqOwYdIqqV/ruZgv/bHomL9zIBAMYGcowI9MDETt5ws+EcOERUPgw6RFSrRCfnYPE/l7E7MgGAag6csR28MLa9F2zNjSSujojqGgYdIqoV0nML8fW+KKw9fgvFSgG5DBjZ1gNvBDeCHQMOEVUSgw4RSaqwWIl1/8Xgq33XkZGnmuiva2N7vPtiU/bBIaJnxqBDRJIQQmBPZAIW77iC6OQcAEBjRwu816cpOjeyl7g6ItIWDDpEVOMu3svA/22PxH83UwEAduaGeOv5xhga4MaJ/oioSuls0OElIIhqXnxGPj7bdRWbz96FEKqriU/q5I0pXRvA3Ehn346IqBrxEhC8BARRtcstLMa3B2/iu0M3kVek+ueiv78LZvVqzKHiRFQpvAQEEUlOqRTYdOYuPt99FQmZBQCANp42+N9LvvB3t5a2OCLSCQw6RFQtjt1IxqLtl3EpVjXhn3s9E8zt3RS9mzvxelREVGMYdIioSt1MysbiHVew56EJ/17v0QAh7b1gpK8ncXVEpGsYdIioSqTlFOKrfdex7r8YFCsF9OQyjGrrgRk9GnJGYyKSDIMOET2TwmIl1h6/ha/3XUdmfjEAoHsTB7z7YhM0cOCEf0QkLQYdIqoUIQR2XUrAxzsu41ZKLgCgiZMF/tfHFx0b2klcHRGRCoMOEVXYhbsZ+HB7JE5Gl0z4Z4RZvRphSIA79OTsaExEtQeDDhGVW1xGnmrCvzP3AABG+nK82rk+Jnfx4YR/RFQr8Z2JiJ4qt7AYKw/exHeHbiC/SAkAGNjKFbN6NYaLtYnE1RERPR6DDhE90ZHryZj753ncSc0DAAR51cN7fZqiJSf8I6I6gEGHiMqUkVuE/9seiQ3hdwEALlbGeP8lX7zACf+IqA5h0CGiUnZciMP7f11CcnYBZDJgzHOemPVCE/bDIaI6h+9aRKSWkJmPeX9dxK5LqlmNfezN8OkQPwR41pO4MiKiytHZoBMWFoawsDAoFAqpSyGSnBACv5+6g0X/XEZWfjH05TJM7eqD0O4NeNkGIqrTZEIIIXURUirvZd6JtNWt5BzM3XwBx2+mAABaulnh48F+aOrMvwciqr3K+/mtsy06RLquWKHEj0ejsXTPNeQXKWFsIMfbzzfGuA7enPSPiLQGgw6RDoqMzcSczedx/m4GAKC9jy0WD2oBT1sziSsjIqpaDDpEOiS/SIFl/0Zh5cEbKFYKWBjr4/0+vhjaxo1DxolIKzHoEOmIU7dSMWfTedxIygEAvNDMCQv7N4ODpbHElRERVR8GHSItl11QjE93XsHa4zEAAHsLI3zYvxleaO4scWVERNWPQYdIi+2/koj3/ryA2Ix8AMCwNm5470VfWJkaSFwZEVHNYNAh0kKpOYVY+PclbImIBQB41DPF4kEt0KGBncSVERHVLAYdIi0ihMDWc7H44O9IpOYUQi4DxnfwxsznG8HUkH/uRKR7+M5HpCVi0/Pwvy0X8e+VRABAEycLfDLYj1cZJyKdxqBDVMcplQLrT8Tgk51XkV1QDEM9OV7v3gCTu/jAUF8udXlERJJi0CGqw24kZWPOpvM4dSsNABDgaYNPBrdAAwcLiSsjIqodGHSI6qAihRLfHbqJr/ZdR2GxEmaGenjnhSZ45TlPyHn5BiIiNQYdojrm/N10zN50AZfjMgEAXRrZY9HA5nCzMZW4MiKi2odBh6iOUCoFwvZH4Yu916AUgI2pAeb19cUAf1devoGI6DEYdIjqgNzCYrz1xznsuBgPAOjb0gXz+/rCztxI4sqIiGo3Bh2iWu5Oai4mrT2NK/FZMNCT4cP+zTEiyEPqsoiI6gQGHaJa7L+bKZi6/gxScwphZ26IlaMD0MarntRlERHVGXV+ko07d+6ga9eu8PX1hZ+fHzZs2CB1SURVYt1/MRi96gRScwrR3NUSf03ryJBDRFRBdb5FR19fH19++SX8/f0RHx+PgIAAvPjiizAzM5O6NKJKKSxW4oO/L2H9idsAVP1xPh3sBxNDPYkrIyKqe+p80HF2doazszMAwMnJCXZ2dkhNTWXQoTopJbsAU9afwcnoVMhkwNvPN8bUrj4cVUVEVEmSn7o6dOgQ+vbtCxcXF8hkMmzZsqXUNmFhYfDy8oKxsTHatm2LkydPlrmv8PBwKBQKuLu7V3PVRFXvUmwG+i07ipPRqTA30seqMW0Q2q0BQw4R0TOQPOjk5OSgZcuWCAsLK3P977//jpkzZ2L+/Pk4c+YMWrZsiV69eiExMVFju9TUVIwZMwbfffddTZRNVKW2n4/DkBXHcS89D162ptgS2h49mjpKXRYRUZ0nE0IIqYsoIZPJ8Oeff2LAgAHqZW3btkVgYCCWLVsGAFAqlXB3d8frr7+OOXPmAAAKCgrQs2dPTJo0Ca+88soTH6OgoAAFBQXq25mZmXB3d0dGRgYsLS2r/qCInkCpFPhy7zV8/W8UAKBTQzsse7k1rEwNJK6MiKh2y8zMhJWV1VM/vyVv0XmSwsJChIeHIzg4WL1MLpcjODgYx48fBwAIITB27Fh07979qSEHABYvXgwrKyv1F09zkVSyC4oxeV24OuRM7OiN1WMDGXKIiKpQrQ46ycnJUCgUcHTUbMJ3dHREfLxqhtijR4/i999/x5YtW+Dv7w9/f39cuHDhsfucO3cuMjIy1F937typ1mMgKktMSg4GLT+KPZEJMNST4/OhLfG/l3yhr1er/ySJiOqcOj/qqmPHjlAqleXe3sjICEZGnDafpHM0Khmhv5xBem4R7C2M8O0rAWjtYSN1WUREWqlWBx07Ozvo6ekhISFBY3lCQgKcnJwkqoqocoQQ+OnYLXy4/TIUSoGWblb49pU2cLIylro0IiKtVavbyQ0NDREQEIB9+/aplymVSuzbtw/t2rV7pn2HhYXB19cXgYGBz1om0VMVFCswZ9MFLPg7EgqlwMBWrvh9cjuGHCKiaiZ5i052djaioqLUt6OjoxEREYF69erBw8MDM2fOREhICNq0aYOgoCB8+eWXyMnJwbhx457pcUNDQxEaGqrutU1UXZKyCvDaunCEx6RBLgPm9G6CSZ3qc34cIqIaIHnQOX36NLp166a+PXPmTABASEgI1qxZg+HDhyMpKQnz5s1DfHw8/P39sXPnzlIdlIlqowt3M/Dqz6cRl5EPC2N9fPNyK3Rt7CB1WUREOqNWzaMjhfKOwyeqqL8i7uGdjedRUKxEfXszrBrTBvXtzaUui4hIK5T381vyFh0ibaNQCny++ypWHLgBAOjW2B5fvdwKlsacH4eIqKbpbNAJCwtDWFgYFAqF1KWQFsnML8Ibv0Xg3yuqS5S81sUHs3o1hp6c/XGIiKTAU1c8dUVVJDo5BxN/OoUbSTkw0pfjk8F+GNDKVeqyiIi0UrWdurp8+TJ+++03HD58GDExMcjNzYW9vT1atWqFXr16YfDgwZyQj3TOoWtJmPbLGWTmF8PJ0hjfjQmAn5u11GUREem8crfonDlzBu+88w6OHDmCDh06ICgoCC4uLjAxMUFqaiouXryIw4cPIzMzE++88w7eeOONOhF42KJDz0IIgR+OROOjfy5DKYDWHtZYOToADpacH4eIqDpVeYvO4MGDMWvWLGzcuBHW1taP3e748eP46quvsGTJErz77rsVKpqoLskvUuDdPy9g85l7AIBhbdzw4YDmMNLXk7gyIiIqUe4WnaKiIhgYlH/USEW3r2kPd0a+du0aW3SoQhIz8/Hqz+GIuJMOPbkM/+vTFGPbe3ESQCKiGlLeFp0q64ycnp7+xJae2oqnrqiirsZnYdzqk4jNyIeViQHCRrZGx4Z2UpdFRKRTyvv5XalrXX3yySf4/fff1beHDRsGW1tbuLq64ty5c5XZJVGdcDQqGUNWHENsRj7q25vhr9AODDlERLVYpYLOypUr4e7uDgDYs2cP9uzZgx07dqB3796YNWtWlRZIVFtsDL+LkB9PIqugGEFe9bB5Snt42ZlJXRYRET1BpSYMjI+PVwedbdu2YdiwYXj++efh5eWFtm3bVmmBRFITQuDrfVH4Yu81AEDfli74bIgfjA3Y6ZiIqLarVIuOjY0N7ty5AwDYuXMngoODAag+EOrKTMNhYWHw9fVFYGCg1KVQLVakUGLWxvPqkDOlqw++Gu7PkENEVEdUqkVn0KBBGDlyJBo2bIiUlBT07t0bAHD27Fk0aNCgSgusLqGhoQgNDVV3ZiJ6VGZ+EaauO4MjUcmQy4APBzTHqLaeUpdFREQVUKmg88UXX8DLywt37tzBp59+CnNz1RWZ4+LiMHXq1CotkEgKcRl5GLf6FK7EZ8HUUA9hI1ujWxMHqcsiIqIK4rWuOLycHhEZm4lxa04iIbMA9hZGWD02EM1d2epHRFSbVNu1rgBg7dq1T1w/ZsyYyuyWSHKHriVh6vozyC4oRkMHc6weFwg3G1OpyyIiokqqVIuOjY2Nxu2ioiLk5ubC0NAQpqamSE1NrbICqxtbdKjEH6fuYO6fF6BQCrSrb4uVrwTAyqT2zu5NRKTLqrVFJy0trdSy69evY8qUKZxHh+ocIQSW7rmGb/6NAgAMbOWKTwb7wVC/UoMSiYioFqmyd/KGDRvi448/xowZM6pql9WKw8sJAAqLlXjrj3PqkPN69wZYOqwlQw4RkZao0s7IERER6Ny5MzIzM6tql9WOp650V0ZeEV77ORzHb6ZATy7DRwObY3igh9RlERFROVTrqautW7dq3BZCIC4uDsuWLUOHDh0qs0uiGnU3LRfjVp/C9cRsmBnqYfnoAHRpZC91WUREVMUqFXQGDBigcVsmk8He3h7du3fHkiVLqqIuompz8V4Gxq05haSsAjhaGuHHsYFo5sLh40RE2qhSQUepVFZ1HUQ1Yv+VRIT+cga5hQo0cbLAj2MD4WJtInVZRERUTSoVdIjqovUnYjDvr0tQKAU6NrDD8tGtYWnM4eNERNqs3ENLPv74Y+Tl5ZVr2xMnTmD79u2VLoqoKimVAp/svIL3/rwIhVJgSIAbVo8LZMghItIB5Q46kZGR8PDwwNSpU7Fjxw4kJSWp1xUXF+P8+fNYvnw52rdvj+HDh8PCwqJaCiaqiIJiBWb8HoEVB24AAN4MboTPhvjBQI/Dx4mIdEG5T12tXbsW586dw7JlyzBy5EhkZmZCT08PRkZGyM3NBQC0atUKEydOxNixY2FsbFxtRVeFsLAwhIWFQaFQSF0KVZP03EK8+nM4TkanQl8uw8eD/TAkwE3qsoiIqAZVah4dpVKJ8+fPIyYmBnl5ebCzs4O/vz/s7Oyqo8ZqxXl0tNOd1FyMXX0SN5JyYGGkj5WvBKBDg7r3+0lERGWr1nl05HI5/P394e/vX9n6iKrNuTvpmPDTKSRnF8LZyhirxwWiiRNDLBGRLuKoK9IqeyITMP3Xs8grUsDX2RKrxwXC0bJ2n0YlIqLqw6BDWmPt8VtYsPUSlALo0sgeYaNaw9yIv+JERLqMnwJU5ymVAot3XMb3h6MBACMC3fHhgOYcWUVERAw6VLflFynw1h/nsP1CHABgVq/GmNrVBzKZTOLKiIioNnimoBMVFYUbN26gc+fOMDExgRCCHzBUYzLzizDxp9M4GZ0KAz0ZPh/aEv39XaUui4iIapFKte2npKQgODgYjRo1wosvvoi4ONV/0xMmTMBbb71VpQUSlSUluwAjv/8PJ6NTYWGkj7Xj2zLkEBFRKZUKOm+++Sb09fVx+/ZtmJqaqpcPHz4cO3furLLiiMoSm56HYd8ex8V7mbA1M8Rvk59DOx9bqcsiIqJaqFKnrnbv3o1du3bBzU1zltmGDRsiJiamSgojKkt0cg5GrzqBe+l5cLEyxrqJbVHf3lzqsoiIqJaqVItOTk6ORktOidTUVBgZGT1zUTUhLCwMvr6+CAwMlLoUKqfI2EwMXXkM99LzUN/ODBumtGfIISKiJ6pU0OnUqRPWrl2rvi2TyaBUKvHpp5+iW7duVVZcdQoNDUVkZCROnToldSlUDqdvpWL4d8eRnF0IX2dL/PFaO7ham0hdFhER1XKVOnX16aefokePHjh9+jQKCwvxzjvv4NKlS0hNTcXRo0erukbScQevJWHyz6eRX6REoJcNVoUEwsrEQOqyiIioDqhUi07z5s1x7do1dOzYEf3790dOTg4GDRqEs2fPwsfHp6prJB32z4U4TPzpFPKLlOjSyB5rx7dlyCEionKr1NXLtQmvXl57/X7qNuZuvgClAPr4OeOLYf4w1Odsx0REVM1XLweA/Px8nD9/HomJiVAqlRrr+vXrV9ndEgEAvj90E4v+uQwAeDnIHf83oAX05JyMkoiIKqZSQWfnzp0YM2YMkpOTS62TyWRQKBTPXBjpJiEEluy+hmX7owAAk7vUx5wXmnDGbSIiqpRKnQd4/fXXMXToUMTFxUGpVGp8MeRQZSmVAvO3XlKHnHdeaIy5vZsy5BARUaVVqkUnISEBM2fOhKOjY1XXQzqqSKHEOxvP48+z9yCTAQv7N8crz3lKXRYREdVxlWrRGTJkCA4cOFDFpZCuyi9SYMq6cPx59h705TJ8OdyfIYeIiKpEpUZd5ebmYujQobC3t0eLFi1gYKA53Hf69OlVVmB146graWUXFGPiT6fw381UGOnLsWJ0a3RvwpZCIiJ6smoddfXrr79i9+7dMDY2xoEDBzT6UMhksjoVdEg6qTmFGLv6JM7fzYC5kT5WhbTBc/V5cU4iIqo6lQo67733Hj744APMmTMHcjnnNaGKi8/Ixys/nMD1xGzYmBpg7fi2aOFmJXVZRESkZSoVdAoLCzF8+HCGHKqUmJQcjFp1AnfT8uBkaYx1E4PQwMFC6rKIiEgLVSqphISE4Pfff6/qWkgHXInPxJCVx3E3LQ9etqbY8Fo7hhwiIqo2lWrRUSgU+PTTT7Fr1y74+fmV6oy8dOnSKimuOoWFhSEsLIzz/tSgM7fTMG71KWTkFaGJkwXWTgiCg4Wx1GUREZEWq9Soq27duj1+hzIZ/v3332cqqiZx1FXNOHI9Ga/+fBq5hQq09rDG6rFBsDLlxTmJiKhyqnXU1f79+ytdGOmenRfjMf3XsyhUKNGpoR2+fSUApoaVvswaERFRufHThqrVhtN3MHvTeSgF0Lu5E74c4Q8jfT2pyyIiIh1R7qAzaNAgrFmzBpaWlhg0aNATt928efMzF0Z1349HorFwWyQAYGiAGxYPagF9PY7UIyKimlPuoGNlZaWeGNDKivOd0OMJIfDl3uv4at91AMCEjt5478WmkMt5cU4iIqpZFeqMvHDhQrz99tswNTWtzppqFDsjVy2lUmDhtkisOXYLADCzZyO83r0Br0BORERVqryf3xU6j/DBBx8gOzv7mYsj7VSsUGLWxvPqkLOgry+m92jIkENERJKpUGfkSoxEJx2RX6TA9F/PYndkAvTkMnw62A+DA9ykLouIiHRchUdd8b9zepRSKTDtl7PYezkBhnpyLBvZCs83c5K6LCIioooHnUaNGj017KSmpla6IKp7lu2Pwt7LCTDSl2P12EC0b2AndUlEREQAKhF0PvjgA466IrWD15Lwxd5rAID/G9CcIYeIiGqVCgedESNGwMHBoTpqoTrmblouZvx2FkIALwd5YGgbd6lLIiIi0lChUVfsn0Ml8osUmLr+DNJzi+DnZoX5fX2lLomIiKiUCgUdjrqiEh/8HYnzdzNgbWqA5aNaw9iAl3UgIqLap0KnrpRKZXXVQXXIhtN38OvJ25DJgK9HtIKbjfZMIElERNqFFx6iCrkUm4H/bbkIAHgzuBE6N7KXuCIiIqLHY9ChcsvILcJr68JRUKxE9yYOmNatgdQlERERPRGDDpWLUinw5h8RuJOaB/d6JvhimD8v0klERLUegw6VS9j+KPx7JRFG+nKsGBUAK1MDqUsiIiJ6Kq0IOgMHDoSNjQ2GDBkidSla6dC1JCy9PynghwOao7krJ4wkIqK6QSuCzowZM7B27Vqpy9BKmpMCumMYJwUkIqI6RCuCTteuXWFhYSF1GVqnoFiB0PVnkJZbhBauVpjft5nUJREREVWI5EHn0KFD6Nu3L1xcXCCTybBly5ZS24SFhcHLywvGxsZo27YtTp48WfOF6qAP/o7EufuTAq4YzUkBiYio7pE86OTk5KBly5YICwsrc/3vv/+OmTNnYv78+Thz5gxatmyJXr16ITExsYYr1S0bw+/ilxOqSQG/4qSARERUR1X4op5VrXfv3ujdu/dj1y9duhSTJk3CuHHjAAArV67E9u3b8eOPP2LOnDkVfryCggIUFBSob2dmZla8aC13KTYD7/15AQDwRo9G6MJJAYmIqI6SvEXnSQoLCxEeHo7g4GD1MrlcjuDgYBw/frxS+1y8eDGsrKzUX+7u7Fz7sIzcIkxZdwYFxUp0a2yP17tzUkAiIqq7anXQSU5OhkKhgKOjo8ZyR0dHxMfHq28HBwdj6NCh+Oeff+Dm5vbEEDR37lxkZGSov+7cuVNt9dc1SqXAzD8icDs1F242JvhiOCcFJCKiuk3yU1dVYe/eveXe1sjICEZGRtVYTd21/EAU9l1JhKG+HCtHB8Da1FDqkoiIiJ5JrW7RsbOzg56eHhISEjSWJyQkwMnJSaKqtNPh60lYskc1KeD/9eekgEREpB1qddAxNDREQEAA9u3bp16mVCqxb98+tGvX7pn2HRYWBl9fXwQGBj5rmXXevfQ8TP9VNSngiEB3DAtkvyUiItIOkp+6ys7ORlRUlPp2dHQ0IiIiUK9ePXh4eGDmzJkICQlBmzZtEBQUhC+//BI5OTnqUViVFRoaitDQUGRmZsLKSndbLwqKFZi6Llw9KeCCfpwUkIiItIfkQef06dPo1q2b+vbMmTMBACEhIVizZg2GDx+OpKQkzJs3D/Hx8fD398fOnTtLdVCmynl4UsDlozgpIBERaReZEEJIXYSUSlp0MjIyYGlpKXU5NWpj+F28veEcZDJg9dhAdG3sIHVJRERE5VLez+9a3UenOul6H52HJwWc0aMhQw4REWkltujoYItORm4R+i47gtupueja2B4/hgRyvhwiIqpT2KJDZXp0UsAvOSkgERFpMQYdHcNJAYmISJcw6OiQhycF/LB/M04KSEREWk9ng46udUZ+eFLA4W3cMTzQQ+qSiIiIqh07I+tAZ+SCYgWGrTyOc3cz0NzVEhtfa8/5coiIqE5jZ2RSW3h/UkArEwOsGBXAkENERDqDQUfLbQq/i/UnbkMmA74c4Q/3eqZSl0RERFRjJL8EBFWfyNhMvHt/UsDp3RuiGycFJCKiqqYoArITgKwEICsOyI5X/VzyPScRmLgPkEtzNoFBR0tl5BVhyvpwFBQr0bWxPWb0aCh1SUREVJcU5QFZ8fdDzKPf4x6EmdyUp+8rJxmwkOYalTobdMLCwhAWFgaFQiF1KVVOqRR4648IxKRwUkAiInpEQdZDLS5lBJeS7/kZ5d+n3AAwd1SFGXMnwOL+l7mj6ruRRfUdz1Nw1JUWjroK2x+Fz3ZdhaG+HJuntOd8OURE2kQIoDAbyM9UhZGCzId+znjM8kxVq0pWPFCUU/7H0jd+EFYsnO6HGMcH3y2cVT+b2ADymu32W97Pb51t0dFWR64nY8nuqwA4KSARUa1UlH8/hNwPJQUZD34uK6A8GmIKMgGhfLYaDM3LEWAcAWMrQFa3zwgw6GiRwmIl3tl4DkpOCkhEVHOEAPLSgJwk1Vd24kPfE4HspIe+JwHFeVXzuHJ9VRAxsgSMLR/62Upzecky03oPAoyRedXUUAcw6GiRLRH3EJuRDwcLI3zQv5nU5RAR1V1KBZCbej+gJGoGmFIhJglQFlXwAWT3Q4jVIyHloWBS6mcrzeUGJnW+taUmMOhoCYVSYOXBGwCAiZ28OSkgEdUtQqhOxygVgFDc/668/7OyjGUlPz/lPmWuv78sP71060tOsurn3OSKnx4ytgLMHABzB8DMXvVV8rO5w/119oBJPdWpoxru06KrdDboaNuoq92X4nEzKQeWxvoY2dZT6nKIiEpTFAFJV4H480DcOSDuPJBwSdXnBLVxXIxMdbqnJKCoQ4ydZqAp+a5vJHXBVAaOutKCUVdCCPQPO4rzdzPwevcGeOv5xlKXRES6rihPFWLizqm+4s8DCZGAoqDy+5TJAZmeauI59XeZ5jKZ/P7PD33XWC978LOxpWaIMbPXDDSmdoCezrYH1HocdaVDjkal4PzdDBgbyDG2vZfU5RCRrslLB+IvaLbUJF8t+9SPoQXg7Ac4+QHOLQGnFqqAoQ4l8jJCix5P81ClMehogeUHogAAIwI9YGvOplMiqkZZCfcDTYQq0MSfB9Julb2tqZ0qzDiXhBo/wMaboYVqFINOHRdxJx3HbqRAXy7DpM71pS6HiLSFEEB6jCrMlJx6ijuvmjG3LFYemi01zn6qocwcFUQSY9Cp45bvV7XmDGjlCldrE4mrIaI6p2QYdXYCkBip2aemzEsAyAC7hvcDzUMtNab1arx0ovJg0KnDridkYXdkAmQy4LUuPlKXQ0S1gRCqYdM5yfe/7s/zUvJz7iPLc1Px2BFPcgPAoen9QOOvCjSOzXRqsjmq+xh06rAV9+fN6eXrhAYOfOMh0kpCAIU590NJykPBJQnISdEMMiUhpsKT10F1rSK7RpqnnuybAvqGVX9MRDWIQaeOupuWi60RsQCAKV3ZmkNUZwkBZMYCSZeBxMuqeWayEzRbZCpzyQAjS8DU9sHEdWZ2978eum16/7ZpPUDPoOqPjagW0NmgU9cnDPz+0E0UKwU6NLBFS3drqcshovLITlL1g0m6ovqeeBlIvKK6WOPT6Bs/FFjuhxWNIGMPmN2/bWoHGBhX//EQ1QGcMLAOThiYnF2ADh//i4JiJdZPbIsODeykLomIHpaXpgowJWGmJNjkppS9vUwPsPVR9YexbwJYuT1obSkJNoZmHMFE9BBOGKjF1hy9hYJiJVq6WaG9j63U5RDproIs1ammxMgHwSbpCpAV95g7yAAbL8DBF3Boovpu30Q1iomXDyCqFgw6dUxWfhF+On4LADClawPI+B8eUfUrylMFGvUppyuqlpqM24+/j5W7KsQ4NH3wZdcYMDStubqJiEGnrll/4jay8ovRwMEcz/s6Sl0OkXZRKoH0W/cvZ3Dhfh+aSNXMv4+7krW54/1TTiWBxhewb6y6jhIRSY5Bpw7JL1LghyPRAFTz5sjlbM0hqrTiAlWQKQk18eeB+ItAYVbZ25vUe+iU00PBhhPlEdVqDDp1yMbwu0jKKoCrtQn6+7tIXQ5R3ZGXpgox6kBzQXUaSllcels9I1WAcWqhmhyvJNSYO7AzMFEdxKBTRxQrlPj2kGqCwEmdvGGgx4viEZUiBJBx90GYKQk26Y/pS2Ns/eD6TE4tVN/tGnJOGSItwqBTR2y/EIc7qXmoZ2aI4YEeUpdDJD1FEZB87UGgiTun+p6fXvb21h6agcaphWoYN1tpiLQag04dIITAigOq1pxx7b1gYqgncUVENawgC0i4pLp6dklrTeJlQFFQelu5vmq0kzrUtACcmqsucUBEOkdng05dmhn53yuJuBKfBTNDPYxp5yV1OURVTwjVxSXTbwFpMUB6jOp0U1oMkHoTSIsu+36GFg+FmRb3r8/UhHPSEJEaZ0auAzMjD1lxDKdj0jC5c33MfbGp1OUQVU5+pirAPBpkSn4uzH7y/S1cHoSZkmBj7QXI2V+NSBdxZmQtcTI6Fadj0mCoL8eEjt5Sl0P0eEV5j4SXh0JNWszj+848zMJZ1ZfG2hOw8Xzw3cFXdSkEIqIKYtCp5ZYfiAIADAlwg4MlL9JHElIUARl3ym6RSYsBchKfvg9T29JBpuRnK3deiJKIqhyDTi12KTYDB64mQS4DJneuL3U5pEuUSiDlOnD3NHD3FHDvNJAQCYin9GkztNBsibH2VAUbm/vfjSxqpn4iovsYdGqxkpFWffxc4GlrJnE1pNVyUlRhRh1szgAFGaW30zcuo0XG48HPJjYcrk1EtQqDTi11KzkH/1xQXQF5ShcfiashrVJcCCRcuB9q7gebskY16ZsALq0AtwDALRBwDQAsXRlkiKhOYdCppb49dANKAXRrbA9fl9o5GozqACFUfWnUrTWnVRPrlTX/jF0jwLUN4Hb/y8GXMwQTUZ3HoFMLJWTmY1P4PQDA1G4NJK6G6pSCLNVpp4eDTVmdhE1s7rfS3A81rq05oR4RaSUGnVpo1eGbKFQoEehlg0AvXhmZHkOpAJKuPugsfPe0arZgPDI1llxfNefMw8GmXn2egiIincCgU8uk5xbilxOqCxBO7crWHIIq0GTeezCcOyVKFWzunSl7kj0rj4f61bRRTbBnYFLzdRMR1QIMOrXM2uMxyClUoKmzJbo2tpe6HKoJSiWQHf/QvDS3VZdCKLmdeQ9QFpd9XwMz1WkntzYPgo2FY42WT0RUmzHo1CK5hcVYfVQ1+mVKVx/IeGpBOwgB5CTdDy637geZhybcy7gDKAqfvA+5AWDt/mBYt0srVbCxbwLIeZFXIqLHYdCpRX47eQdpuUXwqGeKF5s7SV0OlZcQQF7aQ5c8eCTIpN8GivOevA+ZHmDlqjlTcMl8NdYeqksj8JpOREQVxqBTSxQWK/H94ZsAgMld6kNfjx9qtVZ+JnBxI3B9z4MgU5j1lDvJAEuX0jMFl9y2dAX0+OdIRFTVdPadNSwsDGFhYVAonjKlfQ3ZEnEPcRn5sLcwwuDWblKXQ48SQjW6Kfwn4NJmoCi39Dbmjo8PMlbugL5hzddNRKTjZEII8fTNtFd5L/NenRRKgZ5fHMTNpBzM7d0EkzkTcu2Rmwqc/wM48xOQGPlguV1jwH8k4Nj8fqBx58gmIqIaVN7Pb51t0alNdl+Kx82kHFga62PUc55Sl0NCADFHVa03kX89mEVY3wRoNhAICAHc23IeGiKiOoBBR2JCCCy/f/HOkPZeMDfiSyKZ7CTg3C/AmbWquWpKOLUAWocALYYCJtaSlUdERBXHT1WJHYlKxoV7GTA2kGNsey+py9E9SiVwc7/q1NSVfwBlkWq5oTnQfDAQMFY1lJutN0REdRKDjsRW3G/NGRHoAVtzI4mr0SGZscDZ9cDZtapRUyVcA1StN80HA0bm0tVHRERVgkFHQhF30nHsRgr05TJM6lxf6nK0n6IYiNqj6ntzfRcglKrlxlaA33BVwHFqLm2NRERUpRh0JLR8v6ofSH9/V7hac8ROtUmLAc7+rGrByYp9sNyjvapjsW9/jpgiItJSDDoSuZ6Qhd2RCZDJgCld2ZpT5YoLgWs7gPA1wI39UF/R29QWaPmyqvXGvpGUFRIRUQ1g0JHIioOqvjnP+zqigYOFxNVokZQbqo7FEb+ori9VwruLqmNxkz6APvtCERHpCgYdCdxNy8XWCNUplKldG0hcjRYoygcu/60KOLcOP1hu7gj4jwJavwLUY6sZEZEuYtCRwPeHbqJYKdChgS1aultLXU7dI4Sq5SbmKBBzTNWxOC9NtU4mBxoEq05NNeoF6BlIWysREUmKQaeGJWcX4LdTdwCwNafclEog6bIq1Nw6ovqek6i5jaWbquWm1WjAitcKIyIiFQadGrb6aDQKipXwc7NCex9bqcupnRTFQPx5VaApabXJT9fcRs8IcGsDeLYHvDoBXh0BuZ4k5RIRUe3FoFODsvKLsPZ4DABgalcfyDjbrkpxARB7VhVqbh0F7pwACrM1tzEwA9yDAM8OgFcHwKU1YGAsTb1ERFRnMOjUoPUnbiMrvxg+9mZ43tdJ6nKkU5gL3D31oMXm7imgOF9zGyMrwLOdqsXGswPg3JL9bYiIqMIYdGpIfpECPxyJBgC81sUHcrkOtebkZ6paaUpabGLPPrimVAlTuwehxrM94NiMp6KIiOiZMejUkI3hd5GUVQAXK2P093eVupzqlZMC3D5+v8XmCBB/4cHlFkpYuKhOQZWEG7tGvHAmERFVOQadGlCsUOLbQ6oJAid1rg9DfbnEFVWDu6eBc7+qWmySLpdeb+P9oLXGsz1g48VgQ0RE1Y5BpwZsvxCHO6l5qGdmiBGBHlKXU7Wyk4C9C4CIdZrL7Rrfb7HpAHi0A6y0vBWLiIhqJa0IOtu2bcNbb70FpVKJ2bNnY+LEiVKXpCaEwIoDqtacce29YGKoJf1OlArg9I/Avx8C+RmqZS2GAU37qoKNub209REREUELgk5xcTFmzpyJ/fv3w8rKCgEBARg4cCBsbWvHHDX/XknElfgsmBnqYUw7L6nLqRp3TgH/vAXEnVPddmoB9FmqGv5NRERUi9T5ziInT55Es2bN4OrqCnNzc/Tu3Ru7d++WuiwAqtac5fdbc0Y95wkr0zo+PDonGfhrGvBDsCrkGFsBL34OvHqQIYeIiGolyYPOoUOH0LdvX7i4uEAmk2HLli2ltgkLC4OXlxeMjY3Rtm1bnDx5Ur0uNjYWrq4P+n+4urri3r17NVH6U52MTkV4TBoM9eSY0NFb6nIqT6kATq0CvgkAzv6sWuY/GpgWDgRN4jBwIiKqtSQPOjk5OWjZsiXCwsLKXP/7779j5syZmD9/Ps6cOYOWLVuiV69eSExMLHP72mTFQVVrzuAANzha1tFZfO+eBr7vBmx/S3UZBqcWwPjdwIAw9sMhIqJaT/I+Or1790bv3r0fu37p0qWYNGkSxo0bBwBYuXIltm/fjh9//BFz5syBi4uLRgvOvXv3EBT0+NMoBQUFKCgoUN/OzMysgqMo7VJsBg5cTYJcBrzWpX61PEa1ykkB9i0AzqxV3TayArr/D2gzHtCT/NeGiIioXCRv0XmSwsJChIeHIzg4WL1MLpcjODgYx48fBwAEBQXh4sWLuHfvHrKzs7Fjxw706tXrsftcvHgxrKys1F/u7u7VUnvJSKs+fi7wtDWrlseoFiWjqb5p/SDk+I8CXj8NtH2VIYeIiOqUWv2plZycDIVCAUdHR43ljo6OuHLlCgBAX18fS5YsQbdu3aBUKvHOO+88ccTV3LlzMXPmTPXtzMzMKg87iZn52HExHgAwpYtPle67Wt0NV42mij2ruu3YAujzOeDxnLR1ERERVVKtDjrl1a9fP/Tr169c2xoZGcHIyKha63GwNMb26R1x6FoSfF0sq/WxqkROCrDvg/stOAIwsrx/mmoCW3CIiKhOq9WfYnZ2dtDT00NCQoLG8oSEBDg51e6rfzdxskQTp1oecpQKVbjZ9wGQl6Za1vJloOdCwNxB2tqIiIiqQK3uo2NoaIiAgADs27dPvUypVGLfvn1o167dM+07LCwMvr6+CAwMfNYy66Z74cCqYGDbG6qQ49AMGLcDGLiSIYeIiLSG5C062dnZiIqKUt+Ojo5GREQE6tWrBw8PD8ycORMhISFo06YNgoKC8OWXXyInJ0c9CquyQkNDERoaiszMTFhZWT3rYdQduamqFpzwn6A+TdXtXSBwEk9TERGR1pH8k+306dPo1q2b+nZJR+GQkBCsWbMGw4cPR1JSEubNm4f4+Hj4+/tj586dpToo01MolcDZtaoLcJacpvIboTpNZcHnkoiItJNMCCGkLkJKJS06GRkZsLSs5X1qKuveGeCft1WnqwDAwVd16QavDtLWRUREVEnl/fyWvEVHKmFhYQgLC4NCoZC6lOqTm6q6uvjp1QAEYGihOk0VNAnQq+PX3SIiIioHtuhoY4uOUglErAP2zAfyUlXLWgwDnv8QsKjdo9WIiIjKgy06uir+AvD3G8C906rb9k1Vk/55dZS0LCIiIikw6GiTK9uBjROA4rz7p6nmAkGv8jQVERHpLAYdbXHiO2DHOwAE4NMD6B8GWDpLXRUREZGkavWEgdVJayYMVCqBXe8BO2YBEEDrEGDkHww5REREYGfkut0ZuSgf+HMyELlFdbv7+0CntwCZTNKyiIiIqhs7I2u73FTg15eBO/8BcgNgwHLAb5jUVREREdUqDDp1UepNYP1QICUKMLICRqwDvDtLXRUREVGtw6BT19w9DfwyHMhNBqzcgVEbAIemUldFRERUKzHo1CUPDx938lOFHE4ASERE9FgcdVVXRl2d+A74bZQq5DToCYzbwZBDRET0FBx1VdtHXSmVwJ73gePLVLdbhwB9lgJ6bIwjIiLdxVFX2qAoH/jzVSDyL9XtHvOAjjM5fJyIiKicGHRqq1LDx1cAfkOlroqIiKhOYdCpjVJvAuuGAKk37g8fXw94d5K6KiIiojqHQae24fBxIiKiKsOgU5s8PHzcuaXqmlUcWUVERFRpHF5eW4aXn/j2wfDxhs8DY/9hyCEiInpGHF4u9fDyR4ePB4wFXlzC4eNERERPwOHldUGp4ePzgY5vcvg4ERFRFWHQkQqHjxMREVU7Bh0pPDx83NgKGM7h40RERNWBQaemaQwf97g/fLyJ1FURERFpJQadmsTh40RERDWKQaemnPgW2DEbgFANHx+yGjAyl7oqIiIircZ5dKp7Hh2lEtj1HrDjHQACCBgHjPiVIYeIiKgGcB6d6pxHpygP+HMyh48TERFVMc6jI7WcFOC3l4E7JwA9Q6D/cg4fJyIiqmEMOtUhMxZY89KD4eMjfgG8OkpdFRERkc5h0KkOpnaApQugKAJGbwTsG0tdERERkU5i0KkO+obA8HVAcT6HjxMREUmIQae6mFhLXQEREZHO09nh5URERKT9GHSIiIhIazHoEBERkdZi0CEiIiKtpbNBp8YuAUFERESS4SUgqvMSEERERFQtyvv5rbMtOkRERKT9GHSIiIhIazHoEBERkdZi0CEiIiKtxaBDREREWotBh4iIiLQWgw4RERFpLZ2/ennJNEKZmZkSV0JERETlVfK5/bTpAHU+6GRlZQEA3N3dJa6EiIiIKiorKwtWVlaPXa/zMyMrlUrExsbCwsICMplM6nKeWWZmJtzd3XHnzh2dmOmZx6v9dO2YebzajcdbdYQQyMrKgouLC+Tyx/fE0fkWHblcDjc3N6nLqHKWlpY68UdUgser/XTtmHm82o3HWzWe1JJTgp2RiYiISGsx6BAREZHWYtDRMkZGRpg/fz6MjIykLqVG8Hi1n64dM49Xu/F4a57Od0YmIiIi7cUWHSIiItJaDDpERESktRh0iIiISGsx6BAREZHWYtCpow4dOoS+ffvCxcUFMpkMW7Zs0VgvhMC8efPg7OwMExMTBAcH4/r169IUWwUWL16MwMBAWFhYwMHBAQMGDMDVq1c1tsnPz0doaChsbW1hbm6OwYMHIyEhQaKKn82KFSvg5+ennmSrXbt22LFjh3q9Nh3roz7++GPIZDK88cYb6mXadrwLFiyATCbT+GrSpIl6vbYdLwDcu3cPo0ePhq2tLUxMTNCiRQucPn1avV6b3rO8vLxKvb4ymQyhoaEAtO/1VSgUeP/99+Ht7Q0TExP4+Pjgww8/1LgGlaSvr6A66Z9//hHvvfee2Lx5swAg/vzzT431H3/8sbCyshJbtmwR586dE/369RPe3t4iLy9PmoKfUa9evcTq1avFxYsXRUREhHjxxReFh4eHyM7OVm/z2muvCXd3d7Fv3z5x+vRp8dxzz4n27dtLWHXlbd26VWzfvl1cu3ZNXL16Vbz77rvCwMBAXLx4UQihXcf6sJMnTwovLy/h5+cnZsyYoV6ubcc7f/580axZMxEXF6f+SkpKUq/XtuNNTU0Vnp6eYuzYseLEiRPi5s2bYteuXSIqKkq9jTa9ZyUmJmq8tnv27BEAxP79+4UQ2vf6Llq0SNja2opt27aJ6OhosWHDBmFubi6++uor9TZSvr4MOlrg0aCjVCqFk5OT+Oyzz9TL0tPThZGRkfj1118lqLDqJSYmCgDi4MGDQgjV8RkYGIgNGzaot7l8+bIAII4fPy5VmVXKxsZGrFq1SmuPNSsrSzRs2FDs2bNHdOnSRR10tPF458+fL1q2bFnmOm083tmzZ4uOHTs+dr22v2fNmDFD+Pj4CKVSqZWvb58+fcT48eM1lg0aNEiMGjVKCCH968tTV1ooOjoa8fHxCA4OVi+zsrJC27Ztcfz4cQkrqzoZGRkAgHr16gEAwsPDUVRUpHHMTZo0gYeHR50/ZoVCgd9++w05OTlo166d1h5raGgo+vTpo3FcgPa+ttevX4eLiwvq16+PUaNG4fbt2wC083i3bt2KNm3aYOjQoXBwcECrVq3w/fffq9dr83tWYWEh1q1bh/Hjx0Mmk2nl69u+fXvs27cP165dAwCcO3cOR44cQe/evQFI//rq/EU9tVF8fDwAwNHRUWO5o6Ojel1dplQq8cYbb6BDhw5o3rw5ANUxGxoawtraWmPbunzMFy5cQLt27ZCfnw9zc3P8+eef8PX1RUREhNYd62+//YYzZ87g1KlTpdZp42vbtm1brFmzBo0bN0ZcXBw++OADdOrUCRcvXtTK47158yZWrFiBmTNn4t1338WpU6cwffp0GBoaIiQkRKvfs7Zs2YL09HSMHTsWgHb+Ps+ZMweZmZlo0qQJ9PT0oFAosGjRIowaNQqA9J9JDDpU54SGhuLixYs4cuSI1KVUq8aNGyMiIgIZGRnYuHEjQkJCcPDgQanLqnJ37tzBjBkzsGfPHhgbG0tdTo0o+U8XAPz8/NC2bVt4enrijz/+gImJiYSVVQ+lUok2bdrgo48+AgC0atUKFy9exMqVKxESEiJxddXrhx9+QO/eveHi4iJ1KdXmjz/+wPr16/HLL7+gWbNmiIiIwBtvvAEXF5da8fry1JUWcnJyAoBSvfgTEhLU6+qqadOmYdu2bdi/fz/c3NzUy52cnFBYWIj09HSN7evyMRsaGqJBgwYICAjA4sWL0bJlS3z11Vdad6zh4eFITExE69atoa+vD319fRw8eBBff/019PX14ejoqFXHWxZra2s0atQIUVFRWvf6AoCzszN8fX01ljVt2lR9uk5b37NiYmKwd+9eTJw4Ub1MG1/fWbNmYc6cORgxYgRatGiBV155BW+++SYWL14MQPrXl0FHC3l7e8PJyQn79u1TL8vMzMSJEyfQrl07CSurPCEEpk2bhj///BP//vsvvL29NdYHBATAwMBA45ivXr2K27dv19ljfpRSqURBQYHWHWuPHj1w4cIFREREqL/atGmDUaNGqX/WpuMtS3Z2Nm7cuAFnZ2ete30BoEOHDqWmg7h27Ro8PT0BaOd7FgCsXr0aDg4O6NOnj3qZNr6+ubm5kMs144Senh6USiWAWvD6Vnt3Z6oWWVlZ4uzZs+Ls2bMCgFi6dKk4e/asiImJEUKohvJZW1uLv/76S5w/f17079+/zg7VFEKIKVOmCCsrK3HgwAGNYZu5ubnqbV577TXh4eEh/v33X3H69GnRrl070a5dOwmrrrw5c+aIgwcPiujoaHH+/HkxZ84cIZPJxO7du4UQ2nWsZXl41JUQ2ne8b731ljhw4ICIjo4WR48eFcHBwcLOzk4kJiYKIbTveE+ePCn09fXFokWLxPXr18X69euFqampWLdunXobbXvPUigUwsPDQ8yePbvUOm17fUNCQoSrq6t6ePnmzZuFnZ2deOedd9TbSPn6MujUUfv37xcASn2FhIQIIVTD+d5//33h6OgojIyMRI8ePcTVq1elLfoZlHWsAMTq1avV2+Tl5YmpU6cKGxsbYWpqKgYOHCji4uKkK/oZjB8/Xnh6egpDQ0Nhb28vevTooQ45QmjXsZbl0aCjbcc7fPhw4ezsLAwNDYWrq6sYPny4xpwy2na8Qgjx999/i+bNmwsjIyPRpEkT8d1332ms17b3rF27dgkAZR6Dtr2+mZmZYsaMGcLDw0MYGxuL+vXri/fee08UFBSot5Hy9ZUJ8dDUhURERERahH10iIiISGsx6BAREZHWYtAhIiIircWgQ0RERFqLQYeIiIi0FoMOERERaS0GHSIiItJaDDpEJKmuXbvijTfeqPbH8fLywpdfflntj1Mea9asKXX1aiKqHgw6RFQhSUlJmDJlCjw8PGBkZAQnJyf06tULR48eVW8jk8mwZcuWcu1v8+bN+PDDD6upWunVpoBFpIv0pS6AiOqWwYMHo7CwED/99BPq16+PhIQE7Nu3DykpKRXaT2FhIQwNDVGvXr1qqpSIiC06RFQB6enpOHz4MD755BN069YNnp6eCAoKwty5c9GvXz8AqhYMABg4cCBkMpn69oIFC+Dv749Vq1bB29sbxsbGAEqfuvLy8sJHH32E8ePHw8LCAh4eHvjuu+806jh27Bj8/f1hbGyMNm3aYMuWLZDJZIiIiKjQsUycOBH29vawtLRE9+7dce7cOfX6knp//vlneHl5wcrKCiNGjEBWVpZ6m6ysLIwaNQpmZmZwdnbGF198oXE8Xbt2RUxMDN58803IZDLIZDKNGnbt2oWmTZvC3NwcL7zwAuLi4spdPxGVD4MOEZWbubk5zM3NsWXLFhQUFJS5zalTpwAAq1evRlxcnPo2AERFRWHTpk3YvHnzE0PJkiVL0KZNG5w9exZTp07FlClTcPXqVQBAZmYm+vbtixYtWuDMmTP48MMPMXv27Aofy9ChQ5GYmIgdO3YgPDwcrVu3Ro8ePZCamqre5saNG9iyZQu2bduGbdu24eDBg/j444/V62fOnImjR49i69at2LNnDw4fPowzZ86o12/evBlubm5YuHAh4uLiNIJMbm4uPv/8c/z88884dOgQbt++jbfffrvCx0FET1Ejlw4lIq2xceNGYWNjI4yNjUX79u3F3Llzxblz5zS2ASD+/PNPjWXz588XBgYGIjExUWP5o1cq9/T0FKNHj1bfViqVwsHBQaxYsUIIIcSKFSuEra2tyMvLU2/z/fffCwDi7Nmzj63b09NTfPHFF0IIIQ4fPiwsLS1Ffn6+xjY+Pj7i22+/VddramoqMjMz1etnzZol2rZtK4RQXbHZwMBAbNiwQb0+PT1dmJqaljqeksctsXr1agFA44rlYWFhwtHR8bH1E1HlsEWHiCpk8ODBiI2NxdatW/HCCy/gwIEDaN26NdasWfPU+3p6esLe3v6p2/n5+al/lslkcHJyQmJiIgDg6tWr8PPzU5/6AoCgoKAKHcO5c+eQnZ0NW1tbdSuVubk5oqOjcePGDfV2Xl5esLCwUN92dnZW13Hz5k0UFRVpPLaVlRUaN25crhpMTU3h4+NT5r6JqOqwMzIRVZixsTF69uyJnj174v3338fEiRMxf/58jB079on3MzMzK9f+DQwMNG7LZDIolcrKlltKdnY2nJ2dceDAgVLrHh72XZ11lLVvIUSV7JuIHmCLDhE9M19fX+Tk5KhvGxgYQKFQVMtjNW7cGBcuXNDoI/RwP6DyaN26NeLj46Gvr48GDRpofNnZ2ZVrH/Xr14eBgYHGY2dkZODatWsa2xkaGlbbc0FET8egQ0TllpKSgu7du2PdunU4f/48oqOjsWHDBnz66afo37+/ejsvLy/s27cP8fHxSEtLq9IaRo4cCaVSiVdffRWXL1/Grl278PnnnwNAqVFNjxMcHIx27dphwIAB2L17N27duoVjx47hvffew+nTp8u1DwsLC4SEhGDWrFnYv38/Ll26hAkTJkAul2vU4eXlhUOHDuHevXtITk6u+AET0TNh0CGicjM3N0fbtm3xxRdfoHPnzmjevDnef/99TJo0CcuWLVNvt2TJEuzZswfu7u5o1apVldZgaWmJv//+GxEREfD398d7772HefPmAYBGv50nkclk+Oeff9C5c2eMGzcOjRo1wogRIxATEwNHR8dy17J06VK0a9cOL730EoKDg9GhQwc0bdpUo46FCxfi1q1b8PHxKVf/JCKqWjLBk8JEVMetX78e48aNQ0ZGBkxMTCSrIycnB66urliyZAkmTJggWR1E9AA7IxNRnbN27VrUr18frq6uOHfuHGbPno1hw4bVeMg5e/Ysrly5gqCgIGRkZGDhwoUAoHEaj4ikxaBDRHVOfHw85s2bh/j4eDg7O2Po0KFYtGiRJLV8/vnnuHr1KgwNDREQEIDDhw+Xu0MzEVU/nroiIiIircXOyERERKS1GHSIiIhIazHoEBERkdZi0CEiIiKtxaBDREREWotBh4iIiLQWgw4RERFpLQYdIiIi0loMOkRERKS1/h+XfJNytiwo1gAAAABJRU5ErkJggg==", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "plot_data_comparison2(\n", + " plot_title = \"Matching regex (a|b)* against accepting strings\", \n", + " data1 = regex_data,\n", + " data1_label = \"Regex\",\n", + " data2 = zipper_data,\n", + " data2_label = \"Zipper\",\n", + " x_label = \"String length\",\n", + " y_label = \"Time (us)\")" + ] + }, + { + "cell_type": "code", + "execution_count": 43, + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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nGBvb4/8/PwK2y6VZGxszJEhIqIqZOn+axAC6OqjvaNDgMSBaPr06ejevTvc3d2RkZGB9evXIzw8HLt374ZCocCIESMwZcoU1KhRA1ZWVhg/fjz8/f3RunVrAEDXrl3h4+ODd955B4sXL0ZCQgI++eQThISEqEd43nvvPSxbtgwffvghhg8fjv3792PDhg3YsWNHhffH0NCQX9ZERET/uZKYge1R2j13qJikgSgpKQlDhgzBvXv3oFAo4Ovri927d6NLly4Ais5xY2BggD59+iA3NxeBgYFYvny5+v6GhobYvn07xowZA39/f1SrVg3BwcGYN2+euo2Xlxd27NiByZMnY8mSJXB1dcWqVasQGBj40vtLRESkT5bsuwohgG4NHOHj/PTD3qWmdech0kblOY8BERERATEJGei25BCEAHZObAdvp5f//Vklz0NEREREumPJvisQAuje0FGSMFReDERERERUoS4nKPHP+aLzAU7U4iPLHsVARERERBVqyd6rAICgRk6o76j9o0MAAxERERFVoIvxSuyMToBMVnVGhwAGIiIiIqpAS/ZdAVA0OlTXwVLiasqOgYiIiIgqxIX4dOy+kFg0OtS56owOAQxEREREVEG++W/uUA9fZ9SpQqNDAAMRERERVYDou+kIu1g8OlRb6nLKjYGIiIiIXljx6FDPxs6obV+1RocABiIiIiJ6QefvpGPvpUQYyIDxnarW3KFiDERERET0Qr7ZW3RkWdHokIXE1TwfBiIiIiJ6budup2Hf5SQYyIAJVezIskcxEBEREdFzW7KvaO5QryYuqGlXNUeHAAYiIiIiek6Rt9Ow/3ISDA1kGF+FR4cABiIiIiJ6TsVzh3o1cYGXbTWJq3kxDERERERUbmfiHiA85n7R6FCnqnfeoccxEBEREVG5FZ936M2mLvCs4qNDAAMRERERldPpWw9w6IrujA4BDERERERUTsVzh/o0c4GHTdUfHQIYiIiIiKgcTt9KxeGryTAykFXZs1KXhoGIiIiIyuzrsKK5Q339XOFWw1ziaioOAxERERGVycmbqThyrWh0KKSjbswdKsZARERERGVSPHeoX3PdGh0CGIiIiIioDE7EpuLotRQYG+re6BDAQERERERl8HVY8eiQG1yr69boEMBARERERM/w740URNzQ3dEhgIGIiIiInqF4dKh/cze4WJtJXE3l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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "image/png": "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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "plot_data(\n", + " plot_title = \"Matching regex (a|b)* against accepting strings\", \n", + " data = regex_data,\n", + " data_label = \"Regex\",\n", + " x_label = \"String length\",\n", + " y_label = \"Time (us)\")\n", + "\n", + "plot_data(\n", + " plot_title = \"Matching regex (a|b)* against accepting strings using Zipper\", \n", + " data = zipper_data,\n", + " data_label = \"Zipper\",\n", + " x_label = \"String length\",\n", + " y_label = \"Time (us)\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": ".venv", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.13.0" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/benchmark/RegexBenchmark.scala b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/benchmark/RegexBenchmark.scala new file mode 100644 index 00000000..48a59d2c --- /dev/null +++ b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/benchmark/RegexBenchmark.scala @@ -0,0 +1,103 @@ +package benchmark + +import java.util.concurrent.TimeUnit +import org.openjdk.jmh.annotations.* +import scala.util.Random +import stainless.collection.{List => StainlessList} +import ch.epfl.lexer.VerifiedRegex.Regex +import ch.epfl.benchmark.RegexUtils.* +import scala.util.Random +import scala.compiletime.uninitialized +import ch.epfl.lexer.VerifiedRegexMatcher.matchZipper +import ch.epfl.lexer.VerifiedRegexMatcher.matchR +import ch.epfl.lexer.VerifiedRegexMatcher.matchRMem +import ch.epfl.lexer.MemoisationRegex +import ch.epfl.map.Hashable + +@State(Scope.Benchmark) +class RegexBenchmark { + + @Param( + Array( + "5", + "10", + "15", + "20", + "25", + "30", + "35", + "40", + "45", + "50", + "55", + "60", + "65", + "70", + "75", + "80", + // "85", + // "90", + // "95", + // "100" + ) + ) + var size: String = uninitialized + + @Benchmark + @BenchmarkMode(Array(Mode.AverageTime)) + @OutputTimeUnit(TimeUnit.MICROSECONDS) + def abStar_accepting_regex(): Unit = { + val r = RegexBenchmarkUtil.abStar + val s = RegexBenchmarkUtil.abStar_Accepting_strings(size.toInt) + matchR(r, s) + } + + @Benchmark + @BenchmarkMode(Array(Mode.AverageTime)) + @OutputTimeUnit(TimeUnit.MICROSECONDS) + def abStar_accepting_zipper(): Unit = { + val r = RegexBenchmarkUtil.abStar + val s = RegexBenchmarkUtil.abStar_Accepting_strings(size.toInt) + matchZipper(r, s) + } + + @Benchmark + @BenchmarkMode(Array(Mode.AverageTime)) + @OutputTimeUnit(TimeUnit.MICROSECONDS) + def abStar_accepting_regex_mem(): Unit = { + val r = RegexBenchmarkUtil.abStar + val s = RegexBenchmarkUtil.abStar_Accepting_strings(size.toInt) + matchRMem(r, s)(RegexBenchmarkUtil.regexCache) + } + + // @Benchmark + // @BenchmarkMode(Array(Mode.AverageTime)) + // @OutputTimeUnit(TimeUnit.MICROSECONDS) + // def abStar_rejecting_zipper(): Unit = { + // val z = focus(RegexBenchmarkUtil.abStar) + // } +} + +object RegexCharHashable extends Hashable[(Regex[Char], Char)] { + override def hash(k: (Regex[Char], Char)): Long = { + val (r, c) = k + r.hashCode() * 31 + c.hashCode() + } +} + +object RegexBenchmarkUtil { + val seed = 0x0ddba11 + val r = new Random(seed) + + val string_sizes = List(5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 65, 70, 75, 80, 85, 90, 95, 100, 150, 200) + val abStar: Regex[Char] = ("a" | "b").* + + val abStar_Accepting_strings: Map[Int, StainlessList[Char]] = string_sizes.map(n => (n, (1 to n).map(_ => random_a_or_b()).mkString.toStainless)).toMap + + val regexCache: MemoisationRegex.Cache[Char] = MemoisationRegex.empty(RegexCharHashable) + + + def random_a_or_b(): String = { + if (r.nextBoolean()) "a" else "b" + } +} \ No newline at end of file From b7179dc3d310e8e3ddb7b68250e5bc4b023f43cc Mon Sep 17 00:00:00 2001 From: Samuel Chassot Date: Wed, 27 Nov 2024 18:21:05 +0100 Subject: [PATCH 66/78] update CI --- .github/workflows/bolts-CI.yml | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/.github/workflows/bolts-CI.yml b/.github/workflows/bolts-CI.yml index faa2e7ed..9c4a1ccc 100644 --- a/.github/workflows/bolts-CI.yml +++ b/.github/workflows/bolts-CI.yml @@ -9,9 +9,10 @@ jobs: if: github.event.pull_request.draft == false runs-on: [self-hosted, linux] env: + JAVA_OPTS_TMP_DIR: ./tmp_java # define Java options for both official sbt and sbt-extras - JAVA_OPTS: -Dsbt.io.implicit.relative.glob.conversion=allow -Xss512M -Xms1024M -Xmx12G -XX:MaxMetaspaceSize=2G -XX:+UseCodeCacheFlushing -XX:ReservedCodeCacheSize=768M - JVM_OPTS: -Dsbt.io.implicit.relative.glob.conversion=allow -Xss512M -Xms1024M -Xmx12G -XX:MaxMetaspaceSize=2G -XX:+UseCodeCacheFlushing -XX:ReservedCodeCacheSize=768M + JAVA_OPTS: -Dsbt.io.implicit.relative.glob.conversion=allow -Xss512M -Xms1024M -Xmx12G -XX:MaxMetaspaceSize=2G -XX:+UseCodeCacheFlushing -XX:ReservedCodeCacheSize=768M -Djava.io.tmpdir=$JAVA_OPTS_TMP_DIR + JVM_OPTS: -Dsbt.io.implicit.relative.glob.conversion=allow -Xss512M -Xms1024M -Xmx12G -XX:MaxMetaspaceSize=2G -XX:+UseCodeCacheFlushing -XX:ReservedCodeCacheSize=768M -Djava.io.tmpdir=$JAVA_OPTS_TMP_DIR steps: - name: Checkout uses: actions/checkout@v4 @@ -22,6 +23,8 @@ jobs: with: distribution: temurin java-version: 17 + - name: Create temp dir + run: rm -rf $JAVA_OPTS_TMP_DIR && mkdir -p $JAVA_OPTS_TMP_DIR - name: Install stainless and solvers run: ./install_stainless_and_solvers.sh $GITHUB_WORKSPACE/.local/bin $GITHUB_WORKSPACE/.local - name: Add stainless to PATH @@ -34,6 +37,8 @@ jobs: run: cvc5 --version && z3 --version && cvc4 --version - name: Bolts Tests run: ./run-tests.sh --admit-vcs + - name: Clean up + run: rm -rf $JAVA_OPTS_TMP_DIR fail_if_pull_request_is_draft: if: github.event.pull_request.draft == true runs-on: [self-hosted, linux] From 52697c22261f898a23050f0b5ddf0b82411c078d Mon Sep 17 00:00:00 2001 From: Samuel Chassot Date: Thu, 28 Nov 2024 09:56:47 +0100 Subject: [PATCH 67/78] debug ci --- run-one-test.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/run-one-test.sh b/run-one-test.sh index 7445fa7d..09569d52 100755 --- a/run-one-test.sh +++ b/run-one-test.sh @@ -30,9 +30,9 @@ function run_tests { cat "./verify.sh" echo "" if [ "$ADMIT_VCS" = true ]; then - bash "./verify.sh" "--compact" "--admit-vcs=true" + bash "./verify.sh" "--compact" "--admit-vcs=true" "--debug=stack" else - bash "./verify.sh" "--compact" + bash "./verify.sh" "--compact" "--debug=stack" fi status=$? cd - From c62c253470c36ac4364fa8efef80ff9a1b58c9ed Mon Sep 17 00:00:00 2001 From: Samuel Chassot Date: Thu, 28 Nov 2024 10:06:17 +0100 Subject: [PATCH 68/78] debug ci --- run-one-test.sh | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/run-one-test.sh b/run-one-test.sh index 09569d52..c9a4b951 100755 --- a/run-one-test.sh +++ b/run-one-test.sh @@ -30,9 +30,9 @@ function run_tests { cat "./verify.sh" echo "" if [ "$ADMIT_VCS" = true ]; then - bash "./verify.sh" "--compact" "--admit-vcs=true" "--debug=stack" + bash "./verify.sh" "--compact" "--admit-vcs=true" else - bash "./verify.sh" "--compact" "--debug=stack" + bash "./verify.sh" "--compact" fi status=$? cd - @@ -49,6 +49,7 @@ function run_tests { if [ $ADMIT_VCS = true ]; then if [ $status -eq 0 ] || [ $status -eq 1 ]; then + cat $project/stainless-stack-trace.txt echo "Stainless accepted project: $project." exit 0 else From ece5065738cd834670686fbe647ecab34646a1c0 Mon Sep 17 00:00:00 2001 From: Samuel Chassot Date: Thu, 28 Nov 2024 10:10:24 +0100 Subject: [PATCH 69/78] debug --- run-one-test.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/run-one-test.sh b/run-one-test.sh index c9a4b951..274372fc 100755 --- a/run-one-test.sh +++ b/run-one-test.sh @@ -49,7 +49,7 @@ function run_tests { if [ $ADMIT_VCS = true ]; then if [ $status -eq 0 ] || [ $status -eq 1 ]; then - cat $project/stainless-stack-trace.txt + cat stainless-stack-trace.txt echo "Stainless accepted project: $project." exit 0 else From b280697778ad0f2e71eee15a30e9ceaa04347d4d Mon Sep 17 00:00:00 2001 From: Samuel Chassot Date: Thu, 28 Nov 2024 10:22:31 +0100 Subject: [PATCH 70/78] ci --- run-tests.sh | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/run-tests.sh b/run-tests.sh index d656aff5..b4a1f428 100755 --- a/run-tests.sh +++ b/run-tests.sh @@ -1,5 +1,10 @@ #!/usr/bin/env bash +SBT_TEMP=$ROOT_DIR/sbt-temp +# if TEMP_DIR is not set, set it to the default +JAVA_OPTS_TMP_DIR=${JAVA_OPTS_TMP_DIR:-$ROOT_DIR/temporary} +mkdir -p $JAVA_OPTS_TMP_DIR + STAINLESS="stainless-dotty" ADMIT_VCS=false # First check whether the flag --admit-vcs is present @@ -26,4 +31,7 @@ for project in $TC_TESTS; do exit $status fi done + +rm -rf $SBT_TEMP +rm -rf $JAVA_OPTS_TMP_DIR echo "************* Verifying bolts projects was successful! *************" \ No newline at end of file From 030be7f98396b7c9436d6d13a5509f6469e6278a Mon Sep 17 00:00:00 2001 From: Samuel Chassot Date: Thu, 28 Nov 2024 12:40:57 +0100 Subject: [PATCH 71/78] add memoised matching using Zipper to regex --- .../ch/epfl/benchmark/RegexBenchmark.scala | 34 +++++++++++++++++-- .../scala/ch/epfl/lexer/VerifiedRegex.scala | 12 +++++++ 2 files changed, 43 insertions(+), 3 deletions(-) diff --git a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/benchmark/RegexBenchmark.scala b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/benchmark/RegexBenchmark.scala index 48a59d2c..f62587fe 100644 --- a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/benchmark/RegexBenchmark.scala +++ b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/benchmark/RegexBenchmark.scala @@ -49,7 +49,8 @@ class RegexBenchmark { def abStar_accepting_regex(): Unit = { val r = RegexBenchmarkUtil.abStar val s = RegexBenchmarkUtil.abStar_Accepting_strings(size.toInt) - matchR(r, s) + val res = matchR(r, s) + assert(res) } @Benchmark @@ -58,7 +59,18 @@ class RegexBenchmark { def abStar_accepting_zipper(): Unit = { val r = RegexBenchmarkUtil.abStar val s = RegexBenchmarkUtil.abStar_Accepting_strings(size.toInt) - matchZipper(r, s) + val res = matchZipper(r, s) + assert(res) + } + + @Benchmark + @BenchmarkMode(Array(Mode.AverageTime)) + @OutputTimeUnit(TimeUnit.MICROSECONDS) + def abStar_accepting_zipper_mem(): Unit = { + val r = RegexBenchmarkUtil.abStar + val s = RegexBenchmarkUtil.abStar_Accepting_strings(size.toInt) + val res = matchZipper(r, s) + assert(res) } @Benchmark @@ -67,7 +79,8 @@ class RegexBenchmark { def abStar_accepting_regex_mem(): Unit = { val r = RegexBenchmarkUtil.abStar val s = RegexBenchmarkUtil.abStar_Accepting_strings(size.toInt) - matchRMem(r, s)(RegexBenchmarkUtil.regexCache) + val res = matchRMem(r, s)(RegexBenchmarkUtil.regexCache) + assert(res) } // @Benchmark @@ -96,8 +109,23 @@ object RegexBenchmarkUtil { val regexCache: MemoisationRegex.Cache[Char] = MemoisationRegex.empty(RegexCharHashable) + val possibleEmailChars = "abcdedfghijklmnopqrstuvwxyz." + val emailRegex = possibleEmailChars.anyOf.+ ~ "@".r ~ possibleEmailChars.anyOf.+ ~ ".".r ~ possibleEmailChars.anyOf.+ + val email_Accepting_strings: Map[Int, StainlessList[Char]] = string_sizes.map(n => (n, random_email_strings(n))).toMap def random_a_or_b(): String = { if (r.nextBoolean()) "a" else "b" } + + def random_email_char(): String = { + val index = r.nextInt(possibleEmailChars.length) + possibleEmailChars(index).toString + } + + def random_email_strings(n: Int): String = { + val firstPartSize: Int = r.nextInt(n - 2) + val secondPartSize: Int = n - firstPartSize - 2 + val res: String = (1.to(firstPartSize)).map(_ => random_email_char()).mkString + "@" + (1.to(secondPartSize)).map(_ => random_email_char()).mkString + "." + (1 to 3).map(_ => random_email_char()).mkString + assert(res.size == n) + } } \ No newline at end of file diff --git a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedRegex.scala b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedRegex.scala index 3b8d739a..83f380e5 100644 --- a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedRegex.scala +++ b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedRegex.scala @@ -614,6 +614,11 @@ object ZipperRegex { z.flatMap(derivUpMem) // rejected by stainless because of effects in the lambda's body }.ensuring(res => res == derivationStepZipper(z, a)) + def matchZipperMem[C](z: Zipper[C], input: List[C])(implicit cacheUp: CacheUp[C], cacheDown: CacheDown[C]): Boolean = { + decreases(input.size) + if (input.isEmpty) nullableZipper(z) else matchZipperMem(derivationStepZipperMem(z, input.head), input.tail) + }.ensuring(res => res == matchZipper(z, input)) + // PROOFS ----------------------------------------------------------------------------------------------------- @@ -2570,6 +2575,13 @@ object VerifiedRegexMatcher { ZipperRegex.matchZipper(ZipperRegex.focus(r), input) }.ensuring (res => res == matchR(r, input)) + def matchZippeMem[C](r: Regex[C], input: List[C])(implicit cacheUp: MemoisationZipper.CacheUp[C], cacheDown: MemoisationZipper.CacheDown[C]): Boolean = { + require(validRegex(r)) + decreases(input.size) + ghostExpr(ZipperRegex.theoremZipperRegexEquiv(ZipperRegex.focus(r), ZipperRegex.focus(r).toList, r, input)) + ZipperRegex.matchZipperMem(ZipperRegex.focus(r), input) + }.ensuring (res => res == matchR(r, input)) + // COMMENTED OUT BECAUSE NOT VERIFIED THROUGHOUT YET // def matchRMemSimp[C](r: Regex[C], input: List[C])(implicit cache: Cache[C]): Boolean = { // require(validRegex(r)) From 04065e85834739650db8ff3b7e6ae60566b97c0e Mon Sep 17 00:00:00 2001 From: Samuel Chassot Date: Thu, 28 Nov 2024 13:07:30 +0100 Subject: [PATCH 72/78] benchmark + typo --- .../ch/epfl/benchmark/RegexBenchmark.scala | 94 +++++++++++++++---- .../scala/ch/epfl/lexer/VerifiedRegex.scala | 6 +- 2 files changed, 82 insertions(+), 18 deletions(-) diff --git a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/benchmark/RegexBenchmark.scala b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/benchmark/RegexBenchmark.scala index f62587fe..060991d1 100644 --- a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/benchmark/RegexBenchmark.scala +++ b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/benchmark/RegexBenchmark.scala @@ -11,7 +11,10 @@ import scala.compiletime.uninitialized import ch.epfl.lexer.VerifiedRegexMatcher.matchZipper import ch.epfl.lexer.VerifiedRegexMatcher.matchR import ch.epfl.lexer.VerifiedRegexMatcher.matchRMem +import ch.epfl.lexer.VerifiedRegexMatcher.matchZipperMem import ch.epfl.lexer.MemoisationRegex +import ch.epfl.lexer.MemoisationZipper +import ch.epfl.lexer.ZipperRegex.Context import ch.epfl.map.Hashable @State(Scope.Benchmark) @@ -35,10 +38,10 @@ class RegexBenchmark { "70", "75", "80", - // "85", - // "90", - // "95", - // "100" + "85", + "90", + "95", + "100" ) ) var size: String = uninitialized @@ -66,29 +69,66 @@ class RegexBenchmark { @Benchmark @BenchmarkMode(Array(Mode.AverageTime)) @OutputTimeUnit(TimeUnit.MICROSECONDS) - def abStar_accepting_zipper_mem(): Unit = { + def abStar_accepting_regex_mem(): Unit = { val r = RegexBenchmarkUtil.abStar val s = RegexBenchmarkUtil.abStar_Accepting_strings(size.toInt) - val res = matchZipper(r, s) + val res = matchRMem(r, s)(RegexBenchmarkUtil.regexCache) assert(res) } @Benchmark @BenchmarkMode(Array(Mode.AverageTime)) @OutputTimeUnit(TimeUnit.MICROSECONDS) - def abStar_accepting_regex_mem(): Unit = { + def abStar_accepting_zipper_mem(): Unit = { val r = RegexBenchmarkUtil.abStar val s = RegexBenchmarkUtil.abStar_Accepting_strings(size.toInt) + val res = matchZipperMem(r, s)(RegexBenchmarkUtil.zipperCacheUp, RegexBenchmarkUtil.zipperCacheDown) + assert(res) + } + + // Email accepting regex ----------------------------------------------------------------------------------------------------------------------- + + @Benchmark + @BenchmarkMode(Array(Mode.AverageTime)) + @OutputTimeUnit(TimeUnit.MICROSECONDS) + def email_accepting_regex(): Unit = { + val r = RegexBenchmarkUtil.emailRegex + val s = RegexBenchmarkUtil.email_Accepting_strings(size.toInt) + val res = matchR(r, s) + assert(res) + } + + @Benchmark + @BenchmarkMode(Array(Mode.AverageTime)) + @OutputTimeUnit(TimeUnit.MICROSECONDS) + def email_accepting_zipper(): Unit = { + val r = RegexBenchmarkUtil.emailRegex + val s = RegexBenchmarkUtil.email_Accepting_strings(size.toInt) + val res = matchZipper(r, s) + assert(res) + } + + @Benchmark + @BenchmarkMode(Array(Mode.AverageTime)) + @OutputTimeUnit(TimeUnit.MICROSECONDS) + def email_accepting_zipper_mem(): Unit = { + val r = RegexBenchmarkUtil.emailRegex + val s = RegexBenchmarkUtil.email_Accepting_strings(size.toInt) + val res = matchZipper(r, s) + assert(res) + } + + @Benchmark + @BenchmarkMode(Array(Mode.AverageTime)) + @OutputTimeUnit(TimeUnit.MICROSECONDS) + def email_accepting_regex_mem(): Unit = { + val r = RegexBenchmarkUtil.emailRegex + val s = RegexBenchmarkUtil.email_Accepting_strings(size.toInt) val res = matchRMem(r, s)(RegexBenchmarkUtil.regexCache) assert(res) } - // @Benchmark - // @BenchmarkMode(Array(Mode.AverageTime)) - // @OutputTimeUnit(TimeUnit.MICROSECONDS) - // def abStar_rejecting_zipper(): Unit = { - // val z = focus(RegexBenchmarkUtil.abStar) - // } + } object RegexCharHashable extends Hashable[(Regex[Char], Char)] { @@ -98,6 +138,20 @@ object RegexCharHashable extends Hashable[(Regex[Char], Char)] { } } +object ContextCharHashable extends Hashable[(Context[Char], Char)] { + override def hash(k: (Context[Char], Char)): Long = { + val (ctx, c) = k + ctx.hashCode() * 31 + c.hashCode() + } +} + +object RegexContextCharHashable extends Hashable[(Regex[Char], Context[Char], Char)] { + override def hash(k: (Regex[Char], Context[Char], Char)): Long = { + val (r, ctx, c) = k + r.hashCode() * 63 + ctx.hashCode() * 31 + c.hashCode() + } +} + object RegexBenchmarkUtil { val seed = 0x0ddba11 val r = new Random(seed) @@ -108,11 +162,13 @@ object RegexBenchmarkUtil { val abStar_Accepting_strings: Map[Int, StainlessList[Char]] = string_sizes.map(n => (n, (1 to n).map(_ => random_a_or_b()).mkString.toStainless)).toMap val regexCache: MemoisationRegex.Cache[Char] = MemoisationRegex.empty(RegexCharHashable) + val zipperCacheUp: MemoisationZipper.CacheUp[Char] = MemoisationZipper.emptyUp(ContextCharHashable) + val zipperCacheDown: MemoisationZipper.CacheDown[Char] = MemoisationZipper.emptyDown(RegexContextCharHashable) val possibleEmailChars = "abcdedfghijklmnopqrstuvwxyz." val emailRegex = possibleEmailChars.anyOf.+ ~ "@".r ~ possibleEmailChars.anyOf.+ ~ ".".r ~ possibleEmailChars.anyOf.+ - val email_Accepting_strings: Map[Int, StainlessList[Char]] = string_sizes.map(n => (n, random_email_strings(n))).toMap + val email_Accepting_strings: Map[Int, StainlessList[Char]] = string_sizes.map(n => (n, random_email_strings(n).toStainless)).toMap def random_a_or_b(): String = { if (r.nextBoolean()) "a" else "b" } @@ -123,9 +179,13 @@ object RegexBenchmarkUtil { } def random_email_strings(n: Int): String = { - val firstPartSize: Int = r.nextInt(n - 2) - val secondPartSize: Int = n - firstPartSize - 2 - val res: String = (1.to(firstPartSize)).map(_ => random_email_char()).mkString + "@" + (1.to(secondPartSize)).map(_ => random_email_char()).mkString + "." + (1 to 3).map(_ => random_email_char()).mkString + val usableLength = n - 2 + val firstPartSize: Int = r.between(1, usableLength - 2 + 1) + val secondPartSize: Int = r.between(1, usableLength - firstPartSize - 1 + 1) + val thirdPartSize: Int = usableLength - firstPartSize - secondPartSize + val res: String = (1.to(firstPartSize)).map(_ => random_email_char()).mkString + "@" + (1.to(secondPartSize)).map(_ => random_email_char()).mkString + "." + (1.to(thirdPartSize)).map(_ => random_email_char()).mkString + // println(s"n = $n, usable length = $usableLength, first part size = $firstPartSize, second part size = $secondPartSize, res = $res") assert(res.size == n) + res } } \ No newline at end of file diff --git a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedRegex.scala b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedRegex.scala index 83f380e5..c5f8e1cf 100644 --- a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedRegex.scala +++ b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedRegex.scala @@ -123,6 +123,10 @@ object MemoisationZipper { ) } + def emptyUp[C](hashF: Hashable[(Context[C], C)]): CacheUp[C] = CacheUp(MutableHashMap.getEmptyHashMap[(Context[C], C), Zipper[C]](k => Set[Context[C]](), hashF)) + def emptyDown[C](hashF: Hashable[(Regex[C], Context[C], C)]): CacheDown[C] = CacheDown(MutableHashMap.getEmptyHashMap[(Regex[C], Context[C], C), Zipper[C]](k => Set[Context[C]](), hashF)) + + @mutable final case class CacheUp[C](private val cache: HashMap[(Context[C], C), Zipper[C]]) { require(validCacheMapUp(cache)) @@ -2575,7 +2579,7 @@ object VerifiedRegexMatcher { ZipperRegex.matchZipper(ZipperRegex.focus(r), input) }.ensuring (res => res == matchR(r, input)) - def matchZippeMem[C](r: Regex[C], input: List[C])(implicit cacheUp: MemoisationZipper.CacheUp[C], cacheDown: MemoisationZipper.CacheDown[C]): Boolean = { + def matchZipperMem[C](r: Regex[C], input: List[C])(implicit cacheUp: MemoisationZipper.CacheUp[C], cacheDown: MemoisationZipper.CacheDown[C]): Boolean = { require(validRegex(r)) decreases(input.size) ghostExpr(ZipperRegex.theoremZipperRegexEquiv(ZipperRegex.focus(r), ZipperRegex.focus(r).toList, r, input)) From 39850f7e08deed2371ee14a9db8d61e6bad5be8a Mon Sep 17 00:00:00 2001 From: Samuel Chassot Date: Fri, 29 Nov 2024 09:09:21 +0100 Subject: [PATCH 73/78] rename for future benchmark analysis --- .../scala/ch/epfl/benchmark/RegexBenchmark.scala | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/benchmark/RegexBenchmark.scala b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/benchmark/RegexBenchmark.scala index 060991d1..df7d339c 100644 --- a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/benchmark/RegexBenchmark.scala +++ b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/benchmark/RegexBenchmark.scala @@ -49,7 +49,7 @@ class RegexBenchmark { @Benchmark @BenchmarkMode(Array(Mode.AverageTime)) @OutputTimeUnit(TimeUnit.MICROSECONDS) - def abStar_accepting_regex(): Unit = { + def abStarAccepting_Regex(): Unit = { val r = RegexBenchmarkUtil.abStar val s = RegexBenchmarkUtil.abStar_Accepting_strings(size.toInt) val res = matchR(r, s) @@ -59,7 +59,7 @@ class RegexBenchmark { @Benchmark @BenchmarkMode(Array(Mode.AverageTime)) @OutputTimeUnit(TimeUnit.MICROSECONDS) - def abStar_accepting_zipper(): Unit = { + def abStarAccepting_Zipper(): Unit = { val r = RegexBenchmarkUtil.abStar val s = RegexBenchmarkUtil.abStar_Accepting_strings(size.toInt) val res = matchZipper(r, s) @@ -69,7 +69,7 @@ class RegexBenchmark { @Benchmark @BenchmarkMode(Array(Mode.AverageTime)) @OutputTimeUnit(TimeUnit.MICROSECONDS) - def abStar_accepting_regex_mem(): Unit = { + def abStarAccepting_RegexMem(): Unit = { val r = RegexBenchmarkUtil.abStar val s = RegexBenchmarkUtil.abStar_Accepting_strings(size.toInt) val res = matchRMem(r, s)(RegexBenchmarkUtil.regexCache) @@ -79,7 +79,7 @@ class RegexBenchmark { @Benchmark @BenchmarkMode(Array(Mode.AverageTime)) @OutputTimeUnit(TimeUnit.MICROSECONDS) - def abStar_accepting_zipper_mem(): Unit = { + def abStarAccepting_ZipperMem(): Unit = { val r = RegexBenchmarkUtil.abStar val s = RegexBenchmarkUtil.abStar_Accepting_strings(size.toInt) val res = matchZipperMem(r, s)(RegexBenchmarkUtil.zipperCacheUp, RegexBenchmarkUtil.zipperCacheDown) @@ -91,7 +91,7 @@ class RegexBenchmark { @Benchmark @BenchmarkMode(Array(Mode.AverageTime)) @OutputTimeUnit(TimeUnit.MICROSECONDS) - def email_accepting_regex(): Unit = { + def emailAccepting_Regex(): Unit = { val r = RegexBenchmarkUtil.emailRegex val s = RegexBenchmarkUtil.email_Accepting_strings(size.toInt) val res = matchR(r, s) @@ -101,7 +101,7 @@ class RegexBenchmark { @Benchmark @BenchmarkMode(Array(Mode.AverageTime)) @OutputTimeUnit(TimeUnit.MICROSECONDS) - def email_accepting_zipper(): Unit = { + def emailAccepting_Zipper(): Unit = { val r = RegexBenchmarkUtil.emailRegex val s = RegexBenchmarkUtil.email_Accepting_strings(size.toInt) val res = matchZipper(r, s) @@ -111,7 +111,7 @@ class RegexBenchmark { @Benchmark @BenchmarkMode(Array(Mode.AverageTime)) @OutputTimeUnit(TimeUnit.MICROSECONDS) - def email_accepting_zipper_mem(): Unit = { + def emailAccepting_ZipperMem(): Unit = { val r = RegexBenchmarkUtil.emailRegex val s = RegexBenchmarkUtil.email_Accepting_strings(size.toInt) val res = matchZipper(r, s) @@ -121,7 +121,7 @@ class RegexBenchmark { @Benchmark @BenchmarkMode(Array(Mode.AverageTime)) @OutputTimeUnit(TimeUnit.MICROSECONDS) - def email_accepting_regex_mem(): Unit = { + def emailAccepting_RegexMem(): Unit = { val r = RegexBenchmarkUtil.emailRegex val s = RegexBenchmarkUtil.email_Accepting_strings(size.toInt) val res = matchRMem(r, s)(RegexBenchmarkUtil.regexCache) From 822071f8b2d198e6c96193368d9fcec5a140f92b Mon Sep 17 00:00:00 2001 From: Samuel Chassot Date: Fri, 29 Nov 2024 09:09:34 +0100 Subject: [PATCH 74/78] benchmark data --- ...enchmark_wi5_i5_regex_zipper_laraquad3.txt | 4856 +++++++++++++++++ 1 file changed, 4856 insertions(+) create mode 100644 lexers/regex/verifiedlexer/benchmark_wi5_i5_regex_zipper_laraquad3.txt diff --git a/lexers/regex/verifiedlexer/benchmark_wi5_i5_regex_zipper_laraquad3.txt b/lexers/regex/verifiedlexer/benchmark_wi5_i5_regex_zipper_laraquad3.txt new file mode 100644 index 00000000..63ef5f4c --- /dev/null +++ b/lexers/regex/verifiedlexer/benchmark_wi5_i5_regex_zipper_laraquad3.txt @@ -0,0 +1,4856 @@ +[info] welcome to sbt 1.9.8 (Eclipse Adoptium Java 17.0.11) +[info] loading settings for project verifiedlexer-build-build-build from metals.sbt ... +[info] loading project definition from /localhome/chassot/bolts/lexers/regex/verifiedlexer/project/project/project +[info] loading settings for project verifiedlexer-build-build from metals.sbt ... +[info] loading project definition from /localhome/chassot/bolts/lexers/regex/verifiedlexer/project/project +[success] Generated .bloop/verifiedlexer-build-build.json +[success] Total time: 3 s, completed Nov 28, 2024, 1:10:17 PM +[info] loading settings for project verifiedlexer-build from metals.sbt,plugins.sbt ... +[info] loading project definition from /localhome/chassot/bolts/lexers/regex/verifiedlexer/project +[success] Generated .bloop/verifiedlexer-build.json +[success] Total time: 2 s, completed Nov 28, 2024, 1:10:19 PM +[info] loading settings for project verifiedlexer from build.sbt ... +[info] set current project to VerifiedLexer (in build file:/localhome/chassot/bolts/lexers/regex/verifiedlexer/) +[info] compiling 58 Scala sources to /localhome/chassot/bolts/lexers/regex/verifiedlexer/target/scala-3.5.2/classes ... +[warn] -- [E029] Pattern Match Exhaustivity Warning: /localhome/chassot/bolts/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/Utils.scala:522:23 +[warn] 522 | val res: List[B] = l match { +[warn] | ^ +[warn] | match may not be exhaustive. +[warn] | +[warn] | It would fail on pattern case: stainless.collection.Nil() +[warn] | +[warn] | longer explanation available when compiling with `-explain` +[warn] -- [E029] Pattern Match Exhaustivity Warning: /localhome/chassot/bolts/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/Utils.scala:550:4 +[warn] 550 | l match { +[warn] | ^ +[warn] | match may not be exhaustive. +[warn] | +[warn] | It would fail on pattern case: stainless.collection.Cons(_, _) +[warn] | +[warn] | longer explanation available when compiling with `-explain` +[warn] -- [E029] Pattern Match Exhaustivity Warning: /localhome/chassot/bolts/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/Utils.scala:1035:4 +[warn] 1035 | newList match { +[warn] | ^^^^^^^ +[warn] | match may not be exhaustive. +[warn] | +[warn] | It would fail on pattern case: stainless.collection.Cons(_, _) +[warn] | +[warn] | longer explanation available when compiling with `-explain` +[warn] -- [E029] Pattern Match Exhaustivity Warning: /localhome/chassot/bolts/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedLexer.scala:223:10 +[warn] 223 | maxPrefWithoutSep match { +[warn] | ^^^^^^^^^^^^^^^^^ +[warn] | match may not be exhaustive. +[warn] | +[warn] | It would fail on pattern case: stainless.lang.Some(_) +[warn] | +[warn] | longer explanation available when compiling with `-explain` +[warn] -- [E029] Pattern Match Exhaustivity Warning: /localhome/chassot/bolts/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedLexer.scala:257:10 +[warn] 257 | (currentRulePref, othersPrefix) match { +[warn] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +[warn] |match may not be exhaustive. +[warn] | +[warn] |It would fail on pattern case: (stainless.lang.Some(_), stainless.lang.Some(_)) +[warn] | +[warn] | longer explanation available when compiling with `-explain` +[warn] -- [E029] Pattern Match Exhaustivity Warning: /localhome/chassot/bolts/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedLexer.scala:252:45 +[warn] 252 | val ret: Option[(Token[C], List[C])] = rulesArg match { +[warn] | ^^^^^^^^ +[warn] | match may not be exhaustive. +[warn] | +[warn] | It would fail on pattern case: stainless.collection.Nil() +[warn] | +[warn] | longer explanation available when compiling with `-explain` +[warn] -- [E029] Pattern Match Exhaustivity Warning: /localhome/chassot/bolts/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedLexer.scala:611:6 +[warn] 611 | rules match { +[warn] | ^^^^^ +[warn] | match may not be exhaustive. +[warn] | +[warn] | It would fail on pattern case: stainless.collection.Cons(_, _) +[warn] | +[warn] | longer explanation available when compiling with `-explain` +[warn] -- [E029] Pattern Match Exhaustivity Warning: /localhome/chassot/bolts/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedRegex.scala:332:4 +[warn] 332 | r match { +[warn] | ^ +[warn] | match may not be exhaustive. +[warn] | +[warn] | It would fail on pattern case: Concat(_, _) +[warn] | +[warn] | longer explanation available when compiling with `-explain` +[warn] -- [E029] Pattern Match Exhaustivity Warning: /localhome/chassot/bolts/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/map/ListLongMap.scala:157:4 +[warn] 157 | l match { +[warn] | ^ +[warn] | match may not be exhaustive. +[warn] | +[warn] | It would fail on pattern case: stainless.collection.Cons(_, _) +[warn] | +[warn] | longer explanation available when compiling with `-explain` +[warn] -- [E029] Pattern Match Exhaustivity Warning: /localhome/chassot/bolts/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/map/ListLongMap.scala:191:4 +[warn] 191 | l match { +[warn] | ^ +[warn] | match may not be exhaustive. +[warn] | +[warn] | It would fail on pattern case: stainless.collection.Cons(_, _) +[warn] | +[warn] | longer explanation available when compiling with `-explain` +[warn] -- [E029] Pattern Match Exhaustivity Warning: /localhome/chassot/bolts/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/map/ListLongMap.scala:206:4 +[warn] 206 | l match { +[warn] | ^ +[warn] | match may not be exhaustive. +[warn] | +[warn] | It would fail on pattern case: stainless.collection.Cons(_, _) +[warn] | +[warn] | longer explanation available when compiling with `-explain` +[warn] -- [E029] Pattern Match Exhaustivity Warning: /localhome/chassot/bolts/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/map/ListLongMap.scala:222:4 +[warn] 222 | l match { +[warn] | ^ +[warn] | match may not be exhaustive. +[warn] | +[warn] | It would fail on pattern case: stainless.collection.Cons(_, _) +[warn] | +[warn] | longer explanation available when compiling with `-explain` +[warn] -- [E029] Pattern Match Exhaustivity Warning: /localhome/chassot/bolts/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/map/ListLongMap.scala:243:4 +[warn] 243 | l match { +[warn] | ^ +[warn] | match may not be exhaustive. +[warn] | +[warn] | It would fail on pattern case: stainless.collection.Cons(_, _) +[warn] | +[warn] | longer explanation available when compiling with `-explain` +[warn] -- [E029] Pattern Match Exhaustivity Warning: /localhome/chassot/bolts/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/map/ListLongMap.scala:274:4 +[warn] 274 | l match { +[warn] | ^ +[warn] | match may not be exhaustive. +[warn] | +[warn] | It would fail on pattern case: stainless.collection.Cons(_, _) +[warn] | +[warn] | longer explanation available when compiling with `-explain` +[warn] -- [E029] Pattern Match Exhaustivity Warning: /localhome/chassot/bolts/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/map/ListMap.scala:195:4 +[warn] 195 | l match { +[warn] | ^ +[warn] | match may not be exhaustive. +[warn] | +[warn] | It would fail on pattern case: stainless.collection.Cons(_, _) +[warn] | +[warn] | longer explanation available when compiling with `-explain` +[warn] -- [E029] Pattern Match Exhaustivity Warning: /localhome/chassot/bolts/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/map/ListMap.scala:229:4 +[warn] 229 | l match { +[warn] | ^ +[warn] | match may not be exhaustive. +[warn] | +[warn] | It would fail on pattern case: stainless.collection.Cons(_, _) +[warn] | +[warn] | longer explanation available when compiling with `-explain` +[warn] -- [E029] Pattern Match Exhaustivity Warning: /localhome/chassot/bolts/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/map/ListMap.scala:256:4 +[warn] 256 | l match { +[warn] | ^ +[warn] | match may not be exhaustive. +[warn] | +[warn] | It would fail on pattern case: stainless.collection.Cons(_, _) +[warn] | +[warn] | longer explanation available when compiling with `-explain` +[warn] -- [E029] Pattern Match Exhaustivity Warning: /localhome/chassot/bolts/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/map/ListMap.scala:308:4 +[warn] 308 | l match { +[warn] | ^ +[warn] | match may not be exhaustive. +[warn] | +[warn] | It would fail on pattern case: stainless.collection.Cons(_, _) +[warn] | +[warn] | longer explanation available when compiling with `-explain` +[warn] -- [E029] Pattern Match Exhaustivity Warning: /localhome/chassot/bolts/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/map/ListMap.scala:380:4 +[warn] 380 | l match { +[warn] | ^ +[warn] | match may not be exhaustive. +[warn] | +[warn] | It would fail on pattern case: stainless.collection.Cons(_, _) +[warn] | +[warn] | longer explanation available when compiling with `-explain` +[warn] -- [E029] Pattern Match Exhaustivity Warning: /localhome/chassot/bolts/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/map/MutableLongMap.scala:347:4 +[warn] 347 | c match { +[warn] | ^ +[warn] | match may not be exhaustive. +[warn] | +[warn] | It would fail on pattern case: EmptyCell() +[warn] | +[warn] | longer explanation available when compiling with `-explain` +[warn] -- [E029] Pattern Match Exhaustivity Warning: /localhome/chassot/bolts/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/map/MutableLongMap.scala:596:8 +[warn] 596 | seekEntryRes match { +[warn] | ^^^^^^^^^^^^ +[warn] |match may not be exhaustive. +[warn] | +[warn] |It would fail on pattern case: ch.epfl.map.MutableLongMap.Intermediate(_, _, _) +[warn] | +[warn] | longer explanation available when compiling with `-explain` +[warn] -- [E029] Pattern Match Exhaustivity Warning: /localhome/chassot/bolts/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/map/MutableLongMap.scala:1106:6 +[warn] 1106 | intermediate match { +[warn] | ^^^^^^^^^^^^ +[warn] |match may not be exhaustive. +[warn] | +[warn] |It would fail on pattern case: _: ch.epfl.map.MutableLongMap.SeekEntryResult +[warn] | +[warn] | longer explanation available when compiling with `-explain` +[warn] -- [E029] Pattern Match Exhaustivity Warning: /localhome/chassot/bolts/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/map/MutableLongMap.scala:1156:6 +[warn] 1156 | intermediate match { +[warn] | ^^^^^^^^^^^^ +[warn] |match may not be exhaustive. +[warn] | +[warn] |It would fail on pattern case: _: ch.epfl.map.MutableLongMap.SeekEntryResult +[warn] | +[warn] | longer explanation available when compiling with `-explain` +[warn] 23 warnings found +[info] done compiling +[info] compiling 44 Scala sources to /localhome/chassot/bolts/lexers/regex/verifiedlexer/target/scala-3.5.2/test-classes ... +[info] running org.openjdk.jmh.generators.bytecode.JmhBytecodeGenerator /localhome/chassot/bolts/lexers/regex/verifiedlexer/target/scala-3.5.2/classes /localhome/chassot/bolts/lexers/regex/verifiedlexer/target/scala-3.5.2/src_managed/jmh /localhome/chassot/bolts/lexers/regex/verifiedlexer/target/scala-3.5.2/resource_managed/jmh default +Processing 847 classes from /localhome/chassot/bolts/lexers/regex/verifiedlexer/target/scala-3.5.2/classes with "reflection" generator +Writing out Java source to /localhome/chassot/bolts/lexers/regex/verifiedlexer/target/scala-3.5.2/src_managed/jmh and resources to /localhome/chassot/bolts/lexers/regex/verifiedlexer/target/scala-3.5.2/resource_managed/jmh +[info] compiling 12 Java sources to /localhome/chassot/bolts/lexers/regex/verifiedlexer/target/scala-3.5.2/classes ... +[info] done compiling +[info] done compiling +[info] running (fork) org.openjdk.jmh.Main -i 5 -wi 5 -f1 -t1 +[info] # JMH version: 1.32 +[info] # VM version: JDK 17.0.11, OpenJDK 64-Bit Server VM, 17.0.11+9 +[info] # VM invoker: /usr/lib/jvm/temurin-17-jdk-amd64/bin/java +[info] # VM options: +[info] # Blackhole mode: full + dont-inline hint +[info] # Warmup: 5 iterations, 10 s each +[info] # Measurement: 5 iterations, 10 s each +[info] # Timeout: 10 min per iteration +[info] # Threads: 1 thread, will synchronize iterations +[info] # Benchmark mode: Average time, time/op +[info] # Benchmark: benchmark.RegexBenchmark.abStar_accepting_regex +[info] # Parameters: (size = 5) +[info] # Run progress: 0.00% complete, ETA 04:26:40 +[info] # Fork: 1 of 1 +[info] # Warmup Iteration 1: 1.893 us/op +[info] # Warmup Iteration 2: 1.746 us/op +[info] # Warmup Iteration 3: 1.692 us/op +[info] # Warmup Iteration 4: 1.704 us/op +[info] # Warmup Iteration 5: 1.692 us/op +[info] Iteration 1: 1.697 us/op +[info] Iteration 2: 1.743 us/op +[info] Iteration 3: 1.790 us/op +[info] Iteration 4: 1.673 us/op +[info] Iteration 5: 1.689 us/op +[info] Result "benchmark.RegexBenchmark.abStar_accepting_regex": +[info] 1.718 ±(99.9%) 0.185 us/op [Average] +[info] (min, avg, max) = (1.673, 1.718, 1.790), stdev = 0.048 +[info] CI (99.9%): [1.534, 1.903] (assumes normal distribution) +[info] # JMH version: 1.32 +[info] # VM version: JDK 17.0.11, OpenJDK 64-Bit Server VM, 17.0.11+9 +[info] # VM invoker: /usr/lib/jvm/temurin-17-jdk-amd64/bin/java +[info] # VM options: +[info] # Blackhole mode: full + dont-inline hint +[info] # Warmup: 5 iterations, 10 s each +[info] # Measurement: 5 iterations, 10 s each +[info] # Timeout: 10 min per iteration +[info] # Threads: 1 thread, will synchronize iterations +[info] # Benchmark mode: Average time, time/op +[info] # Benchmark: benchmark.RegexBenchmark.abStar_accepting_regex +[info] # Parameters: (size = 10) +[info] # Run progress: 0.63% complete, ETA 04:26:15 +[info] # Fork: 1 of 1 +[info] # Warmup Iteration 1: 10.807 us/op +[info] # Warmup Iteration 2: 10.209 us/op +[info] # Warmup Iteration 3: 10.220 us/op +[info] # Warmup Iteration 4: 10.218 us/op +[info] # Warmup Iteration 5: 10.229 us/op +[info] Iteration 1: 10.054 us/op +[info] Iteration 2: 10.062 us/op +[info] Iteration 3: 10.186 us/op +[info] Iteration 4: 10.071 us/op +[info] Iteration 5: 10.204 us/op +[info] Result "benchmark.RegexBenchmark.abStar_accepting_regex": +[info] 10.116 ±(99.9%) 0.283 us/op [Average] +[info] (min, avg, max) = (10.054, 10.116, 10.204), stdev = 0.073 +[info] CI (99.9%): [9.833, 10.398] (assumes normal distribution) +[info] # JMH version: 1.32 +[info] # VM version: JDK 17.0.11, OpenJDK 64-Bit Server VM, 17.0.11+9 +[info] # VM invoker: /usr/lib/jvm/temurin-17-jdk-amd64/bin/java +[info] # VM options: +[info] # Blackhole mode: full + dont-inline hint +[info] # Warmup: 5 iterations, 10 s each +[info] # Measurement: 5 iterations, 10 s each +[info] # Timeout: 10 min per iteration +[info] # Threads: 1 thread, will synchronize iterations +[info] # Benchmark mode: Average time, time/op +[info] # Benchmark: benchmark.RegexBenchmark.abStar_accepting_regex +[info] # Parameters: (size = 15) +[info] # Run progress: 1.25% complete, ETA 04:24:31 +[info] # Fork: 1 of 1 +[info] # Warmup Iteration 1: 34.100 us/op +[info] # Warmup Iteration 2: 31.979 us/op +[info] # Warmup Iteration 3: 32.033 us/op +[info] # Warmup Iteration 4: 31.788 us/op +[info] # Warmup Iteration 5: 31.933 us/op +[info] Iteration 1: 31.570 us/op +[info] Iteration 2: 31.822 us/op +[info] Iteration 3: 31.868 us/op +[info] Iteration 4: 32.109 us/op +[info] Iteration 5: 31.561 us/op +[info] Result "benchmark.RegexBenchmark.abStar_accepting_regex": +[info] 31.786 ±(99.9%) 0.882 us/op [Average] +[info] (min, avg, max) = (31.561, 31.786, 32.109), stdev = 0.229 +[info] CI (99.9%): [30.904, 32.668] (assumes normal distribution) +[info] # JMH version: 1.32 +[info] # VM version: JDK 17.0.11, OpenJDK 64-Bit Server VM, 17.0.11+9 +[info] # VM invoker: /usr/lib/jvm/temurin-17-jdk-amd64/bin/java +[info] # VM options: +[info] # Blackhole mode: full + dont-inline hint +[info] # Warmup: 5 iterations, 10 s each +[info] # Measurement: 5 iterations, 10 s each +[info] # Timeout: 10 min per iteration +[info] # Threads: 1 thread, will synchronize iterations +[info] # Benchmark mode: Average time, time/op +[info] # Benchmark: benchmark.RegexBenchmark.abStar_accepting_regex +[info] # Parameters: (size = 20) +[info] # Run progress: 1.88% complete, ETA 04:22:48 +[info] # Fork: 1 of 1 +[info] # Warmup Iteration 1: 79.834 us/op +[info] # Warmup Iteration 2: 75.056 us/op +[info] # Warmup Iteration 3: 74.888 us/op +[info] # Warmup Iteration 4: 74.625 us/op +[info] # Warmup Iteration 5: 74.751 us/op +[info] Iteration 1: 73.821 us/op +[info] Iteration 2: 74.809 us/op +[info] Iteration 3: 73.726 us/op +[info] Iteration 4: 75.158 us/op +[info] Iteration 5: 73.807 us/op +[info] Result "benchmark.RegexBenchmark.abStar_accepting_regex": +[info] 74.264 ±(99.9%) 2.577 us/op [Average] +[info] (min, avg, max) = (73.726, 74.264, 75.158), stdev = 0.669 +[info] CI (99.9%): [71.687, 76.841] (assumes normal distribution) +[info] # JMH version: 1.32 +[info] # VM version: JDK 17.0.11, OpenJDK 64-Bit Server VM, 17.0.11+9 +[info] # VM invoker: /usr/lib/jvm/temurin-17-jdk-amd64/bin/java +[info] # VM options: +[info] # Blackhole mode: full + dont-inline hint +[info] # Warmup: 5 iterations, 10 s each +[info] # Measurement: 5 iterations, 10 s each +[info] # Timeout: 10 min per iteration +[info] # Threads: 1 thread, will synchronize iterations +[info] # Benchmark mode: Average time, time/op +[info] # Benchmark: benchmark.RegexBenchmark.abStar_accepting_regex +[info] # Parameters: (size = 25) +[info] # Run progress: 2.50% complete, ETA 04:21:06 +[info] # Fork: 1 of 1 +[info] # Warmup Iteration 1: 156.665 us/op +[info] # Warmup Iteration 2: 148.576 us/op +[info] # Warmup Iteration 3: 147.658 us/op +[info] # Warmup Iteration 4: 147.439 us/op +[info] # Warmup Iteration 5: 147.855 us/op +[info] Iteration 1: 145.484 us/op +[info] Iteration 2: 147.155 us/op +[info] Iteration 3: 144.918 us/op +[info] Iteration 4: 146.754 us/op +[info] Iteration 5: 146.137 us/op +[info] Result "benchmark.RegexBenchmark.abStar_accepting_regex": +[info] 146.090 ±(99.9%) 3.507 us/op [Average] +[info] (min, avg, max) = (144.918, 146.090, 147.155), stdev = 0.911 +[info] CI (99.9%): [142.582, 149.597] (assumes normal distribution) +[info] # JMH version: 1.32 +[info] # VM version: JDK 17.0.11, OpenJDK 64-Bit Server VM, 17.0.11+9 +[info] # VM invoker: /usr/lib/jvm/temurin-17-jdk-amd64/bin/java +[info] # VM options: +[info] # Blackhole mode: full + dont-inline hint +[info] # Warmup: 5 iterations, 10 s each +[info] # Measurement: 5 iterations, 10 s each +[info] # Timeout: 10 min per iteration +[info] # Threads: 1 thread, will synchronize iterations +[info] # Benchmark mode: Average time, time/op +[info] # Benchmark: benchmark.RegexBenchmark.abStar_accepting_regex +[info] # Parameters: (size = 30) +[info] # Run progress: 3.13% complete, ETA 04:19:25 +[info] # Fork: 1 of 1 +[info] # Warmup Iteration 1: 271.811 us/op +[info] # Warmup Iteration 2: 257.193 us/op +[info] # Warmup Iteration 3: 256.897 us/op +[info] # Warmup Iteration 4: 256.498 us/op +[info] # Warmup Iteration 5: 254.767 us/op +[info] Iteration 1: 254.039 us/op +[info] Iteration 2: 258.260 us/op +[info] Iteration 3: 256.664 us/op +[info] Iteration 4: 258.534 us/op +[info] Iteration 5: 252.884 us/op +[info] Result "benchmark.RegexBenchmark.abStar_accepting_regex": +[info] 256.076 ±(99.9%) 9.722 us/op [Average] +[info] (min, avg, max) = (252.884, 256.076, 258.534), stdev = 2.525 +[info] CI (99.9%): [246.355, 265.798] (assumes normal distribution) +[info] # JMH version: 1.32 +[info] # VM version: JDK 17.0.11, OpenJDK 64-Bit Server VM, 17.0.11+9 +[info] # VM invoker: /usr/lib/jvm/temurin-17-jdk-amd64/bin/java +[info] # VM options: +[info] # Blackhole mode: full + dont-inline hint +[info] # Warmup: 5 iterations, 10 s each +[info] # Measurement: 5 iterations, 10 s each +[info] # Timeout: 10 min per iteration +[info] # Threads: 1 thread, will synchronize iterations +[info] # Benchmark mode: Average time, time/op +[info] # Benchmark: benchmark.RegexBenchmark.abStar_accepting_regex +[info] # Parameters: (size = 35) +[info] # Run progress: 3.75% complete, ETA 04:17:44 +[info] # Fork: 1 of 1 +[info] # Warmup Iteration 1: 436.947 us/op +[info] # Warmup Iteration 2: 414.890 us/op +[info] # Warmup Iteration 3: 414.595 us/op +[info] # Warmup Iteration 4: 415.311 us/op +[info] # Warmup Iteration 5: 408.053 us/op +[info] Iteration 1: 412.597 us/op +[info] Iteration 2: 411.373 us/op +[info] Iteration 3: 409.924 us/op +[info] Iteration 4: 410.601 us/op +[info] Iteration 5: 409.639 us/op +[info] Result "benchmark.RegexBenchmark.abStar_accepting_regex": +[info] 410.827 ±(99.9%) 4.600 us/op [Average] +[info] (min, avg, max) = (409.639, 410.827, 412.597), stdev = 1.195 +[info] CI (99.9%): [406.226, 415.427] (assumes normal distribution) +[info] # JMH version: 1.32 +[info] # VM version: JDK 17.0.11, OpenJDK 64-Bit Server VM, 17.0.11+9 +[info] # VM invoker: /usr/lib/jvm/temurin-17-jdk-amd64/bin/java +[info] # VM options: +[info] # Blackhole mode: full + dont-inline hint +[info] # Warmup: 5 iterations, 10 s each +[info] # Measurement: 5 iterations, 10 s each +[info] # Timeout: 10 min per iteration +[info] # Threads: 1 thread, will synchronize iterations +[info] # Benchmark mode: Average time, time/op +[info] # Benchmark: benchmark.RegexBenchmark.abStar_accepting_regex +[info] # Parameters: (size = 40) +[info] # Run progress: 4.38% complete, ETA 04:16:03 +[info] # Fork: 1 of 1 +[info] # Warmup Iteration 1: 663.111 us/op +[info] # Warmup Iteration 2: 625.955 us/op +[info] # Warmup Iteration 3: 627.828 us/op +[info] # Warmup Iteration 4: 626.643 us/op +[info] # Warmup Iteration 5: 619.212 us/op +[info] Iteration 1: 617.324 us/op +[info] Iteration 2: 618.400 us/op +[info] Iteration 3: 617.164 us/op +[info] Iteration 4: 614.740 us/op +[info] Iteration 5: 622.594 us/op +[info] Result "benchmark.RegexBenchmark.abStar_accepting_regex": +[info] 618.044 ±(99.9%) 11.066 us/op [Average] +[info] (min, avg, max) = (614.740, 618.044, 622.594), stdev = 2.874 +[info] CI (99.9%): [606.978, 629.110] (assumes normal distribution) +[info] # JMH version: 1.32 +[info] # VM version: JDK 17.0.11, OpenJDK 64-Bit Server VM, 17.0.11+9 +[info] # VM invoker: /usr/lib/jvm/temurin-17-jdk-amd64/bin/java +[info] # VM options: +[info] # Blackhole mode: full + dont-inline hint +[info] # Warmup: 5 iterations, 10 s each +[info] # Measurement: 5 iterations, 10 s each +[info] # Timeout: 10 min per iteration +[info] # Threads: 1 thread, will synchronize iterations +[info] # Benchmark mode: Average time, time/op +[info] # Benchmark: benchmark.RegexBenchmark.abStar_accepting_regex +[info] # Parameters: (size = 45) +[info] # Run progress: 5.00% complete, ETA 04:14:22 +[info] # Fork: 1 of 1 +[info] # Warmup Iteration 1: 951.215 us/op +[info] # Warmup Iteration 2: 895.022 us/op +[info] # Warmup Iteration 3: 895.570 us/op +[info] # Warmup Iteration 4: 897.134 us/op +[info] # Warmup Iteration 5: 883.724 us/op +[info] Iteration 1: 900.422 us/op +[info] Iteration 2: 886.968 us/op +[info] Iteration 3: 901.261 us/op +[info] Iteration 4: 895.335 us/op +[info] Iteration 5: 894.478 us/op +[info] Result "benchmark.RegexBenchmark.abStar_accepting_regex": +[info] 895.693 ±(99.9%) 22.044 us/op [Average] +[info] (min, avg, max) = (886.968, 895.693, 901.261), stdev = 5.725 +[info] CI (99.9%): [873.649, 917.737] (assumes normal distribution) +[info] # JMH version: 1.32 +[info] # VM version: JDK 17.0.11, OpenJDK 64-Bit Server VM, 17.0.11+9 +[info] # VM invoker: /usr/lib/jvm/temurin-17-jdk-amd64/bin/java +[info] # VM options: +[info] # Blackhole mode: full + dont-inline hint +[info] # Warmup: 5 iterations, 10 s each +[info] # Measurement: 5 iterations, 10 s each +[info] # Timeout: 10 min per iteration +[info] # Threads: 1 thread, will synchronize iterations +[info] # Benchmark mode: Average time, time/op +[info] # Benchmark: benchmark.RegexBenchmark.abStar_accepting_regex +[info] # Parameters: (size = 50) +[info] # Run progress: 5.63% complete, ETA 04:12:42 +[info] # Fork: 1 of 1 +[info] # Warmup Iteration 1: 1321.473 us/op +[info] # Warmup Iteration 2: 1244.650 us/op +[info] # Warmup Iteration 3: 1252.287 us/op +[info] # Warmup Iteration 4: 1242.204 us/op +[info] # Warmup Iteration 5: 1242.275 us/op +[info] Iteration 1: 1247.789 us/op +[info] Iteration 2: 1250.646 us/op +[info] Iteration 3: 1230.086 us/op +[info] Iteration 4: 1244.640 us/op +[info] Iteration 5: 1223.130 us/op +[info] Result "benchmark.RegexBenchmark.abStar_accepting_regex": +[info] 1239.258 ±(99.9%) 46.194 us/op [Average] +[info] (min, avg, max) = (1223.130, 1239.258, 1250.646), stdev = 11.996 +[info] CI (99.9%): [1193.064, 1285.452] (assumes normal distribution) +[info] # JMH version: 1.32 +[info] # VM version: JDK 17.0.11, OpenJDK 64-Bit Server VM, 17.0.11+9 +[info] # VM invoker: /usr/lib/jvm/temurin-17-jdk-amd64/bin/java +[info] # VM options: +[info] # Blackhole mode: full + dont-inline hint +[info] # Warmup: 5 iterations, 10 s each +[info] # Measurement: 5 iterations, 10 s each +[info] # Timeout: 10 min per iteration +[info] # Threads: 1 thread, will synchronize iterations +[info] # Benchmark mode: Average time, time/op +[info] # Benchmark: benchmark.RegexBenchmark.abStar_accepting_regex +[info] # Parameters: (size = 55) +[info] # Run progress: 6.25% complete, ETA 04:11:01 +[info] # Fork: 1 of 1 +[info] # Warmup Iteration 1: 1783.678 us/op +[info] # Warmup Iteration 2: 1684.516 us/op +[info] # Warmup Iteration 3: 1681.852 us/op +[info] # Warmup Iteration 4: 1674.600 us/op +[info] # Warmup Iteration 5: 1677.731 us/op +[info] Iteration 1: 1654.021 us/op +[info] Iteration 2: 1674.448 us/op +[info] Iteration 3: 1654.551 us/op +[info] Iteration 4: 1663.738 us/op +[info] Iteration 5: 1658.206 us/op +[info] Result "benchmark.RegexBenchmark.abStar_accepting_regex": +[info] 1660.993 ±(99.9%) 32.586 us/op [Average] +[info] (min, avg, max) = (1654.021, 1660.993, 1674.448), stdev = 8.462 +[info] CI (99.9%): [1628.407, 1693.579] (assumes normal distribution) +[info] # JMH version: 1.32 +[info] # VM version: JDK 17.0.11, OpenJDK 64-Bit Server VM, 17.0.11+9 +[info] # VM invoker: /usr/lib/jvm/temurin-17-jdk-amd64/bin/java +[info] # VM options: +[info] # Blackhole mode: full + dont-inline hint +[info] # Warmup: 5 iterations, 10 s each +[info] # Measurement: 5 iterations, 10 s each +[info] # Timeout: 10 min per iteration +[info] # Threads: 1 thread, will synchronize iterations +[info] # Benchmark mode: Average time, time/op +[info] # Benchmark: benchmark.RegexBenchmark.abStar_accepting_regex +[info] # Parameters: (size = 60) +[info] # Run progress: 6.88% complete, ETA 04:09:20 +[info] # Fork: 1 of 1 +[info] # Warmup Iteration 1: 2338.491 us/op +[info] # Warmup Iteration 2: 2211.306 us/op +[info] # Warmup Iteration 3: 2200.110 us/op +[info] # Warmup Iteration 4: 2226.046 us/op +[info] # Warmup Iteration 5: 2194.647 us/op +[info] Iteration 1: 2190.000 us/op +[info] Iteration 2: 2211.766 us/op +[info] Iteration 3: 2235.557 us/op +[info] Iteration 4: 2182.800 us/op +[info] Iteration 5: 2206.895 us/op +[info] Result "benchmark.RegexBenchmark.abStar_accepting_regex": +[info] 2205.404 ±(99.9%) 79.386 us/op [Average] +[info] (min, avg, max) = (2182.800, 2205.404, 2235.557), stdev = 20.616 +[info] CI (99.9%): [2126.017, 2284.790] (assumes normal distribution) +[info] # JMH version: 1.32 +[info] # VM version: JDK 17.0.11, OpenJDK 64-Bit Server VM, 17.0.11+9 +[info] # VM invoker: /usr/lib/jvm/temurin-17-jdk-amd64/bin/java +[info] # VM options: +[info] # Blackhole mode: full + dont-inline hint +[info] # Warmup: 5 iterations, 10 s each +[info] # Measurement: 5 iterations, 10 s each +[info] # Timeout: 10 min per iteration +[info] # Threads: 1 thread, will synchronize iterations +[info] # Benchmark mode: Average time, time/op +[info] # Benchmark: benchmark.RegexBenchmark.abStar_accepting_regex +[info] # Parameters: (size = 65) +[info] # Run progress: 7.50% complete, ETA 04:07:40 +[info] # Fork: 1 of 1 +[info] # Warmup Iteration 1: 3018.482 us/op +[info] # Warmup Iteration 2: 2832.532 us/op +[info] # Warmup Iteration 3: 2837.197 us/op +[info] # Warmup Iteration 4: 2807.911 us/op +[info] # Warmup Iteration 5: 2812.827 us/op +[info] Iteration 1: 2824.355 us/op +[info] Iteration 2: 2784.257 us/op +[info] Iteration 3: 2819.723 us/op +[info] Iteration 4: 2784.265 us/op +[info] Iteration 5: 2809.912 us/op +[info] Result "benchmark.RegexBenchmark.abStar_accepting_regex": +[info] 2804.502 ±(99.9%) 73.930 us/op [Average] +[info] (min, avg, max) = (2784.257, 2804.502, 2824.355), stdev = 19.199 +[info] CI (99.9%): [2730.572, 2878.433] (assumes normal distribution) +[info] # JMH version: 1.32 +[info] # VM version: JDK 17.0.11, OpenJDK 64-Bit Server VM, 17.0.11+9 +[info] # VM invoker: /usr/lib/jvm/temurin-17-jdk-amd64/bin/java +[info] # VM options: +[info] # Blackhole mode: full + dont-inline hint +[info] # Warmup: 5 iterations, 10 s each +[info] # Measurement: 5 iterations, 10 s each +[info] # Timeout: 10 min per iteration +[info] # Threads: 1 thread, will synchronize iterations +[info] # Benchmark mode: Average time, time/op +[info] # Benchmark: benchmark.RegexBenchmark.abStar_accepting_regex +[info] # Parameters: (size = 70) +[info] # Run progress: 8.13% complete, ETA 04:06:00 +[info] # Fork: 1 of 1 +[info] # Warmup Iteration 1: 3788.844 us/op +[info] # Warmup Iteration 2: 3545.434 us/op +[info] # Warmup Iteration 3: 3499.799 us/op +[info] # Warmup Iteration 4: 3539.087 us/op +[info] # Warmup Iteration 5: 3482.376 us/op +[info] Iteration 1: 3554.703 us/op +[info] Iteration 2: 3505.661 us/op +[info] Iteration 3: 3548.476 us/op +[info] Iteration 4: 3509.635 us/op +[info] Iteration 5: 3548.219 us/op +[info] Result "benchmark.RegexBenchmark.abStar_accepting_regex": +[info] 3533.339 ±(99.9%) 91.019 us/op [Average] +[info] (min, avg, max) = (3505.661, 3533.339, 3554.703), stdev = 23.637 +[info] CI (99.9%): [3442.320, 3624.358] (assumes normal distribution) +[info] # JMH version: 1.32 +[info] # VM version: JDK 17.0.11, OpenJDK 64-Bit Server VM, 17.0.11+9 +[info] # VM invoker: /usr/lib/jvm/temurin-17-jdk-amd64/bin/java +[info] # VM options: +[info] # Blackhole mode: full + dont-inline hint +[info] # Warmup: 5 iterations, 10 s each +[info] # Measurement: 5 iterations, 10 s each +[info] # Timeout: 10 min per iteration +[info] # Threads: 1 thread, will synchronize iterations +[info] # Benchmark mode: Average time, time/op +[info] # Benchmark: benchmark.RegexBenchmark.abStar_accepting_regex +[info] # Parameters: (size = 75) +[info] # Run progress: 8.75% complete, ETA 04:04:19 +[info] # Fork: 1 of 1 +[info] # Warmup Iteration 1: 4724.984 us/op +[info] # Warmup Iteration 2: 4414.747 us/op +[info] # Warmup Iteration 3: 4385.753 us/op +[info] # Warmup Iteration 4: 4421.577 us/op +[info] # Warmup Iteration 5: 4515.994 us/op +[info] Iteration 1: 4435.122 us/op +[info] Iteration 2: 4469.446 us/op +[info] Iteration 3: 4415.493 us/op +[info] Iteration 4: 4395.342 us/op +[info] Iteration 5: 4342.924 us/op +[info] Result "benchmark.RegexBenchmark.abStar_accepting_regex": +[info] 4411.665 ±(99.9%) 181.586 us/op [Average] +[info] (min, avg, max) = (4342.924, 4411.665, 4469.446), stdev = 47.157 +[info] CI (99.9%): [4230.079, 4593.251] (assumes normal distribution) +[info] # JMH version: 1.32 +[info] # VM version: JDK 17.0.11, OpenJDK 64-Bit Server VM, 17.0.11+9 +[info] # VM invoker: /usr/lib/jvm/temurin-17-jdk-amd64/bin/java +[info] # VM options: +[info] # Blackhole mode: full + dont-inline hint +[info] # Warmup: 5 iterations, 10 s each +[info] # Measurement: 5 iterations, 10 s each +[info] # Timeout: 10 min per iteration +[info] # Threads: 1 thread, will synchronize iterations +[info] # Benchmark mode: Average time, time/op +[info] # Benchmark: benchmark.RegexBenchmark.abStar_accepting_regex +[info] # Parameters: (size = 80) +[info] # Run progress: 9.38% complete, ETA 04:02:39 +[info] # Fork: 1 of 1 +[info] # Warmup Iteration 1: 5727.227 us/op +[info] # Warmup Iteration 2: 5350.702 us/op +[info] # Warmup Iteration 3: 5288.866 us/op +[info] # Warmup Iteration 4: 5369.704 us/op +[info] # Warmup Iteration 5: 5281.919 us/op +[info] Iteration 1: 5355.887 us/op +[info] Iteration 2: 5336.874 us/op +[info] Iteration 3: 5293.648 us/op +[info] Iteration 4: 5261.380 us/op +[info] Iteration 5: 5338.653 us/op +[info] Result "benchmark.RegexBenchmark.abStar_accepting_regex": +[info] 5317.288 ±(99.9%) 149.315 us/op [Average] +[info] (min, avg, max) = (5261.380, 5317.288, 5355.887), stdev = 38.777 +[info] CI (99.9%): [5167.973, 5466.604] (assumes normal distribution) +[info] # JMH version: 1.32 +[info] # VM version: JDK 17.0.11, OpenJDK 64-Bit Server VM, 17.0.11+9 +[info] # VM invoker: /usr/lib/jvm/temurin-17-jdk-amd64/bin/java +[info] # VM options: +[info] # Blackhole mode: full + dont-inline hint +[info] # Warmup: 5 iterations, 10 s each +[info] # Measurement: 5 iterations, 10 s each +[info] # Timeout: 10 min per iteration +[info] # Threads: 1 thread, will synchronize iterations +[info] # Benchmark mode: Average time, time/op +[info] # Benchmark: benchmark.RegexBenchmark.abStar_accepting_regex +[info] # Parameters: (size = 85) +[info] # Run progress: 10.00% complete, ETA 04:00:59 +[info] # Fork: 1 of 1 +[info] # Warmup Iteration 1: 6902.766 us/op +[info] # Warmup Iteration 2: 6444.748 us/op +[info] # Warmup Iteration 3: 6428.795 us/op +[info] # Warmup Iteration 4: 6393.420 us/op +[info] # Warmup Iteration 5: 6472.554 us/op +[info] Iteration 1: 6370.088 us/op +[info] Iteration 2: 6457.461 us/op +[info] Iteration 3: 6346.935 us/op +[info] Iteration 4: 6373.305 us/op +[info] Iteration 5: 6382.865 us/op +[info] Result "benchmark.RegexBenchmark.abStar_accepting_regex": +[info] 6386.131 ±(99.9%) 161.737 us/op [Average] +[info] (min, avg, max) = (6346.935, 6386.131, 6457.461), stdev = 42.003 +[info] CI (99.9%): [6224.394, 6547.868] (assumes normal distribution) +[info] # JMH version: 1.32 +[info] # VM version: JDK 17.0.11, OpenJDK 64-Bit Server VM, 17.0.11+9 +[info] # VM invoker: /usr/lib/jvm/temurin-17-jdk-amd64/bin/java +[info] # VM options: +[info] # Blackhole mode: full + dont-inline hint +[info] # Warmup: 5 iterations, 10 s each +[info] # Measurement: 5 iterations, 10 s each +[info] # Timeout: 10 min per iteration +[info] # Threads: 1 thread, will synchronize iterations +[info] # Benchmark mode: Average time, time/op +[info] # Benchmark: benchmark.RegexBenchmark.abStar_accepting_regex +[info] # Parameters: (size = 90) +[info] # Run progress: 10.63% complete, ETA 03:59:18 +[info] # Fork: 1 of 1 +[info] # Warmup Iteration 1: 8214.794 us/op +[info] # Warmup Iteration 2: 7603.918 us/op +[info] # Warmup Iteration 3: 7676.171 us/op +[info] # Warmup Iteration 4: 7697.759 us/op +[info] # Warmup Iteration 5: 7760.668 us/op +[info] Iteration 1: 7820.583 us/op +[info] Iteration 2: 7801.222 us/op +[info] Iteration 3: 7815.937 us/op +[info] Iteration 4: 7727.704 us/op +[info] Iteration 5: 7667.320 us/op +[info] Result "benchmark.RegexBenchmark.abStar_accepting_regex": +[info] 7766.553 ±(99.9%) 257.710 us/op [Average] +[info] (min, avg, max) = (7667.320, 7766.553, 7820.583), stdev = 66.926 +[info] CI (99.9%): [7508.843, 8024.263] (assumes normal distribution) +[info] # JMH version: 1.32 +[info] # VM version: JDK 17.0.11, OpenJDK 64-Bit Server VM, 17.0.11+9 +[info] # VM invoker: /usr/lib/jvm/temurin-17-jdk-amd64/bin/java +[info] # VM options: +[info] # Blackhole mode: full + dont-inline hint +[info] # Warmup: 5 iterations, 10 s each +[info] # Measurement: 5 iterations, 10 s each +[info] # Timeout: 10 min per iteration +[info] # Threads: 1 thread, will synchronize iterations +[info] # Benchmark mode: Average time, time/op +[info] # Benchmark: benchmark.RegexBenchmark.abStar_accepting_regex +[info] # Parameters: (size = 95) +[info] # Run progress: 11.25% complete, ETA 03:57:38 +[info] # Fork: 1 of 1 +[info] # Warmup Iteration 1: 9716.770 us/op +[info] # Warmup Iteration 2: 9091.213 us/op +[info] # Warmup Iteration 3: 9123.615 us/op +[info] # Warmup Iteration 4: 9083.732 us/op +[info] # Warmup Iteration 5: 9136.628 us/op +[info] Iteration 1: 8988.651 us/op +[info] Iteration 2: 9096.242 us/op +[info] Iteration 3: 8979.067 us/op +[info] Iteration 4: 9190.976 us/op +[info] Iteration 5: 8973.142 us/op +[info] Result "benchmark.RegexBenchmark.abStar_accepting_regex": +[info] 9045.615 ±(99.9%) 368.430 us/op [Average] +[info] (min, avg, max) = (8973.142, 9045.615, 9190.976), stdev = 95.680 +[info] CI (99.9%): [8677.185, 9414.046] (assumes normal distribution) +[info] # JMH version: 1.32 +[info] # VM version: JDK 17.0.11, OpenJDK 64-Bit Server VM, 17.0.11+9 +[info] # VM invoker: /usr/lib/jvm/temurin-17-jdk-amd64/bin/java +[info] # VM options: +[info] # Blackhole mode: full + dont-inline hint +[info] # Warmup: 5 iterations, 10 s each +[info] # Measurement: 5 iterations, 10 s each +[info] # Timeout: 10 min per iteration +[info] # Threads: 1 thread, will synchronize iterations +[info] # Benchmark mode: Average time, time/op +[info] # Benchmark: benchmark.RegexBenchmark.abStar_accepting_regex +[info] # Parameters: (size = 100) +[info] # Run progress: 11.88% complete, ETA 03:55:58 +[info] # Fork: 1 of 1 +[info] # Warmup Iteration 1: 11266.731 us/op +[info] # Warmup Iteration 2: 10759.143 us/op +[info] # Warmup Iteration 3: 10698.762 us/op +[info] # Warmup Iteration 4: 10664.237 us/op +[info] # Warmup Iteration 5: 10670.718 us/op +[info] Iteration 1: 10489.075 us/op +[info] Iteration 2: 10615.258 us/op +[info] Iteration 3: 10470.663 us/op +[info] Iteration 4: 10618.366 us/op +[info] Iteration 5: 10463.976 us/op +[info] Result "benchmark.RegexBenchmark.abStar_accepting_regex": +[info] 10531.468 ±(99.9%) 302.107 us/op [Average] +[info] (min, avg, max) = (10463.976, 10531.468, 10618.366), stdev = 78.456 +[info] CI (99.9%): [10229.360, 10833.575] (assumes normal distribution) +[info] # JMH version: 1.32 +[info] # VM version: JDK 17.0.11, OpenJDK 64-Bit Server VM, 17.0.11+9 +[info] # VM invoker: /usr/lib/jvm/temurin-17-jdk-amd64/bin/java +[info] # VM options: +[info] # Blackhole mode: full + dont-inline hint +[info] # Warmup: 5 iterations, 10 s each +[info] # Measurement: 5 iterations, 10 s each +[info] # Timeout: 10 min per iteration +[info] # Threads: 1 thread, will synchronize iterations +[info] # Benchmark mode: Average time, time/op +[info] # Benchmark: benchmark.RegexBenchmark.abStar_accepting_regex_mem +[info] # Parameters: (size = 5) +[info] # Run progress: 12.50% complete, ETA 03:54:20 +[info] # Fork: 1 of 1 +[info] # Warmup Iteration 1: 10.667 us/op +[info] # Warmup Iteration 2: 10.390 us/op +[info] # Warmup Iteration 3: 10.325 us/op +[info] # Warmup Iteration 4: 10.325 us/op +[info] # Warmup Iteration 5: 10.333 us/op +[info] Iteration 1: 10.272 us/op +[info] Iteration 2: 10.234 us/op +[info] Iteration 3: 10.275 us/op +[info] Iteration 4: 10.224 us/op +[info] Iteration 5: 10.279 us/op +[info] Result "benchmark.RegexBenchmark.abStar_accepting_regex_mem": +[info] 10.257 ±(99.9%) 0.099 us/op [Average] +[info] (min, avg, max) = (10.224, 10.257, 10.279), stdev = 0.026 +[info] CI (99.9%): [10.158, 10.356] (assumes normal distribution) +[info] # JMH version: 1.32 +[info] # VM version: JDK 17.0.11, OpenJDK 64-Bit Server VM, 17.0.11+9 +[info] # VM invoker: /usr/lib/jvm/temurin-17-jdk-amd64/bin/java +[info] # VM options: +[info] # Blackhole mode: full + dont-inline hint +[info] # Warmup: 5 iterations, 10 s each +[info] # Measurement: 5 iterations, 10 s each +[info] # Timeout: 10 min per iteration +[info] # Threads: 1 thread, will synchronize iterations +[info] # Benchmark mode: Average time, time/op +[info] # Benchmark: benchmark.RegexBenchmark.abStar_accepting_regex_mem +[info] # Parameters: (size = 10) +[info] # Run progress: 13.13% complete, ETA 03:52:39 +[info] # Fork: 1 of 1 +[info] # Warmup Iteration 1: 60.737 us/op +[info] # Warmup Iteration 2: 59.223 us/op +[info] # Warmup Iteration 3: 59.497 us/op +[info] # Warmup Iteration 4: 59.506 us/op +[info] # Warmup Iteration 5: 59.421 us/op +[info] Iteration 1: 59.478 us/op +[info] Iteration 2: 59.452 us/op +[info] Iteration 3: 59.436 us/op +[info] Iteration 4: 59.465 us/op +[info] Iteration 5: 59.225 us/op +[info] Result "benchmark.RegexBenchmark.abStar_accepting_regex_mem": +[info] 59.411 ±(99.9%) 0.405 us/op [Average] +[info] (min, avg, max) = (59.225, 59.411, 59.478), stdev = 0.105 +[info] CI (99.9%): [59.007, 59.816] (assumes normal distribution) +[info] # JMH version: 1.32 +[info] # VM version: JDK 17.0.11, OpenJDK 64-Bit Server VM, 17.0.11+9 +[info] # VM invoker: /usr/lib/jvm/temurin-17-jdk-amd64/bin/java +[info] # VM options: +[info] # Blackhole mode: full + dont-inline hint +[info] # Warmup: 5 iterations, 10 s each +[info] # Measurement: 5 iterations, 10 s each +[info] # Timeout: 10 min per iteration +[info] # Threads: 1 thread, will synchronize iterations +[info] # Benchmark mode: Average time, time/op +[info] # Benchmark: benchmark.RegexBenchmark.abStar_accepting_regex_mem +[info] # Parameters: (size = 15) +[info] # Run progress: 13.75% complete, ETA 03:50:59 +[info] # Fork: 1 of 1 +[info] # Warmup Iteration 1: 171.161 us/op +[info] # Warmup Iteration 2: 165.900 us/op +[info] # Warmup Iteration 3: 166.145 us/op +[info] # Warmup Iteration 4: 165.315 us/op +[info] # Warmup Iteration 5: 165.604 us/op +[info] Iteration 1: 165.331 us/op +[info] Iteration 2: 165.273 us/op +[info] Iteration 3: 165.223 us/op +[info] Iteration 4: 165.710 us/op +[info] Iteration 5: 165.329 us/op +[info] Result "benchmark.RegexBenchmark.abStar_accepting_regex_mem": +[info] 165.373 ±(99.9%) 0.745 us/op [Average] +[info] (min, avg, max) = (165.223, 165.373, 165.710), stdev = 0.193 +[info] CI (99.9%): [164.629, 166.118] (assumes normal distribution) +[info] # JMH version: 1.32 +[info] # VM version: JDK 17.0.11, OpenJDK 64-Bit Server VM, 17.0.11+9 +[info] # VM invoker: /usr/lib/jvm/temurin-17-jdk-amd64/bin/java +[info] # VM options: +[info] # Blackhole mode: full + dont-inline hint +[info] # Warmup: 5 iterations, 10 s each +[info] # Measurement: 5 iterations, 10 s each +[info] # Timeout: 10 min per iteration +[info] # Threads: 1 thread, will synchronize iterations +[info] # Benchmark mode: Average time, time/op +[info] # Benchmark: benchmark.RegexBenchmark.abStar_accepting_regex_mem +[info] # Parameters: (size = 20) +[info] # Run progress: 14.37% complete, ETA 03:49:18 +[info] # Fork: 1 of 1 +[info] # Warmup Iteration 1: 355.889 us/op +[info] # Warmup Iteration 2: 345.349 us/op +[info] # Warmup Iteration 3: 345.577 us/op +[info] # Warmup Iteration 4: 345.672 us/op +[info] # Warmup Iteration 5: 345.871 us/op +[info] Iteration 1: 345.955 us/op +[info] Iteration 2: 347.683 us/op +[info] Iteration 3: 351.085 us/op +[info] Iteration 4: 346.863 us/op +[info] Iteration 5: 346.165 us/op +[info] Result "benchmark.RegexBenchmark.abStar_accepting_regex_mem": +[info] 347.550 ±(99.9%) 8.042 us/op [Average] +[info] (min, avg, max) = (345.955, 347.550, 351.085), stdev = 2.088 +[info] CI (99.9%): [339.508, 355.592] (assumes normal distribution) +[info] # JMH version: 1.32 +[info] # VM version: JDK 17.0.11, OpenJDK 64-Bit Server VM, 17.0.11+9 +[info] # VM invoker: /usr/lib/jvm/temurin-17-jdk-amd64/bin/java +[info] # VM options: +[info] # Blackhole mode: full + dont-inline hint +[info] # Warmup: 5 iterations, 10 s each +[info] # Measurement: 5 iterations, 10 s each +[info] # Timeout: 10 min per iteration +[info] # Threads: 1 thread, will synchronize iterations +[info] # Benchmark mode: Average time, time/op +[info] # Benchmark: benchmark.RegexBenchmark.abStar_accepting_regex_mem +[info] # Parameters: (size = 25) +[info] # Run progress: 15.00% complete, ETA 03:47:37 +[info] # Fork: 1 of 1 +[info] # Warmup Iteration 1: 631.425 us/op +[info] # Warmup Iteration 2: 608.935 us/op +[info] # Warmup Iteration 3: 608.618 us/op +[info] # Warmup Iteration 4: 608.791 us/op +[info] # Warmup Iteration 5: 609.563 us/op +[info] Iteration 1: 609.253 us/op +[info] Iteration 2: 608.358 us/op +[info] Iteration 3: 607.146 us/op +[info] Iteration 4: 608.633 us/op +[info] Iteration 5: 607.699 us/op +[info] Result "benchmark.RegexBenchmark.abStar_accepting_regex_mem": +[info] 608.218 ±(99.9%) 3.153 us/op [Average] +[info] (min, avg, max) = (607.146, 608.218, 609.253), stdev = 0.819 +[info] CI (99.9%): [605.065, 611.370] (assumes normal distribution) +[info] # JMH version: 1.32 +[info] # VM version: JDK 17.0.11, OpenJDK 64-Bit Server VM, 17.0.11+9 +[info] # VM invoker: /usr/lib/jvm/temurin-17-jdk-amd64/bin/java +[info] # VM options: +[info] # Blackhole mode: full + dont-inline hint +[info] # Warmup: 5 iterations, 10 s each +[info] # Measurement: 5 iterations, 10 s each +[info] # Timeout: 10 min per iteration +[info] # Threads: 1 thread, will synchronize iterations +[info] # Benchmark mode: Average time, time/op +[info] # Benchmark: benchmark.RegexBenchmark.abStar_accepting_regex_mem +[info] # Parameters: (size = 30) +[info] # Run progress: 15.63% complete, ETA 03:45:57 +[info] # Fork: 1 of 1 +[info] # Warmup Iteration 1: 1028.862 us/op +[info] # Warmup Iteration 2: 984.301 us/op +[info] # Warmup Iteration 3: 984.540 us/op +[info] # Warmup Iteration 4: 985.349 us/op +[info] # Warmup Iteration 5: 984.133 us/op +[info] Iteration 1: 982.540 us/op +[info] Iteration 2: 982.887 us/op +[info] Iteration 3: 979.242 us/op +[info] Iteration 4: 980.467 us/op +[info] Iteration 5: 982.661 us/op +[info] Result "benchmark.RegexBenchmark.abStar_accepting_regex_mem": +[info] 981.560 ±(99.9%) 6.240 us/op [Average] +[info] (min, avg, max) = (979.242, 981.560, 982.887), stdev = 1.620 +[info] CI (99.9%): [975.320, 987.799] (assumes normal distribution) +[info] # JMH version: 1.32 +[info] # VM version: JDK 17.0.11, OpenJDK 64-Bit Server VM, 17.0.11+9 +[info] # VM invoker: /usr/lib/jvm/temurin-17-jdk-amd64/bin/java +[info] # VM options: +[info] # Blackhole mode: full + dont-inline hint +[info] # Warmup: 5 iterations, 10 s each +[info] # Measurement: 5 iterations, 10 s each +[info] # Timeout: 10 min per iteration +[info] # Threads: 1 thread, will synchronize iterations +[info] # Benchmark mode: Average time, time/op +[info] # Benchmark: benchmark.RegexBenchmark.abStar_accepting_regex_mem +[info] # Parameters: (size = 35) +[info] # Run progress: 16.25% complete, ETA 03:44:16 +[info] # Fork: 1 of 1 +[info] # Warmup Iteration 1: 1600.484 us/op +[info] # Warmup Iteration 2: 1536.376 us/op +[info] # Warmup Iteration 3: 1535.827 us/op +[info] # Warmup Iteration 4: 1539.493 us/op +[info] # Warmup Iteration 5: 1569.976 us/op +[info] Iteration 1: 1571.627 us/op +[info] Iteration 2: 1567.371 us/op +[info] Iteration 3: 1565.188 us/op +[info] Iteration 4: 1542.337 us/op +[info] Iteration 5: 1535.467 us/op +[info] Result "benchmark.RegexBenchmark.abStar_accepting_regex_mem": +[info] 1556.398 ±(99.9%) 62.844 us/op [Average] +[info] (min, avg, max) = (1535.467, 1556.398, 1571.627), stdev = 16.320 +[info] CI (99.9%): [1493.554, 1619.242] (assumes normal distribution) +[info] # JMH version: 1.32 +[info] # VM version: JDK 17.0.11, OpenJDK 64-Bit Server VM, 17.0.11+9 +[info] # VM invoker: /usr/lib/jvm/temurin-17-jdk-amd64/bin/java +[info] # VM options: +[info] # Blackhole mode: full + dont-inline hint +[info] # Warmup: 5 iterations, 10 s each +[info] # Measurement: 5 iterations, 10 s each +[info] # Timeout: 10 min per iteration +[info] # Threads: 1 thread, will synchronize iterations +[info] # Benchmark mode: Average time, time/op +[info] # Benchmark: benchmark.RegexBenchmark.abStar_accepting_regex_mem +[info] # Parameters: (size = 40) +[info] # Run progress: 16.88% complete, ETA 03:42:35 +[info] # Fork: 1 of 1 +[info] # Warmup Iteration 1: 2275.232 us/op +[info] # Warmup Iteration 2: 2182.105 us/op +[info] # Warmup Iteration 3: 2163.058 us/op +[info] # Warmup Iteration 4: 2158.304 us/op +[info] # Warmup Iteration 5: 2145.278 us/op +[info] Iteration 1: 2145.590 us/op +[info] Iteration 2: 2148.637 us/op +[info] Iteration 3: 2147.300 us/op +[info] Iteration 4: 2150.169 us/op +[info] Iteration 5: 2149.944 us/op +[info] Result "benchmark.RegexBenchmark.abStar_accepting_regex_mem": +[info] 2148.328 ±(99.9%) 7.370 us/op [Average] +[info] (min, avg, max) = (2145.590, 2148.328, 2150.169), stdev = 1.914 +[info] CI (99.9%): [2140.958, 2155.698] (assumes normal distribution) +[info] # JMH version: 1.32 +[info] # VM version: JDK 17.0.11, OpenJDK 64-Bit Server VM, 17.0.11+9 +[info] # VM invoker: /usr/lib/jvm/temurin-17-jdk-amd64/bin/java +[info] # VM options: +[info] # Blackhole mode: full + dont-inline hint +[info] # Warmup: 5 iterations, 10 s each +[info] # Measurement: 5 iterations, 10 s each +[info] # Timeout: 10 min per iteration +[info] # Threads: 1 thread, will synchronize iterations +[info] # Benchmark mode: Average time, time/op +[info] # Benchmark: benchmark.RegexBenchmark.abStar_accepting_regex_mem +[info] # Parameters: (size = 45) +[info] # Run progress: 17.50% complete, ETA 03:40:55 +[info] # Fork: 1 of 1 +[info] # Warmup Iteration 1: 3131.547 us/op +[info] # Warmup Iteration 2: 3035.408 us/op +[info] # Warmup Iteration 3: 2982.451 us/op +[info] # Warmup Iteration 4: 2976.431 us/op +[info] # Warmup Iteration 5: 2973.928 us/op +[info] Iteration 1: 2975.436 us/op +[info] Iteration 2: 2977.676 us/op +[info] Iteration 3: 2969.007 us/op +[info] Iteration 4: 2971.172 us/op +[info] Iteration 5: 2971.366 us/op +[info] Result "benchmark.RegexBenchmark.abStar_accepting_regex_mem": +[info] 2972.931 ±(99.9%) 13.578 us/op [Average] +[info] (min, avg, max) = (2969.007, 2972.931, 2977.676), stdev = 3.526 +[info] CI (99.9%): [2959.354, 2986.509] (assumes normal distribution) +[info] # JMH version: 1.32 +[info] # VM version: JDK 17.0.11, OpenJDK 64-Bit Server VM, 17.0.11+9 +[info] # VM invoker: /usr/lib/jvm/temurin-17-jdk-amd64/bin/java +[info] # VM options: +[info] # Blackhole mode: full + dont-inline hint +[info] # Warmup: 5 iterations, 10 s each +[info] # Measurement: 5 iterations, 10 s each +[info] # Timeout: 10 min per iteration +[info] # Threads: 1 thread, will synchronize iterations +[info] # Benchmark mode: Average time, time/op +[info] # Benchmark: benchmark.RegexBenchmark.abStar_accepting_regex_mem +[info] # Parameters: (size = 50) +[info] # Run progress: 18.13% complete, ETA 03:39:14 +[info] # Fork: 1 of 1 +[info] # Warmup Iteration 1: 4198.168 us/op +[info] # Warmup Iteration 2: 4084.518 us/op +[info] # Warmup Iteration 3: 3987.449 us/op +[info] # Warmup Iteration 4: 3989.937 us/op +[info] # Warmup Iteration 5: 4049.938 us/op +[info] Iteration 1: 4082.779 us/op +[info] Iteration 2: 4057.805 us/op +[info] Iteration 3: 4046.948 us/op +[info] Iteration 4: 4042.255 us/op +[info] Iteration 5: 3971.852 us/op +[info] Result "benchmark.RegexBenchmark.abStar_accepting_regex_mem": +[info] 4040.328 ±(99.9%) 159.279 us/op [Average] +[info] (min, avg, max) = (3971.852, 4040.328, 4082.779), stdev = 41.364 +[info] CI (99.9%): [3881.048, 4199.607] (assumes normal distribution) +[info] # JMH version: 1.32 +[info] # VM version: JDK 17.0.11, OpenJDK 64-Bit Server VM, 17.0.11+9 +[info] # VM invoker: /usr/lib/jvm/temurin-17-jdk-amd64/bin/java +[info] # VM options: +[info] # Blackhole mode: full + dont-inline hint +[info] # Warmup: 5 iterations, 10 s each +[info] # Measurement: 5 iterations, 10 s each +[info] # Timeout: 10 min per iteration +[info] # Threads: 1 thread, will synchronize iterations +[info] # Benchmark mode: Average time, time/op +[info] # Benchmark: benchmark.RegexBenchmark.abStar_accepting_regex_mem +[info] # Parameters: (size = 55) +[info] # Run progress: 18.75% complete, ETA 03:37:33 +[info] # Fork: 1 of 1 +[info] # Warmup Iteration 1: 5398.971 us/op +[info] # Warmup Iteration 2: 5224.216 us/op +[info] # Warmup Iteration 3: 5098.822 us/op +[info] # Warmup Iteration 4: 5061.053 us/op +[info] # Warmup Iteration 5: 5055.937 us/op +[info] Iteration 1: 5066.927 us/op +[info] Iteration 2: 5065.037 us/op +[info] Iteration 3: 5058.024 us/op +[info] Iteration 4: 5060.597 us/op +[info] Iteration 5: 5050.158 us/op +[info] Result "benchmark.RegexBenchmark.abStar_accepting_regex_mem": +[info] 5060.148 ±(99.9%) 25.422 us/op [Average] +[info] (min, avg, max) = (5050.158, 5060.148, 5066.927), stdev = 6.602 +[info] CI (99.9%): [5034.726, 5085.571] (assumes normal distribution) +[info] # JMH version: 1.32 +[info] # VM version: JDK 17.0.11, OpenJDK 64-Bit Server VM, 17.0.11+9 +[info] # VM invoker: /usr/lib/jvm/temurin-17-jdk-amd64/bin/java +[info] # VM options: +[info] # Blackhole mode: full + dont-inline hint +[info] # Warmup: 5 iterations, 10 s each +[info] # Measurement: 5 iterations, 10 s each +[info] # Timeout: 10 min per iteration +[info] # Threads: 1 thread, will synchronize iterations +[info] # Benchmark mode: Average time, time/op +[info] # Benchmark: benchmark.RegexBenchmark.abStar_accepting_regex_mem +[info] # Parameters: (size = 60) +[info] # Run progress: 19.38% complete, ETA 03:35:53 +[info] # Fork: 1 of 1 +[info] # Warmup Iteration 1: 7221.293 us/op +[info] # Warmup Iteration 2: 7019.297 us/op +[info] # Warmup Iteration 3: 6997.384 us/op +[info] # Warmup Iteration 4: 6717.553 us/op +[info] # Warmup Iteration 5: 6724.414 us/op +[info] Iteration 1: 6730.023 us/op +[info] Iteration 2: 6740.835 us/op +[info] Iteration 3: 6730.897 us/op +[info] Iteration 4: 6715.019 us/op +[info] Iteration 5: 6706.971 us/op +[info] Result "benchmark.RegexBenchmark.abStar_accepting_regex_mem": +[info] 6724.749 ±(99.9%) 52.200 us/op [Average] +[info] (min, avg, max) = (6706.971, 6724.749, 6740.835), stdev = 13.556 +[info] CI (99.9%): [6672.549, 6776.949] (assumes normal distribution) +[info] # JMH version: 1.32 +[info] # VM version: JDK 17.0.11, OpenJDK 64-Bit Server VM, 17.0.11+9 +[info] # VM invoker: /usr/lib/jvm/temurin-17-jdk-amd64/bin/java +[info] # VM options: +[info] # Blackhole mode: full + dont-inline hint +[info] # Warmup: 5 iterations, 10 s each +[info] # Measurement: 5 iterations, 10 s each +[info] # Timeout: 10 min per iteration +[info] # Threads: 1 thread, will synchronize iterations +[info] # Benchmark mode: Average time, time/op +[info] # Benchmark: benchmark.RegexBenchmark.abStar_accepting_regex_mem +[info] # Parameters: (size = 65) +[info] # Run progress: 20.00% complete, ETA 03:34:12 +[info] # Fork: 1 of 1 +[info] # Warmup Iteration 1: 8910.599 us/op +[info] # Warmup Iteration 2: 8620.643 us/op +[info] # Warmup Iteration 3: 8626.055 us/op +[info] # Warmup Iteration 4: 8263.495 us/op +[info] # Warmup Iteration 5: 8316.588 us/op +[info] Iteration 1: 8437.705 us/op +[info] Iteration 2: 8403.233 us/op +[info] Iteration 3: 8399.945 us/op +[info] Iteration 4: 8307.588 us/op +[info] Iteration 5: 8268.302 us/op +[info] Result "benchmark.RegexBenchmark.abStar_accepting_regex_mem": +[info] 8363.355 ±(99.9%) 276.350 us/op [Average] +[info] (min, avg, max) = (8268.302, 8363.355, 8437.705), stdev = 71.767 +[info] CI (99.9%): [8087.005, 8639.704] (assumes normal distribution) +[info] # JMH version: 1.32 +[info] # VM version: JDK 17.0.11, OpenJDK 64-Bit Server VM, 17.0.11+9 +[info] # VM invoker: /usr/lib/jvm/temurin-17-jdk-amd64/bin/java +[info] # VM options: +[info] # Blackhole mode: full + dont-inline hint +[info] # Warmup: 5 iterations, 10 s each +[info] # Measurement: 5 iterations, 10 s each +[info] # Timeout: 10 min per iteration +[info] # Threads: 1 thread, will synchronize iterations +[info] # Benchmark mode: Average time, time/op +[info] # Benchmark: benchmark.RegexBenchmark.abStar_accepting_regex_mem +[info] # Parameters: (size = 70) +[info] # Run progress: 20.63% complete, ETA 03:32:32 +[info] # Fork: 1 of 1 +[info] # Warmup Iteration 1: 11240.312 us/op +[info] # Warmup Iteration 2: 10895.878 us/op +[info] # Warmup Iteration 3: 10893.084 us/op +[info] # Warmup Iteration 4: 10664.743 us/op +[info] # Warmup Iteration 5: 10300.694 us/op +[info] Iteration 1: 10323.206 us/op +[info] Iteration 2: 10329.788 us/op +[info] Iteration 3: 10316.453 us/op +[info] Iteration 4: 10320.956 us/op +[info] Iteration 5: 10318.997 us/op +[info] Result "benchmark.RegexBenchmark.abStar_accepting_regex_mem": +[info] 10321.880 ±(99.9%) 19.532 us/op [Average] +[info] (min, avg, max) = (10316.453, 10321.880, 10329.788), stdev = 5.072 +[info] CI (99.9%): [10302.348, 10341.412] (assumes normal distribution) +[info] # JMH version: 1.32 +[info] # VM version: JDK 17.0.11, OpenJDK 64-Bit Server VM, 17.0.11+9 +[info] # VM invoker: /usr/lib/jvm/temurin-17-jdk-amd64/bin/java +[info] # VM options: +[info] # Blackhole mode: full + dont-inline hint +[info] # Warmup: 5 iterations, 10 s each +[info] # Measurement: 5 iterations, 10 s each +[info] # Timeout: 10 min per iteration +[info] # Threads: 1 thread, will synchronize iterations +[info] # Benchmark mode: Average time, time/op +[info] # Benchmark: benchmark.RegexBenchmark.abStar_accepting_regex_mem +[info] # Parameters: (size = 75) +[info] # Run progress: 21.25% complete, ETA 03:30:52 +[info] # Fork: 1 of 1 +[info] # Warmup Iteration 1: 13535.347 us/op +[info] # Warmup Iteration 2: 13077.899 us/op +[info] # Warmup Iteration 3: 13068.392 us/op +[info] # Warmup Iteration 4: 12941.220 us/op +[info] # Warmup Iteration 5: 12321.522 us/op +[info] Iteration 1: 12322.787 us/op +[info] Iteration 2: 12326.612 us/op +[info] Iteration 3: 12311.573 us/op +[info] Iteration 4: 12286.670 us/op +[info] Iteration 5: 12269.942 us/op +[info] Result "benchmark.RegexBenchmark.abStar_accepting_regex_mem": +[info] 12303.517 ±(99.9%) 93.941 us/op [Average] +[info] (min, avg, max) = (12269.942, 12303.517, 12326.612), stdev = 24.396 +[info] CI (99.9%): [12209.576, 12397.457] (assumes normal distribution) +[info] # JMH version: 1.32 +[info] # VM version: JDK 17.0.11, OpenJDK 64-Bit Server VM, 17.0.11+9 +[info] # VM invoker: /usr/lib/jvm/temurin-17-jdk-amd64/bin/java +[info] # VM options: +[info] # Blackhole mode: full + dont-inline hint +[info] # Warmup: 5 iterations, 10 s each +[info] # Measurement: 5 iterations, 10 s each +[info] # Timeout: 10 min per iteration +[info] # Threads: 1 thread, will synchronize iterations +[info] # Benchmark mode: Average time, time/op +[info] # Benchmark: benchmark.RegexBenchmark.abStar_accepting_regex_mem +[info] # Parameters: (size = 80) +[info] # Run progress: 21.88% complete, ETA 03:29:11 +[info] # Fork: 1 of 1 +[info] # Warmup Iteration 1: 16457.726 us/op +[info] # Warmup Iteration 2: 15853.245 us/op +[info] # Warmup Iteration 3: 15883.293 us/op +[info] # Warmup Iteration 4: 15897.341 us/op +[info] # Warmup Iteration 5: 15429.948 us/op +[info] Iteration 1: 15288.583 us/op +[info] Iteration 2: 15298.622 us/op +[info] Iteration 3: 15290.565 us/op +[info] Iteration 4: 14939.219 us/op +[info] Iteration 5: 14895.260 us/op +[info] Result "benchmark.RegexBenchmark.abStar_accepting_regex_mem": +[info] 15142.450 ±(99.9%) 794.036 us/op [Average] +[info] (min, avg, max) = (14895.260, 15142.450, 15298.622), stdev = 206.209 +[info] CI (99.9%): [14348.414, 15936.486] (assumes normal distribution) +[info] # JMH version: 1.32 +[info] # VM version: JDK 17.0.11, OpenJDK 64-Bit Server VM, 17.0.11+9 +[info] # VM invoker: /usr/lib/jvm/temurin-17-jdk-amd64/bin/java +[info] # VM options: +[info] # Blackhole mode: full + dont-inline hint +[info] # Warmup: 5 iterations, 10 s each +[info] # Measurement: 5 iterations, 10 s each +[info] # Timeout: 10 min per iteration +[info] # Threads: 1 thread, will synchronize iterations +[info] # Benchmark mode: Average time, time/op +[info] # Benchmark: benchmark.RegexBenchmark.abStar_accepting_regex_mem +[info] # Parameters: (size = 85) +[info] # Run progress: 22.50% complete, ETA 03:27:31 +[info] # Fork: 1 of 1 +[info] # Warmup Iteration 1: 19822.674 us/op +[info] # Warmup Iteration 2: 19093.638 us/op +[info] # Warmup Iteration 3: 19103.663 us/op +[info] # Warmup Iteration 4: 19121.169 us/op +[info] # Warmup Iteration 5: 18874.789 us/op +[info] Iteration 1: 17859.632 us/op +[info] Iteration 2: 17802.157 us/op +[info] Iteration 3: 17750.810 us/op +[info] Iteration 4: 17788.071 us/op +[info] Iteration 5: 17793.401 us/op +[info] Result "benchmark.RegexBenchmark.abStar_accepting_regex_mem": +[info] 17798.814 ±(99.9%) 151.100 us/op [Average] +[info] (min, avg, max) = (17750.810, 17798.814, 17859.632), stdev = 39.240 +[info] CI (99.9%): [17647.715, 17949.914] (assumes normal distribution) +[info] # JMH version: 1.32 +[info] # VM version: JDK 17.0.11, OpenJDK 64-Bit Server VM, 17.0.11+9 +[info] # VM invoker: /usr/lib/jvm/temurin-17-jdk-amd64/bin/java +[info] # VM options: +[info] # Blackhole mode: full + dont-inline hint +[info] # Warmup: 5 iterations, 10 s each +[info] # Measurement: 5 iterations, 10 s each +[info] # Timeout: 10 min per iteration +[info] # Threads: 1 thread, will synchronize iterations +[info] # Benchmark mode: Average time, time/op +[info] # Benchmark: benchmark.RegexBenchmark.abStar_accepting_regex_mem +[info] # Parameters: (size = 90) +[info] # Run progress: 23.13% complete, ETA 03:25:51 +[info] # Fork: 1 of 1 +[info] # Warmup Iteration 1: 23200.695 us/op +[info] # Warmup Iteration 2: 22316.740 us/op +[info] # Warmup Iteration 3: 22302.344 us/op +[info] # Warmup Iteration 4: 22312.888 us/op +[info] # Warmup Iteration 5: 22337.557 us/op +[info] Iteration 1: 21467.180 us/op +[info] Iteration 2: 20761.996 us/op +[info] Iteration 3: 20808.484 us/op +[info] Iteration 4: 20763.084 us/op +[info] Iteration 5: 20703.560 us/op +[info] Result "benchmark.RegexBenchmark.abStar_accepting_regex_mem": +[info] 20900.861 ±(99.9%) 1227.450 us/op [Average] +[info] (min, avg, max) = (20703.560, 20900.861, 21467.180), stdev = 318.765 +[info] CI (99.9%): [19673.410, 22128.311] (assumes normal distribution) +[info] # JMH version: 1.32 +[info] # VM version: JDK 17.0.11, OpenJDK 64-Bit Server VM, 17.0.11+9 +[info] # VM invoker: /usr/lib/jvm/temurin-17-jdk-amd64/bin/java +[info] # VM options: +[info] # Blackhole mode: full + dont-inline hint +[info] # Warmup: 5 iterations, 10 s each +[info] # Measurement: 5 iterations, 10 s each +[info] # Timeout: 10 min per iteration +[info] # Threads: 1 thread, will synchronize iterations +[info] # Benchmark mode: Average time, time/op +[info] # Benchmark: benchmark.RegexBenchmark.abStar_accepting_regex_mem +[info] # Parameters: (size = 95) +[info] # Run progress: 23.75% complete, ETA 03:24:10 +[info] # Fork: 1 of 1 +[info] # Warmup Iteration 1: 27179.291 us/op +[info] # Warmup Iteration 2: 25993.547 us/op +[info] # Warmup Iteration 3: 26333.724 us/op +[info] # Warmup Iteration 4: 26819.910 us/op +[info] # Warmup Iteration 5: 26617.269 us/op +[info] Iteration 1: 26212.894 us/op +[info] Iteration 2: 24304.420 us/op +[info] Iteration 3: 24323.173 us/op +[info] Iteration 4: 24297.885 us/op +[info] Iteration 5: 24322.680 us/op +[info] Result "benchmark.RegexBenchmark.abStar_accepting_regex_mem": +[info] 24692.210 ±(99.9%) 3273.667 us/op [Average] +[info] (min, avg, max) = (24297.885, 24692.210, 26212.894), stdev = 850.161 +[info] CI (99.9%): [21418.543, 27965.878] (assumes normal distribution) +[info] # JMH version: 1.32 +[info] # VM version: JDK 17.0.11, OpenJDK 64-Bit Server VM, 17.0.11+9 +[info] # VM invoker: /usr/lib/jvm/temurin-17-jdk-amd64/bin/java +[info] # VM options: +[info] # Blackhole mode: full + dont-inline hint +[info] # Warmup: 5 iterations, 10 s each +[info] # Measurement: 5 iterations, 10 s each +[info] # Timeout: 10 min per iteration +[info] # Threads: 1 thread, will synchronize iterations +[info] # Benchmark mode: Average time, time/op +[info] # Benchmark: benchmark.RegexBenchmark.abStar_accepting_regex_mem +[info] # Parameters: (size = 100) +[info] # Run progress: 24.38% complete, ETA 03:22:30 +[info] # Fork: 1 of 1 +[info] # Warmup Iteration 1: 31713.365 us/op +[info] # Warmup Iteration 2: 30169.910 us/op +[info] # Warmup Iteration 3: 30153.767 us/op +[info] # Warmup Iteration 4: 30153.784 us/op +[info] # Warmup Iteration 5: 30261.883 us/op +[info] Iteration 1: 30257.357 us/op +[info] Iteration 2: 28608.962 us/op +[info] Iteration 3: 28202.618 us/op +[info] Iteration 4: 28114.364 us/op +[info] Iteration 5: 28118.413 us/op +[info] Result "benchmark.RegexBenchmark.abStar_accepting_regex_mem": +[info] 28660.343 ±(99.9%) 3526.225 us/op [Average] +[info] (min, avg, max) = (28114.364, 28660.343, 30257.357), stdev = 915.749 +[info] CI (99.9%): [25134.118, 32186.568] (assumes normal distribution) +[info] # JMH version: 1.32 +[info] # VM version: JDK 17.0.11, OpenJDK 64-Bit Server VM, 17.0.11+9 +[info] # VM invoker: /usr/lib/jvm/temurin-17-jdk-amd64/bin/java +[info] # VM options: +[info] # Blackhole mode: full + dont-inline hint +[info] # Warmup: 5 iterations, 10 s each +[info] # Measurement: 5 iterations, 10 s each +[info] # Timeout: 10 min per iteration +[info] # Threads: 1 thread, will synchronize iterations +[info] # Benchmark mode: Average time, time/op +[info] # Benchmark: benchmark.RegexBenchmark.abStar_accepting_zipper +[info] # Parameters: (size = 5) +[info] # Run progress: 25.00% complete, ETA 03:20:50 +[info] # Fork: 1 of 1 +[info] # Warmup Iteration 1: 1.177 us/op +[info] # Warmup Iteration 2: 1.080 us/op +[info] # Warmup Iteration 3: 1.081 us/op +[info] # Warmup Iteration 4: 1.067 us/op +[info] # Warmup Iteration 5: 1.069 us/op +[info] Iteration 1: 1.079 us/op +[info] Iteration 2: 1.058 us/op +[info] Iteration 3: 1.080 us/op +[info] Iteration 4: 1.057 us/op +[info] Iteration 5: 1.073 us/op +[info] Result "benchmark.RegexBenchmark.abStar_accepting_zipper": +[info] 1.069 ±(99.9%) 0.043 us/op [Average] +[info] (min, avg, max) = (1.057, 1.069, 1.080), stdev = 0.011 +[info] CI (99.9%): [1.026, 1.113] (assumes normal distribution) +[info] # JMH version: 1.32 +[info] # VM version: JDK 17.0.11, OpenJDK 64-Bit Server VM, 17.0.11+9 +[info] # VM invoker: /usr/lib/jvm/temurin-17-jdk-amd64/bin/java +[info] # VM options: +[info] # Blackhole mode: full + dont-inline hint +[info] # Warmup: 5 iterations, 10 s each +[info] # Measurement: 5 iterations, 10 s each +[info] # Timeout: 10 min per iteration +[info] # Threads: 1 thread, will synchronize iterations +[info] # Benchmark mode: Average time, time/op +[info] # Benchmark: benchmark.RegexBenchmark.abStar_accepting_zipper +[info] # Parameters: (size = 10) +[info] # Run progress: 25.62% complete, ETA 03:19:09 +[info] # Fork: 1 of 1 +[info] # Warmup Iteration 1: 2.354 us/op +[info] # Warmup Iteration 2: 2.167 us/op +[info] # Warmup Iteration 3: 2.190 us/op +[info] # Warmup Iteration 4: 2.205 us/op +[info] # Warmup Iteration 5: 2.180 us/op +[info] Iteration 1: 2.168 us/op +[info] Iteration 2: 2.122 us/op +[info] Iteration 3: 2.172 us/op +[info] Iteration 4: 2.122 us/op +[info] Iteration 5: 2.156 us/op +[info] Result "benchmark.RegexBenchmark.abStar_accepting_zipper": +[info] 2.148 ±(99.9%) 0.094 us/op [Average] +[info] (min, avg, max) = (2.122, 2.148, 2.172), stdev = 0.024 +[info] CI (99.9%): [2.054, 2.242] (assumes normal distribution) +[info] # JMH version: 1.32 +[info] # VM version: JDK 17.0.11, OpenJDK 64-Bit Server VM, 17.0.11+9 +[info] # VM invoker: /usr/lib/jvm/temurin-17-jdk-amd64/bin/java +[info] # VM options: +[info] # Blackhole mode: full + dont-inline hint +[info] # Warmup: 5 iterations, 10 s each +[info] # Measurement: 5 iterations, 10 s each +[info] # Timeout: 10 min per iteration +[info] # Threads: 1 thread, will synchronize iterations +[info] # Benchmark mode: Average time, time/op +[info] # Benchmark: benchmark.RegexBenchmark.abStar_accepting_zipper +[info] # Parameters: (size = 15) +[info] # Run progress: 26.25% complete, ETA 03:17:29 +[info] # Fork: 1 of 1 +[info] # Warmup Iteration 1: 3.697 us/op +[info] # Warmup Iteration 2: 3.432 us/op +[info] # Warmup Iteration 3: 3.450 us/op +[info] # Warmup Iteration 4: 3.440 us/op +[info] # Warmup Iteration 5: 3.391 us/op +[info] Iteration 1: 3.410 us/op +[info] Iteration 2: 3.419 us/op +[info] Iteration 3: 3.387 us/op +[info] Iteration 4: 3.417 us/op +[info] Iteration 5: 3.382 us/op +[info] Result "benchmark.RegexBenchmark.abStar_accepting_zipper": +[info] 3.403 ±(99.9%) 0.067 us/op [Average] +[info] (min, avg, max) = (3.382, 3.403, 3.419), stdev = 0.017 +[info] CI (99.9%): [3.336, 3.469] (assumes normal distribution) +[info] # JMH version: 1.32 +[info] # VM version: JDK 17.0.11, OpenJDK 64-Bit Server VM, 17.0.11+9 +[info] # VM invoker: /usr/lib/jvm/temurin-17-jdk-amd64/bin/java +[info] # VM options: +[info] # Blackhole mode: full + dont-inline hint +[info] # Warmup: 5 iterations, 10 s each +[info] # Measurement: 5 iterations, 10 s each +[info] # Timeout: 10 min per iteration +[info] # Threads: 1 thread, will synchronize iterations +[info] # Benchmark mode: Average time, time/op +[info] # Benchmark: benchmark.RegexBenchmark.abStar_accepting_zipper +[info] # Parameters: (size = 20) +[info] # Run progress: 26.88% complete, ETA 03:15:49 +[info] # Fork: 1 of 1 +[info] # Warmup Iteration 1: 4.614 us/op +[info] # Warmup Iteration 2: 4.300 us/op +[info] # Warmup Iteration 3: 4.318 us/op +[info] # Warmup Iteration 4: 4.311 us/op +[info] # Warmup Iteration 5: 4.332 us/op +[info] Iteration 1: 4.258 us/op +[info] Iteration 2: 4.315 us/op +[info] Iteration 3: 4.247 us/op +[info] Iteration 4: 4.285 us/op +[info] Iteration 5: 4.284 us/op +[info] Result "benchmark.RegexBenchmark.abStar_accepting_zipper": +[info] 4.278 ±(99.9%) 0.103 us/op [Average] +[info] (min, avg, max) = (4.247, 4.278, 4.315), stdev = 0.027 +[info] CI (99.9%): [4.175, 4.381] (assumes normal distribution) +[info] # JMH version: 1.32 +[info] # VM version: JDK 17.0.11, OpenJDK 64-Bit Server VM, 17.0.11+9 +[info] # VM invoker: /usr/lib/jvm/temurin-17-jdk-amd64/bin/java +[info] # VM options: +[info] # Blackhole mode: full + dont-inline hint +[info] # Warmup: 5 iterations, 10 s each +[info] # Measurement: 5 iterations, 10 s each +[info] # Timeout: 10 min per iteration +[info] # Threads: 1 thread, will synchronize iterations +[info] # Benchmark mode: Average time, time/op +[info] # Benchmark: benchmark.RegexBenchmark.abStar_accepting_zipper +[info] # Parameters: (size = 25) +[info] # Run progress: 27.50% complete, ETA 03:14:08 +[info] # Fork: 1 of 1 +[info] # Warmup Iteration 1: 6.157 us/op +[info] # Warmup Iteration 2: 5.603 us/op +[info] # Warmup Iteration 3: 5.653 us/op +[info] # Warmup Iteration 4: 5.577 us/op +[info] # Warmup Iteration 5: 5.615 us/op +[info] Iteration 1: 5.691 us/op +[info] Iteration 2: 5.625 us/op +[info] Iteration 3: 5.595 us/op +[info] Iteration 4: 5.618 us/op +[info] Iteration 5: 5.586 us/op +[info] Result "benchmark.RegexBenchmark.abStar_accepting_zipper": +[info] 5.623 ±(99.9%) 0.159 us/op [Average] +[info] (min, avg, max) = (5.586, 5.623, 5.691), stdev = 0.041 +[info] CI (99.9%): [5.464, 5.782] (assumes normal distribution) +[info] # JMH version: 1.32 +[info] # VM version: JDK 17.0.11, OpenJDK 64-Bit Server VM, 17.0.11+9 +[info] # VM invoker: /usr/lib/jvm/temurin-17-jdk-amd64/bin/java +[info] # VM options: +[info] # Blackhole mode: full + dont-inline hint +[info] # Warmup: 5 iterations, 10 s each +[info] # Measurement: 5 iterations, 10 s each +[info] # Timeout: 10 min per iteration +[info] # Threads: 1 thread, will synchronize iterations +[info] # Benchmark mode: Average time, time/op +[info] # Benchmark: benchmark.RegexBenchmark.abStar_accepting_zipper +[info] # Parameters: (size = 30) +[info] # Run progress: 28.13% complete, ETA 03:12:28 +[info] # Fork: 1 of 1 +[info] # Warmup Iteration 1: 7.079 us/op +[info] # Warmup Iteration 2: 6.680 us/op +[info] # Warmup Iteration 3: 6.720 us/op +[info] # Warmup Iteration 4: 6.688 us/op +[info] # Warmup Iteration 5: 6.713 us/op +[info] Iteration 1: 6.603 us/op +[info] Iteration 2: 6.679 us/op +[info] Iteration 3: 6.561 us/op +[info] Iteration 4: 6.626 us/op +[info] Iteration 5: 6.605 us/op +[info] Result "benchmark.RegexBenchmark.abStar_accepting_zipper": +[info] 6.615 ±(99.9%) 0.165 us/op [Average] +[info] (min, avg, max) = (6.561, 6.615, 6.679), stdev = 0.043 +[info] CI (99.9%): [6.449, 6.780] (assumes normal distribution) +[info] # JMH version: 1.32 +[info] # VM version: JDK 17.0.11, OpenJDK 64-Bit Server VM, 17.0.11+9 +[info] # VM invoker: /usr/lib/jvm/temurin-17-jdk-amd64/bin/java +[info] # VM options: +[info] # Blackhole mode: full + dont-inline hint +[info] # Warmup: 5 iterations, 10 s each +[info] # Measurement: 5 iterations, 10 s each +[info] # Timeout: 10 min per iteration +[info] # Threads: 1 thread, will synchronize iterations +[info] # Benchmark mode: Average time, time/op +[info] # Benchmark: benchmark.RegexBenchmark.abStar_accepting_zipper +[info] # Parameters: (size = 35) +[info] # Run progress: 28.75% complete, ETA 03:10:47 +[info] # Fork: 1 of 1 +[info] # Warmup Iteration 1: 8.415 us/op +[info] # Warmup Iteration 2: 7.773 us/op +[info] # Warmup Iteration 3: 7.868 us/op +[info] # Warmup Iteration 4: 7.833 us/op +[info] # Warmup Iteration 5: 7.771 us/op +[info] Iteration 1: 7.896 us/op +[info] Iteration 2: 7.723 us/op +[info] Iteration 3: 7.814 us/op +[info] Iteration 4: 7.862 us/op +[info] Iteration 5: 7.772 us/op +[info] Result "benchmark.RegexBenchmark.abStar_accepting_zipper": +[info] 7.813 ±(99.9%) 0.265 us/op [Average] +[info] (min, avg, max) = (7.723, 7.813, 7.896), stdev = 0.069 +[info] CI (99.9%): [7.548, 8.079] (assumes normal distribution) +[info] # JMH version: 1.32 +[info] # VM version: JDK 17.0.11, OpenJDK 64-Bit Server VM, 17.0.11+9 +[info] # VM invoker: /usr/lib/jvm/temurin-17-jdk-amd64/bin/java +[info] # VM options: +[info] # Blackhole mode: full + dont-inline hint +[info] # Warmup: 5 iterations, 10 s each +[info] # Measurement: 5 iterations, 10 s each +[info] # Timeout: 10 min per iteration +[info] # Threads: 1 thread, will synchronize iterations +[info] # Benchmark mode: Average time, time/op +[info] # Benchmark: benchmark.RegexBenchmark.abStar_accepting_zipper +[info] # Parameters: (size = 40) +[info] # Run progress: 29.38% complete, ETA 03:09:07 +[info] # Fork: 1 of 1 +[info] # Warmup Iteration 1: 9.668 us/op +[info] # Warmup Iteration 2: 8.933 us/op +[info] # Warmup Iteration 3: 9.135 us/op +[info] # Warmup Iteration 4: 9.036 us/op +[info] # Warmup Iteration 5: 8.914 us/op +[info] Iteration 1: 9.053 us/op +[info] Iteration 2: 8.858 us/op +[info] Iteration 3: 9.049 us/op +[info] Iteration 4: 8.856 us/op +[info] Iteration 5: 9.014 us/op +[info] Result "benchmark.RegexBenchmark.abStar_accepting_zipper": +[info] 8.966 ±(99.9%) 0.387 us/op [Average] +[info] (min, avg, max) = (8.856, 8.966, 9.053), stdev = 0.101 +[info] CI (99.9%): [8.579, 9.353] (assumes normal distribution) +[info] # JMH version: 1.32 +[info] # VM version: JDK 17.0.11, OpenJDK 64-Bit Server VM, 17.0.11+9 +[info] # VM invoker: /usr/lib/jvm/temurin-17-jdk-amd64/bin/java +[info] # VM options: +[info] # Blackhole mode: full + dont-inline hint +[info] # Warmup: 5 iterations, 10 s each +[info] # Measurement: 5 iterations, 10 s each +[info] # Timeout: 10 min per iteration +[info] # Threads: 1 thread, will synchronize iterations +[info] # Benchmark mode: Average time, time/op +[info] # Benchmark: benchmark.RegexBenchmark.abStar_accepting_zipper +[info] # Parameters: (size = 45) +[info] # Run progress: 30.00% complete, ETA 03:07:26 +[info] # Fork: 1 of 1 +[info] # Warmup Iteration 1: 11.092 us/op +[info] # Warmup Iteration 2: 10.293 us/op +[info] # Warmup Iteration 3: 10.244 us/op +[info] # Warmup Iteration 4: 10.113 us/op +[info] # Warmup Iteration 5: 10.161 us/op +[info] Iteration 1: 10.266 us/op +[info] Iteration 2: 10.003 us/op +[info] Iteration 3: 10.125 us/op +[info] Iteration 4: 10.227 us/op +[info] Iteration 5: 10.040 us/op +[info] Result "benchmark.RegexBenchmark.abStar_accepting_zipper": +[info] 10.132 ±(99.9%) 0.440 us/op [Average] +[info] (min, avg, max) = (10.003, 10.132, 10.266), stdev = 0.114 +[info] CI (99.9%): [9.693, 10.572] (assumes normal distribution) +[info] # JMH version: 1.32 +[info] # VM version: JDK 17.0.11, OpenJDK 64-Bit Server VM, 17.0.11+9 +[info] # VM invoker: /usr/lib/jvm/temurin-17-jdk-amd64/bin/java +[info] # VM options: +[info] # Blackhole mode: full + dont-inline hint +[info] # Warmup: 5 iterations, 10 s each +[info] # Measurement: 5 iterations, 10 s each +[info] # Timeout: 10 min per iteration +[info] # Threads: 1 thread, will synchronize iterations +[info] # Benchmark mode: Average time, time/op +[info] # Benchmark: benchmark.RegexBenchmark.abStar_accepting_zipper +[info] # Parameters: (size = 50) +[info] # Run progress: 30.63% complete, ETA 03:05:46 +[info] # Fork: 1 of 1 +[info] # Warmup Iteration 1: 12.312 us/op +[info] # Warmup Iteration 2: 11.536 us/op +[info] # Warmup Iteration 3: 11.432 us/op +[info] # Warmup Iteration 4: 11.478 us/op +[info] # Warmup Iteration 5: 11.237 us/op +[info] Iteration 1: 11.280 us/op +[info] Iteration 2: 11.498 us/op +[info] Iteration 3: 11.321 us/op +[info] Iteration 4: 11.272 us/op +[info] Iteration 5: 11.296 us/op +[info] Result "benchmark.RegexBenchmark.abStar_accepting_zipper": +[info] 11.333 ±(99.9%) 0.361 us/op [Average] +[info] (min, avg, max) = (11.272, 11.333, 11.498), stdev = 0.094 +[info] CI (99.9%): [10.972, 11.695] (assumes normal distribution) +[info] # JMH version: 1.32 +[info] # VM version: JDK 17.0.11, OpenJDK 64-Bit Server VM, 17.0.11+9 +[info] # VM invoker: /usr/lib/jvm/temurin-17-jdk-amd64/bin/java +[info] # VM options: +[info] # Blackhole mode: full + dont-inline hint +[info] # Warmup: 5 iterations, 10 s each +[info] # Measurement: 5 iterations, 10 s each +[info] # Timeout: 10 min per iteration +[info] # Threads: 1 thread, will synchronize iterations +[info] # Benchmark mode: Average time, time/op +[info] # Benchmark: benchmark.RegexBenchmark.abStar_accepting_zipper +[info] # Parameters: (size = 55) +[info] # Run progress: 31.25% complete, ETA 03:04:05 +[info] # Fork: 1 of 1 +[info] # Warmup Iteration 1: 13.952 us/op +[info] # Warmup Iteration 2: 12.775 us/op +[info] # Warmup Iteration 3: 12.810 us/op +[info] # Warmup Iteration 4: 12.771 us/op +[info] # Warmup Iteration 5: 12.831 us/op +[info] Iteration 1: 13.037 us/op +[info] Iteration 2: 12.852 us/op +[info] Iteration 3: 12.877 us/op +[info] Iteration 4: 12.773 us/op +[info] Iteration 5: 12.563 us/op +[info] Result "benchmark.RegexBenchmark.abStar_accepting_zipper": +[info] 12.820 ±(99.9%) 0.667 us/op [Average] +[info] (min, avg, max) = (12.563, 12.820, 13.037), stdev = 0.173 +[info] CI (99.9%): [12.154, 13.487] (assumes normal distribution) +[info] # JMH version: 1.32 +[info] # VM version: JDK 17.0.11, OpenJDK 64-Bit Server VM, 17.0.11+9 +[info] # VM invoker: /usr/lib/jvm/temurin-17-jdk-amd64/bin/java +[info] # VM options: +[info] # Blackhole mode: full + dont-inline hint +[info] # Warmup: 5 iterations, 10 s each +[info] # Measurement: 5 iterations, 10 s each +[info] # Timeout: 10 min per iteration +[info] # Threads: 1 thread, will synchronize iterations +[info] # Benchmark mode: Average time, time/op +[info] # Benchmark: benchmark.RegexBenchmark.abStar_accepting_zipper +[info] # Parameters: (size = 60) +[info] # Run progress: 31.87% complete, ETA 03:02:25 +[info] # Fork: 1 of 1 +[info] # Warmup Iteration 1: 14.849 us/op +[info] # Warmup Iteration 2: 13.703 us/op +[info] # Warmup Iteration 3: 13.618 us/op +[info] # Warmup Iteration 4: 13.566 us/op +[info] # Warmup Iteration 5: 13.646 us/op +[info] Iteration 1: 13.712 us/op +[info] Iteration 2: 13.446 us/op +[info] Iteration 3: 13.686 us/op +[info] Iteration 4: 13.551 us/op +[info] Iteration 5: 13.581 us/op +[info] Result "benchmark.RegexBenchmark.abStar_accepting_zipper": +[info] 13.595 ±(99.9%) 0.414 us/op [Average] +[info] (min, avg, max) = (13.446, 13.595, 13.712), stdev = 0.108 +[info] CI (99.9%): [13.181, 14.009] (assumes normal distribution) +[info] # JMH version: 1.32 +[info] # VM version: JDK 17.0.11, OpenJDK 64-Bit Server VM, 17.0.11+9 +[info] # VM invoker: /usr/lib/jvm/temurin-17-jdk-amd64/bin/java +[info] # VM options: +[info] # Blackhole mode: full + dont-inline hint +[info] # Warmup: 5 iterations, 10 s each +[info] # Measurement: 5 iterations, 10 s each +[info] # Timeout: 10 min per iteration +[info] # Threads: 1 thread, will synchronize iterations +[info] # Benchmark mode: Average time, time/op +[info] # Benchmark: benchmark.RegexBenchmark.abStar_accepting_zipper +[info] # Parameters: (size = 65) +[info] # Run progress: 32.50% complete, ETA 03:00:44 +[info] # Fork: 1 of 1 +[info] # Warmup Iteration 1: 13.174 us/op +[info] # Warmup Iteration 2: 12.178 us/op +[info] # Warmup Iteration 3: 12.192 us/op +[info] # Warmup Iteration 4: 12.111 us/op +[info] # Warmup Iteration 5: 11.979 us/op +[info] Iteration 1: 12.157 us/op +[info] Iteration 2: 11.917 us/op +[info] Iteration 3: 12.171 us/op +[info] Iteration 4: 12.016 us/op +[info] Iteration 5: 12.072 us/op +[info] Result "benchmark.RegexBenchmark.abStar_accepting_zipper": +[info] 12.067 ±(99.9%) 0.404 us/op [Average] +[info] (min, avg, max) = (11.917, 12.067, 12.171), stdev = 0.105 +[info] CI (99.9%): [11.663, 12.470] (assumes normal distribution) +[info] # JMH version: 1.32 +[info] # VM version: JDK 17.0.11, OpenJDK 64-Bit Server VM, 17.0.11+9 +[info] # VM invoker: /usr/lib/jvm/temurin-17-jdk-amd64/bin/java +[info] # VM options: +[info] # Blackhole mode: full + dont-inline hint +[info] # Warmup: 5 iterations, 10 s each +[info] # Measurement: 5 iterations, 10 s each +[info] # Timeout: 10 min per iteration +[info] # Threads: 1 thread, will synchronize iterations +[info] # Benchmark mode: Average time, time/op +[info] # Benchmark: benchmark.RegexBenchmark.abStar_accepting_zipper +[info] # Parameters: (size = 70) +[info] # Run progress: 33.13% complete, ETA 02:59:04 +[info] # Fork: 1 of 1 +[info] # Warmup Iteration 1: 14.463 us/op +[info] # Warmup Iteration 2: 13.369 us/op +[info] # Warmup Iteration 3: 13.566 us/op +[info] # Warmup Iteration 4: 13.224 us/op +[info] # Warmup Iteration 5: 13.225 us/op +[info] Iteration 1: 13.407 us/op +[info] Iteration 2: 13.104 us/op +[info] Iteration 3: 13.155 us/op +[info] Iteration 4: 13.241 us/op +[info] Iteration 5: 13.074 us/op +[info] Result "benchmark.RegexBenchmark.abStar_accepting_zipper": +[info] 13.196 ±(99.9%) 0.515 us/op [Average] +[info] (min, avg, max) = (13.074, 13.196, 13.407), stdev = 0.134 +[info] CI (99.9%): [12.682, 13.711] (assumes normal distribution) +[info] # JMH version: 1.32 +[info] # VM version: JDK 17.0.11, OpenJDK 64-Bit Server VM, 17.0.11+9 +[info] # VM invoker: /usr/lib/jvm/temurin-17-jdk-amd64/bin/java +[info] # VM options: +[info] # Blackhole mode: full + dont-inline hint +[info] # Warmup: 5 iterations, 10 s each +[info] # Measurement: 5 iterations, 10 s each +[info] # Timeout: 10 min per iteration +[info] # Threads: 1 thread, will synchronize iterations +[info] # Benchmark mode: Average time, time/op +[info] # Benchmark: benchmark.RegexBenchmark.abStar_accepting_zipper +[info] # Parameters: (size = 75) +[info] # Run progress: 33.75% complete, ETA 02:57:24 +[info] # Fork: 1 of 1 +[info] # Warmup Iteration 1: 15.466 us/op +[info] # Warmup Iteration 2: 13.992 us/op +[info] # Warmup Iteration 3: 13.976 us/op +[info] # Warmup Iteration 4: 13.883 us/op +[info] # Warmup Iteration 5: 13.753 us/op +[info] Iteration 1: 13.931 us/op +[info] Iteration 2: 13.685 us/op +[info] Iteration 3: 13.878 us/op +[info] Iteration 4: 13.656 us/op +[info] Iteration 5: 13.865 us/op +[info] Result "benchmark.RegexBenchmark.abStar_accepting_zipper": +[info] 13.803 ±(99.9%) 0.478 us/op [Average] +[info] (min, avg, max) = (13.656, 13.803, 13.931), stdev = 0.124 +[info] CI (99.9%): [13.326, 14.281] (assumes normal distribution) +[info] # JMH version: 1.32 +[info] # VM version: JDK 17.0.11, OpenJDK 64-Bit Server VM, 17.0.11+9 +[info] # VM invoker: /usr/lib/jvm/temurin-17-jdk-amd64/bin/java +[info] # VM options: +[info] # Blackhole mode: full + dont-inline hint +[info] # Warmup: 5 iterations, 10 s each +[info] # Measurement: 5 iterations, 10 s each +[info] # Timeout: 10 min per iteration +[info] # Threads: 1 thread, will synchronize iterations +[info] # Benchmark mode: Average time, time/op +[info] # Benchmark: benchmark.RegexBenchmark.abStar_accepting_zipper +[info] # Parameters: (size = 80) +[info] # Run progress: 34.38% complete, ETA 02:55:43 +[info] # Fork: 1 of 1 +[info] # Warmup Iteration 1: 16.496 us/op +[info] # Warmup Iteration 2: 15.224 us/op +[info] # Warmup Iteration 3: 14.826 us/op +[info] # Warmup Iteration 4: 15.079 us/op +[info] # Warmup Iteration 5: 15.005 us/op +[info] Iteration 1: 15.087 us/op +[info] Iteration 2: 14.726 us/op +[info] Iteration 3: 15.074 us/op +[info] Iteration 4: 14.733 us/op +[info] Iteration 5: 14.972 us/op +[info] Result "benchmark.RegexBenchmark.abStar_accepting_zipper": +[info] 14.918 ±(99.9%) 0.685 us/op [Average] +[info] (min, avg, max) = (14.726, 14.918, 15.087), stdev = 0.178 +[info] CI (99.9%): [14.233, 15.604] (assumes normal distribution) +[info] # JMH version: 1.32 +[info] # VM version: JDK 17.0.11, OpenJDK 64-Bit Server VM, 17.0.11+9 +[info] # VM invoker: /usr/lib/jvm/temurin-17-jdk-amd64/bin/java +[info] # VM options: +[info] # Blackhole mode: full + dont-inline hint +[info] # Warmup: 5 iterations, 10 s each +[info] # Measurement: 5 iterations, 10 s each +[info] # Timeout: 10 min per iteration +[info] # Threads: 1 thread, will synchronize iterations +[info] # Benchmark mode: Average time, time/op +[info] # Benchmark: benchmark.RegexBenchmark.abStar_accepting_zipper +[info] # Parameters: (size = 85) +[info] # Run progress: 35.00% complete, ETA 02:54:03 +[info] # Fork: 1 of 1 +[info] # Warmup Iteration 1: 17.602 us/op +[info] # Warmup Iteration 2: 16.177 us/op +[info] # Warmup Iteration 3: 16.379 us/op +[info] # Warmup Iteration 4: 16.487 us/op +[info] # Warmup Iteration 5: 16.197 us/op +[info] Iteration 1: 16.565 us/op +[info] Iteration 2: 16.265 us/op +[info] Iteration 3: 16.155 us/op +[info] Iteration 4: 16.019 us/op +[info] Iteration 5: 16.078 us/op +[info] Result "benchmark.RegexBenchmark.abStar_accepting_zipper": +[info] 16.216 ±(99.9%) 0.831 us/op [Average] +[info] (min, avg, max) = (16.019, 16.216, 16.565), stdev = 0.216 +[info] CI (99.9%): [15.386, 17.047] (assumes normal distribution) +[info] # JMH version: 1.32 +[info] # VM version: JDK 17.0.11, OpenJDK 64-Bit Server VM, 17.0.11+9 +[info] # VM invoker: /usr/lib/jvm/temurin-17-jdk-amd64/bin/java +[info] # VM options: +[info] # Blackhole mode: full + dont-inline hint +[info] # Warmup: 5 iterations, 10 s each +[info] # Measurement: 5 iterations, 10 s each +[info] # Timeout: 10 min per iteration +[info] # Threads: 1 thread, will synchronize iterations +[info] # Benchmark mode: Average time, time/op +[info] # Benchmark: benchmark.RegexBenchmark.abStar_accepting_zipper +[info] # Parameters: (size = 90) +[info] # Run progress: 35.63% complete, ETA 02:52:22 +[info] # Fork: 1 of 1 +[info] # Warmup Iteration 1: 18.352 us/op +[info] # Warmup Iteration 2: 16.545 us/op +[info] # Warmup Iteration 3: 16.478 us/op +[info] # Warmup Iteration 4: 16.475 us/op +[info] # Warmup Iteration 5: 16.532 us/op +[info] Iteration 1: 16.637 us/op +[info] Iteration 2: 16.297 us/op +[info] Iteration 3: 16.584 us/op +[info] Iteration 4: 16.316 us/op +[info] Iteration 5: 16.512 us/op +[info] Result "benchmark.RegexBenchmark.abStar_accepting_zipper": +[info] 16.469 ±(99.9%) 0.596 us/op [Average] +[info] (min, avg, max) = (16.297, 16.469, 16.637), stdev = 0.155 +[info] CI (99.9%): [15.873, 17.066] (assumes normal distribution) +[info] # JMH version: 1.32 +[info] # VM version: JDK 17.0.11, OpenJDK 64-Bit Server VM, 17.0.11+9 +[info] # VM invoker: /usr/lib/jvm/temurin-17-jdk-amd64/bin/java +[info] # VM options: +[info] # Blackhole mode: full + dont-inline hint +[info] # Warmup: 5 iterations, 10 s each +[info] # Measurement: 5 iterations, 10 s each +[info] # Timeout: 10 min per iteration +[info] # Threads: 1 thread, will synchronize iterations +[info] # Benchmark mode: Average time, time/op +[info] # Benchmark: benchmark.RegexBenchmark.abStar_accepting_zipper +[info] # Parameters: (size = 95) +[info] # Run progress: 36.25% complete, ETA 02:50:42 +[info] # Fork: 1 of 1 +[info] # Warmup Iteration 1: 19.060 us/op +[info] # Warmup Iteration 2: 17.468 us/op +[info] # Warmup Iteration 3: 17.473 us/op +[info] # Warmup Iteration 4: 17.370 us/op +[info] # Warmup Iteration 5: 17.200 us/op +[info] Iteration 1: 17.455 us/op +[info] Iteration 2: 17.120 us/op +[info] Iteration 3: 17.407 us/op +[info] Iteration 4: 17.197 us/op +[info] Iteration 5: 17.270 us/op +[info] Result "benchmark.RegexBenchmark.abStar_accepting_zipper": +[info] 17.290 ±(99.9%) 0.540 us/op [Average] +[info] (min, avg, max) = (17.120, 17.290, 17.455), stdev = 0.140 +[info] CI (99.9%): [16.750, 17.830] (assumes normal distribution) +[info] # JMH version: 1.32 +[info] # VM version: JDK 17.0.11, OpenJDK 64-Bit Server VM, 17.0.11+9 +[info] # VM invoker: /usr/lib/jvm/temurin-17-jdk-amd64/bin/java +[info] # VM options: +[info] # Blackhole mode: full + dont-inline hint +[info] # Warmup: 5 iterations, 10 s each +[info] # Measurement: 5 iterations, 10 s each +[info] # Timeout: 10 min per iteration +[info] # Threads: 1 thread, will synchronize iterations +[info] # Benchmark mode: Average time, time/op +[info] # Benchmark: benchmark.RegexBenchmark.abStar_accepting_zipper +[info] # Parameters: (size = 100) +[info] # Run progress: 36.88% complete, ETA 02:49:02 +[info] # Fork: 1 of 1 +[info] # Warmup Iteration 1: 20.744 us/op +[info] # Warmup Iteration 2: 19.390 us/op +[info] # Warmup Iteration 3: 19.504 us/op +[info] # Warmup Iteration 4: 19.182 us/op +[info] # Warmup Iteration 5: 18.900 us/op +[info] Iteration 1: 19.258 us/op +[info] Iteration 2: 18.836 us/op +[info] Iteration 3: 19.159 us/op +[info] Iteration 4: 18.730 us/op +[info] Iteration 5: 18.906 us/op +[info] Result "benchmark.RegexBenchmark.abStar_accepting_zipper": +[info] 18.978 ±(99.9%) 0.857 us/op [Average] +[info] (min, avg, max) = (18.730, 18.978, 19.258), stdev = 0.222 +[info] CI (99.9%): [18.121, 19.835] (assumes normal distribution) +[info] # JMH version: 1.32 +[info] # VM version: JDK 17.0.11, OpenJDK 64-Bit Server VM, 17.0.11+9 +[info] # VM invoker: /usr/lib/jvm/temurin-17-jdk-amd64/bin/java +[info] # VM options: +[info] # Blackhole mode: full + dont-inline hint +[info] # Warmup: 5 iterations, 10 s each +[info] # Measurement: 5 iterations, 10 s each +[info] # Timeout: 10 min per iteration +[info] # Threads: 1 thread, will synchronize iterations +[info] # Benchmark mode: Average time, time/op +[info] # Benchmark: benchmark.RegexBenchmark.abStar_accepting_zipper_mem +[info] # Parameters: (size = 5) +[info] # Run progress: 37.50% complete, ETA 02:47:21 +[info] # Fork: 1 of 1 +[info] # Warmup Iteration 1: 2.878 us/op +[info] # Warmup Iteration 2: 2.706 us/op +[info] # Warmup Iteration 3: 2.683 us/op +[info] # Warmup Iteration 4: 2.667 us/op +[info] # Warmup Iteration 5: 2.697 us/op +[info] Iteration 1: 2.683 us/op +[info] Iteration 2: 2.669 us/op +[info] Iteration 3: 2.652 us/op +[info] Iteration 4: 2.690 us/op +[info] Iteration 5: 2.653 us/op +[info] Result "benchmark.RegexBenchmark.abStar_accepting_zipper_mem": +[info] 2.669 ±(99.9%) 0.066 us/op [Average] +[info] (min, avg, max) = (2.652, 2.669, 2.690), stdev = 0.017 +[info] CI (99.9%): [2.604, 2.735] (assumes normal distribution) +[info] # JMH version: 1.32 +[info] # VM version: JDK 17.0.11, OpenJDK 64-Bit Server VM, 17.0.11+9 +[info] # VM invoker: /usr/lib/jvm/temurin-17-jdk-amd64/bin/java +[info] # VM options: +[info] # Blackhole mode: full + dont-inline hint +[info] # Warmup: 5 iterations, 10 s each +[info] # Measurement: 5 iterations, 10 s each +[info] # Timeout: 10 min per iteration +[info] # Threads: 1 thread, will synchronize iterations +[info] # Benchmark mode: Average time, time/op +[info] # Benchmark: benchmark.RegexBenchmark.abStar_accepting_zipper_mem +[info] # Parameters: (size = 10) +[info] # Run progress: 38.13% complete, ETA 02:45:41 +[info] # Fork: 1 of 1 +[info] # Warmup Iteration 1: 5.884 us/op +[info] # Warmup Iteration 2: 5.584 us/op +[info] # Warmup Iteration 3: 5.511 us/op +[info] # Warmup Iteration 4: 5.478 us/op +[info] # Warmup Iteration 5: 5.478 us/op +[info] Iteration 1: 5.482 us/op +[info] Iteration 2: 5.473 us/op +[info] Iteration 3: 5.472 us/op +[info] Iteration 4: 5.483 us/op +[info] Iteration 5: 5.446 us/op +[info] Result "benchmark.RegexBenchmark.abStar_accepting_zipper_mem": +[info] 5.471 ±(99.9%) 0.057 us/op [Average] +[info] (min, avg, max) = (5.446, 5.471, 5.483), stdev = 0.015 +[info] CI (99.9%): [5.414, 5.528] (assumes normal distribution) +[info] # JMH version: 1.32 +[info] # VM version: JDK 17.0.11, OpenJDK 64-Bit Server VM, 17.0.11+9 +[info] # VM invoker: /usr/lib/jvm/temurin-17-jdk-amd64/bin/java +[info] # VM options: +[info] # Blackhole mode: full + dont-inline hint +[info] # Warmup: 5 iterations, 10 s each +[info] # Measurement: 5 iterations, 10 s each +[info] # Timeout: 10 min per iteration +[info] # Threads: 1 thread, will synchronize iterations +[info] # Benchmark mode: Average time, time/op +[info] # Benchmark: benchmark.RegexBenchmark.abStar_accepting_zipper_mem +[info] # Parameters: (size = 15) +[info] # Run progress: 38.75% complete, ETA 02:44:00 +[info] # Fork: 1 of 1 +[info] # Warmup Iteration 1: 8.894 us/op +[info] # Warmup Iteration 2: 8.323 us/op +[info] # Warmup Iteration 3: 8.265 us/op +[info] # Warmup Iteration 4: 8.270 us/op +[info] # Warmup Iteration 5: 8.208 us/op +[info] Iteration 1: 8.299 us/op +[info] Iteration 2: 8.204 us/op +[info] Iteration 3: 8.289 us/op +[info] Iteration 4: 8.158 us/op +[info] Iteration 5: 8.287 us/op +[info] Result "benchmark.RegexBenchmark.abStar_accepting_zipper_mem": +[info] 8.247 ±(99.9%) 0.241 us/op [Average] +[info] (min, avg, max) = (8.158, 8.247, 8.299), stdev = 0.063 +[info] CI (99.9%): [8.006, 8.489] (assumes normal distribution) +[info] # JMH version: 1.32 +[info] # VM version: JDK 17.0.11, OpenJDK 64-Bit Server VM, 17.0.11+9 +[info] # VM invoker: /usr/lib/jvm/temurin-17-jdk-amd64/bin/java +[info] # VM options: +[info] # Blackhole mode: full + dont-inline hint +[info] # Warmup: 5 iterations, 10 s each +[info] # Measurement: 5 iterations, 10 s each +[info] # Timeout: 10 min per iteration +[info] # Threads: 1 thread, will synchronize iterations +[info] # Benchmark mode: Average time, time/op +[info] # Benchmark: benchmark.RegexBenchmark.abStar_accepting_zipper_mem +[info] # Parameters: (size = 20) +[info] # Run progress: 39.38% complete, ETA 02:42:20 +[info] # Fork: 1 of 1 +[info] # Warmup Iteration 1: 11.352 us/op +[info] # Warmup Iteration 2: 10.664 us/op +[info] # Warmup Iteration 3: 10.552 us/op +[info] # Warmup Iteration 4: 10.675 us/op +[info] # Warmup Iteration 5: 10.516 us/op +[info] Iteration 1: 10.548 us/op +[info] Iteration 2: 10.661 us/op +[info] Iteration 3: 10.500 us/op +[info] Iteration 4: 10.560 us/op +[info] Iteration 5: 10.598 us/op +[info] Result "benchmark.RegexBenchmark.abStar_accepting_zipper_mem": +[info] 10.573 ±(99.9%) 0.231 us/op [Average] +[info] (min, avg, max) = (10.500, 10.573, 10.661), stdev = 0.060 +[info] CI (99.9%): [10.342, 10.804] (assumes normal distribution) +[info] # JMH version: 1.32 +[info] # VM version: JDK 17.0.11, OpenJDK 64-Bit Server VM, 17.0.11+9 +[info] # VM invoker: /usr/lib/jvm/temurin-17-jdk-amd64/bin/java +[info] # VM options: +[info] # Blackhole mode: full + dont-inline hint +[info] # Warmup: 5 iterations, 10 s each +[info] # Measurement: 5 iterations, 10 s each +[info] # Timeout: 10 min per iteration +[info] # Threads: 1 thread, will synchronize iterations +[info] # Benchmark mode: Average time, time/op +[info] # Benchmark: benchmark.RegexBenchmark.abStar_accepting_zipper_mem +[info] # Parameters: (size = 25) +[info] # Run progress: 40.00% complete, ETA 02:40:40 +[info] # Fork: 1 of 1 +[info] # Warmup Iteration 1: 15.246 us/op +[info] # Warmup Iteration 2: 14.395 us/op +[info] # Warmup Iteration 3: 14.433 us/op +[info] # Warmup Iteration 4: 14.447 us/op +[info] # Warmup Iteration 5: 14.139 us/op +[info] Iteration 1: 14.364 us/op +[info] Iteration 2: 14.186 us/op +[info] Iteration 3: 14.176 us/op +[info] Iteration 4: 14.179 us/op +[info] Iteration 5: 14.186 us/op +[info] Result "benchmark.RegexBenchmark.abStar_accepting_zipper_mem": +[info] 14.218 ±(99.9%) 0.314 us/op [Average] +[info] (min, avg, max) = (14.176, 14.218, 14.364), stdev = 0.081 +[info] CI (99.9%): [13.905, 14.532] (assumes normal distribution) +[info] # JMH version: 1.32 +[info] # VM version: JDK 17.0.11, OpenJDK 64-Bit Server VM, 17.0.11+9 +[info] # VM invoker: /usr/lib/jvm/temurin-17-jdk-amd64/bin/java +[info] # VM options: +[info] # Blackhole mode: full + dont-inline hint +[info] # Warmup: 5 iterations, 10 s each +[info] # Measurement: 5 iterations, 10 s each +[info] # Timeout: 10 min per iteration +[info] # Threads: 1 thread, will synchronize iterations +[info] # Benchmark mode: Average time, time/op +[info] # Benchmark: benchmark.RegexBenchmark.abStar_accepting_zipper_mem +[info] # Parameters: (size = 30) +[info] # Run progress: 40.63% complete, ETA 02:39:00 +[info] # Fork: 1 of 1 +[info] # Warmup Iteration 1: 21.454 us/op +[info] # Warmup Iteration 2: 19.809 us/op +[info] # Warmup Iteration 3: 19.750 us/op +[info] # Warmup Iteration 4: 19.933 us/op +[info] # Warmup Iteration 5: 19.958 us/op +[info] Iteration 1: 19.800 us/op +[info] Iteration 2: 19.748 us/op +[info] Iteration 3: 19.613 us/op +[info] Iteration 4: 19.656 us/op +[info] Iteration 5: 19.607 us/op +[info] Result "benchmark.RegexBenchmark.abStar_accepting_zipper_mem": +[info] 19.685 ±(99.9%) 0.329 us/op [Average] +[info] (min, avg, max) = (19.607, 19.685, 19.800), stdev = 0.085 +[info] CI (99.9%): [19.356, 20.013] (assumes normal distribution) +[info] # JMH version: 1.32 +[info] # VM version: JDK 17.0.11, OpenJDK 64-Bit Server VM, 17.0.11+9 +[info] # VM invoker: /usr/lib/jvm/temurin-17-jdk-amd64/bin/java +[info] # VM options: +[info] # Blackhole mode: full + dont-inline hint +[info] # Warmup: 5 iterations, 10 s each +[info] # Measurement: 5 iterations, 10 s each +[info] # Timeout: 10 min per iteration +[info] # Threads: 1 thread, will synchronize iterations +[info] # Benchmark mode: Average time, time/op +[info] # Benchmark: benchmark.RegexBenchmark.abStar_accepting_zipper_mem +[info] # Parameters: (size = 35) +[info] # Run progress: 41.25% complete, ETA 02:37:19 +[info] # Fork: 1 of 1 +[info] # Warmup Iteration 1: 24.878 us/op +[info] # Warmup Iteration 2: 23.401 us/op +[info] # Warmup Iteration 3: 23.474 us/op +[info] # Warmup Iteration 4: 23.344 us/op +[info] # Warmup Iteration 5: 23.460 us/op +[info] Iteration 1: 23.031 us/op +[info] Iteration 2: 23.372 us/op +[info] Iteration 3: 22.998 us/op +[info] Iteration 4: 23.008 us/op +[info] Iteration 5: 23.001 us/op +[info] Result "benchmark.RegexBenchmark.abStar_accepting_zipper_mem": +[info] 23.082 ±(99.9%) 0.626 us/op [Average] +[info] (min, avg, max) = (22.998, 23.082, 23.372), stdev = 0.162 +[info] CI (99.9%): [22.456, 23.708] (assumes normal distribution) +[info] # JMH version: 1.32 +[info] # VM version: JDK 17.0.11, OpenJDK 64-Bit Server VM, 17.0.11+9 +[info] # VM invoker: /usr/lib/jvm/temurin-17-jdk-amd64/bin/java +[info] # VM options: +[info] # Blackhole mode: full + dont-inline hint +[info] # Warmup: 5 iterations, 10 s each +[info] # Measurement: 5 iterations, 10 s each +[info] # Timeout: 10 min per iteration +[info] # Threads: 1 thread, will synchronize iterations +[info] # Benchmark mode: Average time, time/op +[info] # Benchmark: benchmark.RegexBenchmark.abStar_accepting_zipper_mem +[info] # Parameters: (size = 40) +[info] # Run progress: 41.88% complete, ETA 02:35:39 +[info] # Fork: 1 of 1 +[info] # Warmup Iteration 1: 28.040 us/op +[info] # Warmup Iteration 2: 26.019 us/op +[info] # Warmup Iteration 3: 26.120 us/op +[info] # Warmup Iteration 4: 26.105 us/op +[info] # Warmup Iteration 5: 25.717 us/op +[info] Iteration 1: 25.777 us/op +[info] Iteration 2: 26.050 us/op +[info] Iteration 3: 25.916 us/op +[info] Iteration 4: 25.763 us/op +[info] Iteration 5: 25.784 us/op +[info] Result "benchmark.RegexBenchmark.abStar_accepting_zipper_mem": +[info] 25.858 ±(99.9%) 0.477 us/op [Average] +[info] (min, avg, max) = (25.763, 25.858, 26.050), stdev = 0.124 +[info] CI (99.9%): [25.381, 26.335] (assumes normal distribution) +[info] # JMH version: 1.32 +[info] # VM version: JDK 17.0.11, OpenJDK 64-Bit Server VM, 17.0.11+9 +[info] # VM invoker: /usr/lib/jvm/temurin-17-jdk-amd64/bin/java +[info] # VM options: +[info] # Blackhole mode: full + dont-inline hint +[info] # Warmup: 5 iterations, 10 s each +[info] # Measurement: 5 iterations, 10 s each +[info] # Timeout: 10 min per iteration +[info] # Threads: 1 thread, will synchronize iterations +[info] # Benchmark mode: Average time, time/op +[info] # Benchmark: benchmark.RegexBenchmark.abStar_accepting_zipper_mem +[info] # Parameters: (size = 45) +[info] # Run progress: 42.50% complete, ETA 02:33:59 +[info] # Fork: 1 of 1 +[info] # Warmup Iteration 1: 32.550 us/op +[info] # Warmup Iteration 2: 29.880 us/op +[info] # Warmup Iteration 3: 30.563 us/op +[info] # Warmup Iteration 4: 30.099 us/op +[info] # Warmup Iteration 5: 30.448 us/op +[info] Iteration 1: 29.841 us/op +[info] Iteration 2: 30.182 us/op +[info] Iteration 3: 29.689 us/op +[info] Iteration 4: 29.685 us/op +[info] Iteration 5: 29.626 us/op +[info] Result "benchmark.RegexBenchmark.abStar_accepting_zipper_mem": +[info] 29.805 ±(99.9%) 0.868 us/op [Average] +[info] (min, avg, max) = (29.626, 29.805, 30.182), stdev = 0.225 +[info] CI (99.9%): [28.937, 30.673] (assumes normal distribution) +[info] # JMH version: 1.32 +[info] # VM version: JDK 17.0.11, OpenJDK 64-Bit Server VM, 17.0.11+9 +[info] # VM invoker: /usr/lib/jvm/temurin-17-jdk-amd64/bin/java +[info] # VM options: +[info] # Blackhole mode: full + dont-inline hint +[info] # Warmup: 5 iterations, 10 s each +[info] # Measurement: 5 iterations, 10 s each +[info] # Timeout: 10 min per iteration +[info] # Threads: 1 thread, will synchronize iterations +[info] # Benchmark mode: Average time, time/op +[info] # Benchmark: benchmark.RegexBenchmark.abStar_accepting_zipper_mem +[info] # Parameters: (size = 50) +[info] # Run progress: 43.13% complete, ETA 02:32:18 +[info] # Fork: 1 of 1 +[info] # Warmup Iteration 1: 35.695 us/op +[info] # Warmup Iteration 2: 32.850 us/op +[info] # Warmup Iteration 3: 32.799 us/op +[info] # Warmup Iteration 4: 32.801 us/op +[info] # Warmup Iteration 5: 32.957 us/op +[info] Iteration 1: 32.468 us/op +[info] Iteration 2: 32.902 us/op +[info] Iteration 3: 32.336 us/op +[info] Iteration 4: 32.448 us/op +[info] Iteration 5: 32.927 us/op +[info] Result "benchmark.RegexBenchmark.abStar_accepting_zipper_mem": +[info] 32.617 ±(99.9%) 1.067 us/op [Average] +[info] (min, avg, max) = (32.336, 32.617, 32.927), stdev = 0.277 +[info] CI (99.9%): [31.550, 33.683] (assumes normal distribution) +[info] # JMH version: 1.32 +[info] # VM version: JDK 17.0.11, OpenJDK 64-Bit Server VM, 17.0.11+9 +[info] # VM invoker: /usr/lib/jvm/temurin-17-jdk-amd64/bin/java +[info] # VM options: +[info] # Blackhole mode: full + dont-inline hint +[info] # Warmup: 5 iterations, 10 s each +[info] # Measurement: 5 iterations, 10 s each +[info] # Timeout: 10 min per iteration +[info] # Threads: 1 thread, will synchronize iterations +[info] # Benchmark mode: Average time, time/op +[info] # Benchmark: benchmark.RegexBenchmark.abStar_accepting_zipper_mem +[info] # Parameters: (size = 55) +[info] # Run progress: 43.75% complete, ETA 02:30:38 +[info] # Fork: 1 of 1 +[info] # Warmup Iteration 1: 34.298 us/op +[info] # Warmup Iteration 2: 31.780 us/op +[info] # Warmup Iteration 3: 31.549 us/op +[info] # Warmup Iteration 4: 30.991 us/op +[info] # Warmup Iteration 5: 30.951 us/op +[info] Iteration 1: 31.248 us/op +[info] Iteration 2: 30.974 us/op +[info] Iteration 3: 31.066 us/op +[info] Iteration 4: 30.882 us/op +[info] Iteration 5: 30.956 us/op +[info] Result "benchmark.RegexBenchmark.abStar_accepting_zipper_mem": +[info] 31.025 ±(99.9%) 0.543 us/op [Average] +[info] (min, avg, max) = (30.882, 31.025, 31.248), stdev = 0.141 +[info] CI (99.9%): [30.483, 31.568] (assumes normal distribution) +[info] # JMH version: 1.32 +[info] # VM version: JDK 17.0.11, OpenJDK 64-Bit Server VM, 17.0.11+9 +[info] # VM invoker: /usr/lib/jvm/temurin-17-jdk-amd64/bin/java +[info] # VM options: +[info] # Blackhole mode: full + dont-inline hint +[info] # Warmup: 5 iterations, 10 s each +[info] # Measurement: 5 iterations, 10 s each +[info] # Timeout: 10 min per iteration +[info] # Threads: 1 thread, will synchronize iterations +[info] # Benchmark mode: Average time, time/op +[info] # Benchmark: benchmark.RegexBenchmark.abStar_accepting_zipper_mem +[info] # Parameters: (size = 60) +[info] # Run progress: 44.38% complete, ETA 02:28:57 +[info] # Fork: 1 of 1 +[info] # Warmup Iteration 1: 41.959 us/op +[info] # Warmup Iteration 2: 39.268 us/op +[info] # Warmup Iteration 3: 39.853 us/op +[info] # Warmup Iteration 4: 39.953 us/op +[info] # Warmup Iteration 5: 40.042 us/op +[info] Iteration 1: 40.050 us/op +[info] Iteration 2: 39.375 us/op +[info] Iteration 3: 39.283 us/op +[info] Iteration 4: 39.226 us/op +[info] Iteration 5: 39.053 us/op +[info] Result "benchmark.RegexBenchmark.abStar_accepting_zipper_mem": +[info] 39.397 ±(99.9%) 1.475 us/op [Average] +[info] (min, avg, max) = (39.053, 39.397, 40.050), stdev = 0.383 +[info] CI (99.9%): [37.922, 40.872] (assumes normal distribution) +[info] # JMH version: 1.32 +[info] # VM version: JDK 17.0.11, OpenJDK 64-Bit Server VM, 17.0.11+9 +[info] # VM invoker: /usr/lib/jvm/temurin-17-jdk-amd64/bin/java +[info] # VM options: +[info] # Blackhole mode: full + dont-inline hint +[info] # Warmup: 5 iterations, 10 s each +[info] # Measurement: 5 iterations, 10 s each +[info] # Timeout: 10 min per iteration +[info] # Threads: 1 thread, will synchronize iterations +[info] # Benchmark mode: Average time, time/op +[info] # Benchmark: benchmark.RegexBenchmark.abStar_accepting_zipper_mem +[info] # Parameters: (size = 65) +[info] # Run progress: 45.00% complete, ETA 02:27:17 +[info] # Fork: 1 of 1 +[info] # Warmup Iteration 1: 53.996 us/op +[info] # Warmup Iteration 2: 50.120 us/op +[info] # Warmup Iteration 3: 49.075 us/op +[info] # Warmup Iteration 4: 50.240 us/op +[info] # Warmup Iteration 5: 50.197 us/op +[info] Iteration 1: 49.329 us/op +[info] Iteration 2: 49.727 us/op +[info] Iteration 3: 49.673 us/op +[info] Iteration 4: 49.128 us/op +[info] Iteration 5: 49.100 us/op +[info] Result "benchmark.RegexBenchmark.abStar_accepting_zipper_mem": +[info] 49.392 ±(99.9%) 1.140 us/op [Average] +[info] (min, avg, max) = (49.100, 49.392, 49.727), stdev = 0.296 +[info] CI (99.9%): [48.252, 50.531] (assumes normal distribution) +[info] # JMH version: 1.32 +[info] # VM version: JDK 17.0.11, OpenJDK 64-Bit Server VM, 17.0.11+9 +[info] # VM invoker: /usr/lib/jvm/temurin-17-jdk-amd64/bin/java +[info] # VM options: +[info] # Blackhole mode: full + dont-inline hint +[info] # Warmup: 5 iterations, 10 s each +[info] # Measurement: 5 iterations, 10 s each +[info] # Timeout: 10 min per iteration +[info] # Threads: 1 thread, will synchronize iterations +[info] # Benchmark mode: Average time, time/op +[info] # Benchmark: benchmark.RegexBenchmark.abStar_accepting_zipper_mem +[info] # Parameters: (size = 70) +[info] # Run progress: 45.63% complete, ETA 02:25:37 +[info] # Fork: 1 of 1 +[info] # Warmup Iteration 1: 48.546 us/op +[info] # Warmup Iteration 2: 45.227 us/op +[info] # Warmup Iteration 3: 45.983 us/op +[info] # Warmup Iteration 4: 45.867 us/op +[info] # Warmup Iteration 5: 46.073 us/op +[info] Iteration 1: 45.364 us/op +[info] Iteration 2: 45.853 us/op +[info] Iteration 3: 45.605 us/op +[info] Iteration 4: 45.315 us/op +[info] Iteration 5: 45.278 us/op +[info] Result "benchmark.RegexBenchmark.abStar_accepting_zipper_mem": +[info] 45.483 ±(99.9%) 0.936 us/op [Average] +[info] (min, avg, max) = (45.278, 45.483, 45.853), stdev = 0.243 +[info] CI (99.9%): [44.547, 46.419] (assumes normal distribution) +[info] # JMH version: 1.32 +[info] # VM version: JDK 17.0.11, OpenJDK 64-Bit Server VM, 17.0.11+9 +[info] # VM invoker: /usr/lib/jvm/temurin-17-jdk-amd64/bin/java +[info] # VM options: +[info] # Blackhole mode: full + dont-inline hint +[info] # Warmup: 5 iterations, 10 s each +[info] # Measurement: 5 iterations, 10 s each +[info] # Timeout: 10 min per iteration +[info] # Threads: 1 thread, will synchronize iterations +[info] # Benchmark mode: Average time, time/op +[info] # Benchmark: benchmark.RegexBenchmark.abStar_accepting_zipper_mem +[info] # Parameters: (size = 75) +[info] # Run progress: 46.25% complete, ETA 02:23:56 +[info] # Fork: 1 of 1 +[info] # Warmup Iteration 1: 52.914 us/op +[info] # Warmup Iteration 2: 49.310 us/op +[info] # Warmup Iteration 3: 49.351 us/op +[info] # Warmup Iteration 4: 48.173 us/op +[info] # Warmup Iteration 5: 49.260 us/op +[info] Iteration 1: 48.993 us/op +[info] Iteration 2: 48.951 us/op +[info] Iteration 3: 48.223 us/op +[info] Iteration 4: 48.470 us/op +[info] Iteration 5: 48.474 us/op +[info] Result "benchmark.RegexBenchmark.abStar_accepting_zipper_mem": +[info] 48.622 ±(99.9%) 1.293 us/op [Average] +[info] (min, avg, max) = (48.223, 48.622, 48.993), stdev = 0.336 +[info] CI (99.9%): [47.329, 49.915] (assumes normal distribution) +[info] # JMH version: 1.32 +[info] # VM version: JDK 17.0.11, OpenJDK 64-Bit Server VM, 17.0.11+9 +[info] # VM invoker: /usr/lib/jvm/temurin-17-jdk-amd64/bin/java +[info] # VM options: +[info] # Blackhole mode: full + dont-inline hint +[info] # Warmup: 5 iterations, 10 s each +[info] # Measurement: 5 iterations, 10 s each +[info] # Timeout: 10 min per iteration +[info] # Threads: 1 thread, will synchronize iterations +[info] # Benchmark mode: Average time, time/op +[info] # Benchmark: benchmark.RegexBenchmark.abStar_accepting_zipper_mem +[info] # Parameters: (size = 80) +[info] # Run progress: 46.88% complete, ETA 02:22:16 +[info] # Fork: 1 of 1 +[info] # Warmup Iteration 1: 57.075 us/op +[info] # Warmup Iteration 2: 53.808 us/op +[info] # Warmup Iteration 3: 54.410 us/op +[info] # Warmup Iteration 4: 53.797 us/op +[info] # Warmup Iteration 5: 53.579 us/op +[info] Iteration 1: 53.846 us/op +[info] Iteration 2: 52.992 us/op +[info] Iteration 3: 52.960 us/op +[info] Iteration 4: 53.104 us/op +[info] Iteration 5: 53.337 us/op +[info] Result "benchmark.RegexBenchmark.abStar_accepting_zipper_mem": +[info] 53.248 ±(99.9%) 1.408 us/op [Average] +[info] (min, avg, max) = (52.960, 53.248, 53.846), stdev = 0.366 +[info] CI (99.9%): [51.840, 54.656] (assumes normal distribution) +[info] # JMH version: 1.32 +[info] # VM version: JDK 17.0.11, OpenJDK 64-Bit Server VM, 17.0.11+9 +[info] # VM invoker: /usr/lib/jvm/temurin-17-jdk-amd64/bin/java +[info] # VM options: +[info] # Blackhole mode: full + dont-inline hint +[info] # Warmup: 5 iterations, 10 s each +[info] # Measurement: 5 iterations, 10 s each +[info] # Timeout: 10 min per iteration +[info] # Threads: 1 thread, will synchronize iterations +[info] # Benchmark mode: Average time, time/op +[info] # Benchmark: benchmark.RegexBenchmark.abStar_accepting_zipper_mem +[info] # Parameters: (size = 85) +[info] # Run progress: 47.50% complete, ETA 02:20:35 +[info] # Fork: 1 of 1 +[info] # Warmup Iteration 1: 58.827 us/op +[info] # Warmup Iteration 2: 55.377 us/op +[info] # Warmup Iteration 3: 55.081 us/op +[info] # Warmup Iteration 4: 54.751 us/op +[info] # Warmup Iteration 5: 54.767 us/op +[info] Iteration 1: 54.174 us/op +[info] Iteration 2: 54.871 us/op +[info] Iteration 3: 54.804 us/op +[info] Iteration 4: 54.473 us/op +[info] Iteration 5: 54.348 us/op +[info] Result "benchmark.RegexBenchmark.abStar_accepting_zipper_mem": +[info] 54.534 ±(99.9%) 1.146 us/op [Average] +[info] (min, avg, max) = (54.174, 54.534, 54.871), stdev = 0.297 +[info] CI (99.9%): [53.389, 55.680] (assumes normal distribution) +[info] # JMH version: 1.32 +[info] # VM version: JDK 17.0.11, OpenJDK 64-Bit Server VM, 17.0.11+9 +[info] # VM invoker: /usr/lib/jvm/temurin-17-jdk-amd64/bin/java +[info] # VM options: +[info] # Blackhole mode: full + dont-inline hint +[info] # Warmup: 5 iterations, 10 s each +[info] # Measurement: 5 iterations, 10 s each +[info] # Timeout: 10 min per iteration +[info] # Threads: 1 thread, will synchronize iterations +[info] # Benchmark mode: Average time, time/op +[info] # Benchmark: benchmark.RegexBenchmark.abStar_accepting_zipper_mem +[info] # Parameters: (size = 90) +[info] # Run progress: 48.13% complete, ETA 02:18:55 +[info] # Fork: 1 of 1 +[info] # Warmup Iteration 1: 62.305 us/op +[info] # Warmup Iteration 2: 59.547 us/op +[info] # Warmup Iteration 3: 61.038 us/op +[info] # Warmup Iteration 4: 59.663 us/op +[info] # Warmup Iteration 5: 59.248 us/op +[info] Iteration 1: 59.780 us/op +[info] Iteration 2: 58.483 us/op +[info] Iteration 3: 58.518 us/op +[info] Iteration 4: 57.981 us/op +[info] Iteration 5: 57.887 us/op +[info] Result "benchmark.RegexBenchmark.abStar_accepting_zipper_mem": +[info] 58.530 ±(99.9%) 2.906 us/op [Average] +[info] (min, avg, max) = (57.887, 58.530, 59.780), stdev = 0.755 +[info] CI (99.9%): [55.624, 61.436] (assumes normal distribution) +[info] # JMH version: 1.32 +[info] # VM version: JDK 17.0.11, OpenJDK 64-Bit Server VM, 17.0.11+9 +[info] # VM invoker: /usr/lib/jvm/temurin-17-jdk-amd64/bin/java +[info] # VM options: +[info] # Blackhole mode: full + dont-inline hint +[info] # Warmup: 5 iterations, 10 s each +[info] # Measurement: 5 iterations, 10 s each +[info] # Timeout: 10 min per iteration +[info] # Threads: 1 thread, will synchronize iterations +[info] # Benchmark mode: Average time, time/op +[info] # Benchmark: benchmark.RegexBenchmark.abStar_accepting_zipper_mem +[info] # Parameters: (size = 95) +[info] # Run progress: 48.75% complete, ETA 02:17:14 +[info] # Fork: 1 of 1 +[info] # Warmup Iteration 1: 67.026 us/op +[info] # Warmup Iteration 2: 63.482 us/op +[info] # Warmup Iteration 3: 63.308 us/op +[info] # Warmup Iteration 4: 62.855 us/op +[info] # Warmup Iteration 5: 62.700 us/op +[info] Iteration 1: 62.199 us/op +[info] Iteration 2: 62.993 us/op +[info] Iteration 3: 62.975 us/op +[info] Iteration 4: 62.537 us/op +[info] Iteration 5: 62.672 us/op +[info] Result "benchmark.RegexBenchmark.abStar_accepting_zipper_mem": +[info] 62.675 ±(99.9%) 1.273 us/op [Average] +[info] (min, avg, max) = (62.199, 62.675, 62.993), stdev = 0.331 +[info] CI (99.9%): [61.402, 63.948] (assumes normal distribution) +[info] # JMH version: 1.32 +[info] # VM version: JDK 17.0.11, OpenJDK 64-Bit Server VM, 17.0.11+9 +[info] # VM invoker: /usr/lib/jvm/temurin-17-jdk-amd64/bin/java +[info] # VM options: +[info] # Blackhole mode: full + dont-inline hint +[info] # Warmup: 5 iterations, 10 s each +[info] # Measurement: 5 iterations, 10 s each +[info] # Timeout: 10 min per iteration +[info] # Threads: 1 thread, will synchronize iterations +[info] # Benchmark mode: Average time, time/op +[info] # Benchmark: benchmark.RegexBenchmark.abStar_accepting_zipper_mem +[info] # Parameters: (size = 100) +[info] # Run progress: 49.38% complete, ETA 02:15:34 +[info] # Fork: 1 of 1 +[info] # Warmup Iteration 1: 72.762 us/op +[info] # Warmup Iteration 2: 66.547 us/op +[info] # Warmup Iteration 3: 66.994 us/op +[info] # Warmup Iteration 4: 65.436 us/op +[info] # Warmup Iteration 5: 65.281 us/op +[info] Iteration 1: 66.284 us/op +[info] Iteration 2: 65.649 us/op +[info] Iteration 3: 65.768 us/op +[info] Iteration 4: 65.369 us/op +[info] Iteration 5: 65.231 us/op +[info] Result "benchmark.RegexBenchmark.abStar_accepting_zipper_mem": +[info] 65.660 ±(99.9%) 1.576 us/op [Average] +[info] (min, avg, max) = (65.231, 65.660, 66.284), stdev = 0.409 +[info] CI (99.9%): [64.084, 67.236] (assumes normal distribution) +[info] # JMH version: 1.32 +[info] # VM version: JDK 17.0.11, OpenJDK 64-Bit Server VM, 17.0.11+9 +[info] # VM invoker: /usr/lib/jvm/temurin-17-jdk-amd64/bin/java +[info] # VM options: +[info] # Blackhole mode: full + dont-inline hint +[info] # Warmup: 5 iterations, 10 s each +[info] # Measurement: 5 iterations, 10 s each +[info] # Timeout: 10 min per iteration +[info] # Threads: 1 thread, will synchronize iterations +[info] # Benchmark mode: Average time, time/op +[info] # Benchmark: benchmark.RegexBenchmark.email_accepting_regex +[info] # Parameters: (size = 5) +[info] # Run progress: 50.00% complete, ETA 02:13:54 +[info] # Fork: 1 of 1 +[info] # Warmup Iteration 1: 15.767 us/op +[info] # Warmup Iteration 2: 14.933 us/op +[info] # Warmup Iteration 3: 15.011 us/op +[info] # Warmup Iteration 4: 14.966 us/op +[info] # Warmup Iteration 5: 14.679 us/op +[info] Iteration 1: 14.725 us/op +[info] Iteration 2: 14.514 us/op +[info] Iteration 3: 14.427 us/op +[info] Iteration 4: 14.347 us/op +[info] Iteration 5: 14.381 us/op +[info] Result "benchmark.RegexBenchmark.email_accepting_regex": +[info] 14.479 ±(99.9%) 0.582 us/op [Average] +[info] (min, avg, max) = (14.347, 14.479, 14.725), stdev = 0.151 +[info] CI (99.9%): [13.897, 15.060] (assumes normal distribution) +[info] # JMH version: 1.32 +[info] # VM version: JDK 17.0.11, OpenJDK 64-Bit Server VM, 17.0.11+9 +[info] # VM invoker: /usr/lib/jvm/temurin-17-jdk-amd64/bin/java +[info] # VM options: +[info] # Blackhole mode: full + dont-inline hint +[info] # Warmup: 5 iterations, 10 s each +[info] # Measurement: 5 iterations, 10 s each +[info] # Timeout: 10 min per iteration +[info] # Threads: 1 thread, will synchronize iterations +[info] # Benchmark mode: Average time, time/op +[info] # Benchmark: benchmark.RegexBenchmark.email_accepting_regex +[info] # Parameters: (size = 10) +[info] # Run progress: 50.63% complete, ETA 02:12:13 +[info] # Fork: 1 of 1 +[info] # Warmup Iteration 1: 68.551 us/op +[info] # Warmup Iteration 2: 63.418 us/op +[info] # Warmup Iteration 3: 63.599 us/op +[info] # Warmup Iteration 4: 63.747 us/op +[info] # Warmup Iteration 5: 62.467 us/op +[info] Iteration 1: 63.823 us/op +[info] Iteration 2: 63.188 us/op +[info] Iteration 3: 63.135 us/op +[info] Iteration 4: 62.702 us/op +[info] Iteration 5: 62.690 us/op +[info] Result "benchmark.RegexBenchmark.email_accepting_regex": +[info] 63.107 ±(99.9%) 1.784 us/op [Average] +[info] (min, avg, max) = (62.690, 63.107, 63.823), stdev = 0.463 +[info] CI (99.9%): [61.324, 64.891] (assumes normal distribution) +[info] # JMH version: 1.32 +[info] # VM version: JDK 17.0.11, OpenJDK 64-Bit Server VM, 17.0.11+9 +[info] # VM invoker: /usr/lib/jvm/temurin-17-jdk-amd64/bin/java +[info] # VM options: +[info] # Blackhole mode: full + dont-inline hint +[info] # Warmup: 5 iterations, 10 s each +[info] # Measurement: 5 iterations, 10 s each +[info] # Timeout: 10 min per iteration +[info] # Threads: 1 thread, will synchronize iterations +[info] # Benchmark mode: Average time, time/op +[info] # Benchmark: benchmark.RegexBenchmark.email_accepting_regex +[info] # Parameters: (size = 15) +[info] # Run progress: 51.25% complete, ETA 02:10:33 +[info] # Fork: 1 of 1 +[info] # Warmup Iteration 1: 150.426 us/op +[info] # Warmup Iteration 2: 142.334 us/op +[info] # Warmup Iteration 3: 142.205 us/op +[info] # Warmup Iteration 4: 142.129 us/op +[info] # Warmup Iteration 5: 139.512 us/op +[info] Iteration 1: 142.121 us/op +[info] Iteration 2: 139.666 us/op +[info] Iteration 3: 141.540 us/op +[info] Iteration 4: 139.590 us/op +[info] Iteration 5: 139.478 us/op +[info] Result "benchmark.RegexBenchmark.email_accepting_regex": +[info] 140.479 ±(99.9%) 4.823 us/op [Average] +[info] (min, avg, max) = (139.478, 140.479, 142.121), stdev = 1.252 +[info] CI (99.9%): [135.656, 145.302] (assumes normal distribution) +[info] # JMH version: 1.32 +[info] # VM version: JDK 17.0.11, OpenJDK 64-Bit Server VM, 17.0.11+9 +[info] # VM invoker: /usr/lib/jvm/temurin-17-jdk-amd64/bin/java +[info] # VM options: +[info] # Blackhole mode: full + dont-inline hint +[info] # Warmup: 5 iterations, 10 s each +[info] # Measurement: 5 iterations, 10 s each +[info] # Timeout: 10 min per iteration +[info] # Threads: 1 thread, will synchronize iterations +[info] # Benchmark mode: Average time, time/op +[info] # Benchmark: benchmark.RegexBenchmark.email_accepting_regex +[info] # Parameters: (size = 20) +[info] # Run progress: 51.88% complete, ETA 02:08:52 +[info] # Fork: 1 of 1 +[info] # Warmup Iteration 1: 297.653 us/op +[info] # Warmup Iteration 2: 273.726 us/op +[info] # Warmup Iteration 3: 281.561 us/op +[info] # Warmup Iteration 4: 276.422 us/op +[info] # Warmup Iteration 5: 280.318 us/op +[info] Iteration 1: 270.196 us/op +[info] Iteration 2: 272.672 us/op +[info] Iteration 3: 272.198 us/op +[info] Iteration 4: 270.368 us/op +[info] Iteration 5: 270.852 us/op +[info] Result "benchmark.RegexBenchmark.email_accepting_regex": +[info] 271.257 ±(99.9%) 4.290 us/op [Average] +[info] (min, avg, max) = (270.196, 271.257, 272.672), stdev = 1.114 +[info] CI (99.9%): [266.967, 275.548] (assumes normal distribution) +[info] # JMH version: 1.32 +[info] # VM version: JDK 17.0.11, OpenJDK 64-Bit Server VM, 17.0.11+9 +[info] # VM invoker: /usr/lib/jvm/temurin-17-jdk-amd64/bin/java +[info] # VM options: +[info] # Blackhole mode: full + dont-inline hint +[info] # Warmup: 5 iterations, 10 s each +[info] # Measurement: 5 iterations, 10 s each +[info] # Timeout: 10 min per iteration +[info] # Threads: 1 thread, will synchronize iterations +[info] # Benchmark mode: Average time, time/op +[info] # Benchmark: benchmark.RegexBenchmark.email_accepting_regex +[info] # Parameters: (size = 25) +[info] # Run progress: 52.50% complete, ETA 02:07:12 +[info] # Fork: 1 of 1 +[info] # Warmup Iteration 1: 450.852 us/op +[info] # Warmup Iteration 2: 424.203 us/op +[info] # Warmup Iteration 3: 423.430 us/op +[info] # Warmup Iteration 4: 424.419 us/op +[info] # Warmup Iteration 5: 415.305 us/op +[info] Iteration 1: 423.145 us/op +[info] Iteration 2: 419.750 us/op +[info] Iteration 3: 417.931 us/op +[info] Iteration 4: 414.884 us/op +[info] Iteration 5: 415.396 us/op +[info] Result "benchmark.RegexBenchmark.email_accepting_regex": +[info] 418.221 ±(99.9%) 13.027 us/op [Average] +[info] (min, avg, max) = (414.884, 418.221, 423.145), stdev = 3.383 +[info] CI (99.9%): [405.195, 431.248] (assumes normal distribution) +[info] # JMH version: 1.32 +[info] # VM version: JDK 17.0.11, OpenJDK 64-Bit Server VM, 17.0.11+9 +[info] # VM invoker: /usr/lib/jvm/temurin-17-jdk-amd64/bin/java +[info] # VM options: +[info] # Blackhole mode: full + dont-inline hint +[info] # Warmup: 5 iterations, 10 s each +[info] # Measurement: 5 iterations, 10 s each +[info] # Timeout: 10 min per iteration +[info] # Threads: 1 thread, will synchronize iterations +[info] # Benchmark mode: Average time, time/op +[info] # Benchmark: benchmark.RegexBenchmark.email_accepting_regex +[info] # Parameters: (size = 30) +[info] # Run progress: 53.13% complete, ETA 02:05:32 +[info] # Fork: 1 of 1 +[info] # Warmup Iteration 1: 806.193 us/op +[info] # Warmup Iteration 2: 759.024 us/op +[info] # Warmup Iteration 3: 764.188 us/op +[info] # Warmup Iteration 4: 757.943 us/op +[info] # Warmup Iteration 5: 747.072 us/op +[info] Iteration 1: 759.313 us/op +[info] Iteration 2: 754.395 us/op +[info] Iteration 3: 750.781 us/op +[info] Iteration 4: 745.668 us/op +[info] Iteration 5: 744.802 us/op +[info] Result "benchmark.RegexBenchmark.email_accepting_regex": +[info] 750.992 ±(99.9%) 23.385 us/op [Average] +[info] (min, avg, max) = (744.802, 750.992, 759.313), stdev = 6.073 +[info] CI (99.9%): [727.607, 774.377] (assumes normal distribution) +[info] # JMH version: 1.32 +[info] # VM version: JDK 17.0.11, OpenJDK 64-Bit Server VM, 17.0.11+9 +[info] # VM invoker: /usr/lib/jvm/temurin-17-jdk-amd64/bin/java +[info] # VM options: +[info] # Blackhole mode: full + dont-inline hint +[info] # Warmup: 5 iterations, 10 s each +[info] # Measurement: 5 iterations, 10 s each +[info] # Timeout: 10 min per iteration +[info] # Threads: 1 thread, will synchronize iterations +[info] # Benchmark mode: Average time, time/op +[info] # Benchmark: benchmark.RegexBenchmark.email_accepting_regex +[info] # Parameters: (size = 35) +[info] # Run progress: 53.75% complete, ETA 02:03:51 +[info] # Fork: 1 of 1 +[info] # Warmup Iteration 1: 1100.522 us/op +[info] # Warmup Iteration 2: 1037.535 us/op +[info] # Warmup Iteration 3: 1050.014 us/op +[info] # Warmup Iteration 4: 1041.095 us/op +[info] # Warmup Iteration 5: 1024.572 us/op +[info] Iteration 1: 1038.150 us/op +[info] Iteration 2: 1026.275 us/op +[info] Iteration 3: 1032.371 us/op +[info] Iteration 4: 1016.903 us/op +[info] Iteration 5: 1013.334 us/op +[info] Result "benchmark.RegexBenchmark.email_accepting_regex": +[info] 1025.407 ±(99.9%) 39.910 us/op [Average] +[info] (min, avg, max) = (1013.334, 1025.407, 1038.150), stdev = 10.365 +[info] CI (99.9%): [985.496, 1065.317] (assumes normal distribution) +[info] # JMH version: 1.32 +[info] # VM version: JDK 17.0.11, OpenJDK 64-Bit Server VM, 17.0.11+9 +[info] # VM invoker: /usr/lib/jvm/temurin-17-jdk-amd64/bin/java +[info] # VM options: +[info] # Blackhole mode: full + dont-inline hint +[info] # Warmup: 5 iterations, 10 s each +[info] # Measurement: 5 iterations, 10 s each +[info] # Timeout: 10 min per iteration +[info] # Threads: 1 thread, will synchronize iterations +[info] # Benchmark mode: Average time, time/op +[info] # Benchmark: benchmark.RegexBenchmark.email_accepting_regex +[info] # Parameters: (size = 40) +[info] # Run progress: 54.37% complete, ETA 02:02:11 +[info] # Fork: 1 of 1 +[info] # Warmup Iteration 1: 1812.686 us/op +[info] # Warmup Iteration 2: 1701.102 us/op +[info] # Warmup Iteration 3: 1703.948 us/op +[info] # Warmup Iteration 4: 1706.109 us/op +[info] # Warmup Iteration 5: 1674.899 us/op +[info] Iteration 1: 1692.061 us/op +[info] Iteration 2: 1689.882 us/op +[info] Iteration 3: 1707.192 us/op +[info] Iteration 4: 1683.237 us/op +[info] Iteration 5: 1682.154 us/op +[info] Result "benchmark.RegexBenchmark.email_accepting_regex": +[info] 1690.905 ±(99.9%) 38.652 us/op [Average] +[info] (min, avg, max) = (1682.154, 1690.905, 1707.192), stdev = 10.038 +[info] CI (99.9%): [1652.253, 1729.557] (assumes normal distribution) +[info] # JMH version: 1.32 +[info] # VM version: JDK 17.0.11, OpenJDK 64-Bit Server VM, 17.0.11+9 +[info] # VM invoker: /usr/lib/jvm/temurin-17-jdk-amd64/bin/java +[info] # VM options: +[info] # Blackhole mode: full + dont-inline hint +[info] # Warmup: 5 iterations, 10 s each +[info] # Measurement: 5 iterations, 10 s each +[info] # Timeout: 10 min per iteration +[info] # Threads: 1 thread, will synchronize iterations +[info] # Benchmark mode: Average time, time/op +[info] # Benchmark: benchmark.RegexBenchmark.email_accepting_regex +[info] # Parameters: (size = 45) +[info] # Run progress: 55.00% complete, ETA 02:00:30 +[info] # Fork: 1 of 1 +[info] # Warmup Iteration 1: 2281.149 us/op +[info] # Warmup Iteration 2: 2131.715 us/op +[info] # Warmup Iteration 3: 2142.010 us/op +[info] # Warmup Iteration 4: 2135.391 us/op +[info] # Warmup Iteration 5: 2122.801 us/op +[info] Iteration 1: 2144.013 us/op +[info] Iteration 2: 2105.133 us/op +[info] Iteration 3: 2100.777 us/op +[info] Iteration 4: 2127.302 us/op +[info] Iteration 5: 2118.891 us/op +[info] Result "benchmark.RegexBenchmark.email_accepting_regex": +[info] 2119.223 ±(99.9%) 67.212 us/op [Average] +[info] (min, avg, max) = (2100.777, 2119.223, 2144.013), stdev = 17.455 +[info] CI (99.9%): [2052.011, 2186.435] (assumes normal distribution) +[info] # JMH version: 1.32 +[info] # VM version: JDK 17.0.11, OpenJDK 64-Bit Server VM, 17.0.11+9 +[info] # VM invoker: /usr/lib/jvm/temurin-17-jdk-amd64/bin/java +[info] # VM options: +[info] # Blackhole mode: full + dont-inline hint +[info] # Warmup: 5 iterations, 10 s each +[info] # Measurement: 5 iterations, 10 s each +[info] # Timeout: 10 min per iteration +[info] # Threads: 1 thread, will synchronize iterations +[info] # Benchmark mode: Average time, time/op +[info] # Benchmark: benchmark.RegexBenchmark.email_accepting_regex +[info] # Parameters: (size = 50) +[info] # Run progress: 55.63% complete, ETA 01:58:50 +[info] # Fork: 1 of 1 +[info] # Warmup Iteration 1: 3246.413 us/op +[info] # Warmup Iteration 2: 3080.286 us/op +[info] # Warmup Iteration 3: 3084.870 us/op +[info] # Warmup Iteration 4: 3090.836 us/op +[info] # Warmup Iteration 5: 3039.394 us/op +[info] Iteration 1: 3051.697 us/op +[info] Iteration 2: 3014.486 us/op +[info] Iteration 3: 2984.224 us/op +[info] Iteration 4: 3034.426 us/op +[info] Iteration 5: 2982.877 us/op +[info] Result "benchmark.RegexBenchmark.email_accepting_regex": +[info] 3013.542 ±(99.9%) 116.997 us/op [Average] +[info] (min, avg, max) = (2982.877, 3013.542, 3051.697), stdev = 30.384 +[info] CI (99.9%): [2896.545, 3130.539] (assumes normal distribution) +[info] # JMH version: 1.32 +[info] # VM version: JDK 17.0.11, OpenJDK 64-Bit Server VM, 17.0.11+9 +[info] # VM invoker: /usr/lib/jvm/temurin-17-jdk-amd64/bin/java +[info] # VM options: +[info] # Blackhole mode: full + dont-inline hint +[info] # Warmup: 5 iterations, 10 s each +[info] # Measurement: 5 iterations, 10 s each +[info] # Timeout: 10 min per iteration +[info] # Threads: 1 thread, will synchronize iterations +[info] # Benchmark mode: Average time, time/op +[info] # Benchmark: benchmark.RegexBenchmark.email_accepting_regex +[info] # Parameters: (size = 55) +[info] # Run progress: 56.25% complete, ETA 01:57:09 +[info] # Fork: 1 of 1 +[info] # Warmup Iteration 1: 3803.446 us/op +[info] # Warmup Iteration 2: 3527.405 us/op +[info] # Warmup Iteration 3: 3569.036 us/op +[info] # Warmup Iteration 4: 3571.279 us/op +[info] # Warmup Iteration 5: 3504.520 us/op +[info] Iteration 1: 3567.597 us/op +[info] Iteration 2: 3537.244 us/op +[info] Iteration 3: 3534.956 us/op +[info] Iteration 4: 3509.922 us/op +[info] Iteration 5: 3523.045 us/op +[info] Result "benchmark.RegexBenchmark.email_accepting_regex": +[info] 3534.553 ±(99.9%) 82.552 us/op [Average] +[info] (min, avg, max) = (3509.922, 3534.553, 3567.597), stdev = 21.438 +[info] CI (99.9%): [3452.001, 3617.105] (assumes normal distribution) +[info] # JMH version: 1.32 +[info] # VM version: JDK 17.0.11, OpenJDK 64-Bit Server VM, 17.0.11+9 +[info] # VM invoker: /usr/lib/jvm/temurin-17-jdk-amd64/bin/java +[info] # VM options: +[info] # Blackhole mode: full + dont-inline hint +[info] # Warmup: 5 iterations, 10 s each +[info] # Measurement: 5 iterations, 10 s each +[info] # Timeout: 10 min per iteration +[info] # Threads: 1 thread, will synchronize iterations +[info] # Benchmark mode: Average time, time/op +[info] # Benchmark: benchmark.RegexBenchmark.email_accepting_regex +[info] # Parameters: (size = 60) +[info] # Run progress: 56.88% complete, ETA 01:55:29 +[info] # Fork: 1 of 1 +[info] # Warmup Iteration 1: 4519.099 us/op +[info] # Warmup Iteration 2: 4234.664 us/op +[info] # Warmup Iteration 3: 4246.635 us/op +[info] # Warmup Iteration 4: 4235.680 us/op +[info] # Warmup Iteration 5: 4239.196 us/op +[info] Iteration 1: 4162.927 us/op +[info] Iteration 2: 4171.752 us/op +[info] Iteration 3: 4219.836 us/op +[info] Iteration 4: 4164.173 us/op +[info] Iteration 5: 4165.526 us/op +[info] Result "benchmark.RegexBenchmark.email_accepting_regex": +[info] 4176.843 ±(99.9%) 93.465 us/op [Average] +[info] (min, avg, max) = (4162.927, 4176.843, 4219.836), stdev = 24.272 +[info] CI (99.9%): [4083.378, 4270.307] (assumes normal distribution) +[info] # JMH version: 1.32 +[info] # VM version: JDK 17.0.11, OpenJDK 64-Bit Server VM, 17.0.11+9 +[info] # VM invoker: /usr/lib/jvm/temurin-17-jdk-amd64/bin/java +[info] # VM options: +[info] # Blackhole mode: full + dont-inline hint +[info] # Warmup: 5 iterations, 10 s each +[info] # Measurement: 5 iterations, 10 s each +[info] # Timeout: 10 min per iteration +[info] # Threads: 1 thread, will synchronize iterations +[info] # Benchmark mode: Average time, time/op +[info] # Benchmark: benchmark.RegexBenchmark.email_accepting_regex +[info] # Parameters: (size = 65) +[info] # Run progress: 57.50% complete, ETA 01:53:49 +[info] # Fork: 1 of 1 +[info] # Warmup Iteration 1: 6031.758 us/op +[info] # Warmup Iteration 2: 5687.405 us/op +[info] # Warmup Iteration 3: 5604.169 us/op +[info] # Warmup Iteration 4: 5675.077 us/op +[info] # Warmup Iteration 5: 5579.744 us/op +[info] Iteration 1: 5694.549 us/op +[info] Iteration 2: 5543.194 us/op +[info] Iteration 3: 5541.836 us/op +[info] Iteration 4: 5632.084 us/op +[info] Iteration 5: 5526.451 us/op +[info] Result "benchmark.RegexBenchmark.email_accepting_regex": +[info] 5587.623 ±(99.9%) 280.479 us/op [Average] +[info] (min, avg, max) = (5526.451, 5587.623, 5694.549), stdev = 72.839 +[info] CI (99.9%): [5307.144, 5868.102] (assumes normal distribution) +[info] # JMH version: 1.32 +[info] # VM version: JDK 17.0.11, OpenJDK 64-Bit Server VM, 17.0.11+9 +[info] # VM invoker: /usr/lib/jvm/temurin-17-jdk-amd64/bin/java +[info] # VM options: +[info] # Blackhole mode: full + dont-inline hint +[info] # Warmup: 5 iterations, 10 s each +[info] # Measurement: 5 iterations, 10 s each +[info] # Timeout: 10 min per iteration +[info] # Threads: 1 thread, will synchronize iterations +[info] # Benchmark mode: Average time, time/op +[info] # Benchmark: benchmark.RegexBenchmark.email_accepting_regex +[info] # Parameters: (size = 70) +[info] # Run progress: 58.13% complete, ETA 01:52:08 +[info] # Fork: 1 of 1 +[info] # Warmup Iteration 1: 7339.159 us/op +[info] # Warmup Iteration 2: 6942.061 us/op +[info] # Warmup Iteration 3: 6947.970 us/op +[info] # Warmup Iteration 4: 6932.373 us/op +[info] # Warmup Iteration 5: 6818.457 us/op +[info] Iteration 1: 6856.230 us/op +[info] Iteration 2: 6936.095 us/op +[info] Iteration 3: 6830.617 us/op +[info] Iteration 4: 6819.419 us/op +[info] Iteration 5: 6872.533 us/op +[info] Result "benchmark.RegexBenchmark.email_accepting_regex": +[info] 6862.979 ±(99.9%) 176.748 us/op [Average] +[info] (min, avg, max) = (6819.419, 6862.979, 6936.095), stdev = 45.901 +[info] CI (99.9%): [6686.231, 7039.727] (assumes normal distribution) +[info] # JMH version: 1.32 +[info] # VM version: JDK 17.0.11, OpenJDK 64-Bit Server VM, 17.0.11+9 +[info] # VM invoker: /usr/lib/jvm/temurin-17-jdk-amd64/bin/java +[info] # VM options: +[info] # Blackhole mode: full + dont-inline hint +[info] # Warmup: 5 iterations, 10 s each +[info] # Measurement: 5 iterations, 10 s each +[info] # Timeout: 10 min per iteration +[info] # Threads: 1 thread, will synchronize iterations +[info] # Benchmark mode: Average time, time/op +[info] # Benchmark: benchmark.RegexBenchmark.email_accepting_regex +[info] # Parameters: (size = 75) +[info] # Run progress: 58.75% complete, ETA 01:50:28 +[info] # Fork: 1 of 1 +[info] # Warmup Iteration 1: 9219.093 us/op +[info] # Warmup Iteration 2: 8750.682 us/op +[info] # Warmup Iteration 3: 8734.988 us/op +[info] # Warmup Iteration 4: 8718.179 us/op +[info] # Warmup Iteration 5: 8671.876 us/op +[info] Iteration 1: 8645.887 us/op +[info] Iteration 2: 8700.887 us/op +[info] Iteration 3: 8674.581 us/op +[info] Iteration 4: 8619.847 us/op +[info] Iteration 5: 8613.064 us/op +[info] Result "benchmark.RegexBenchmark.email_accepting_regex": +[info] 8650.853 ±(99.9%) 142.532 us/op [Average] +[info] (min, avg, max) = (8613.064, 8650.853, 8700.887), stdev = 37.015 +[info] CI (99.9%): [8508.321, 8793.385] (assumes normal distribution) +[info] # JMH version: 1.32 +[info] # VM version: JDK 17.0.11, OpenJDK 64-Bit Server VM, 17.0.11+9 +[info] # VM invoker: /usr/lib/jvm/temurin-17-jdk-amd64/bin/java +[info] # VM options: +[info] # Blackhole mode: full + dont-inline hint +[info] # Warmup: 5 iterations, 10 s each +[info] # Measurement: 5 iterations, 10 s each +[info] # Timeout: 10 min per iteration +[info] # Threads: 1 thread, will synchronize iterations +[info] # Benchmark mode: Average time, time/op +[info] # Benchmark: benchmark.RegexBenchmark.email_accepting_regex +[info] # Parameters: (size = 80) +[info] # Run progress: 59.38% complete, ETA 01:48:47 +[info] # Fork: 1 of 1 +[info] # Warmup Iteration 1: 10050.804 us/op +[info] # Warmup Iteration 2: 9447.013 us/op +[info] # Warmup Iteration 3: 9389.744 us/op +[info] # Warmup Iteration 4: 9337.514 us/op +[info] # Warmup Iteration 5: 9200.877 us/op +[info] Iteration 1: 9257.820 us/op +[info] Iteration 2: 9092.141 us/op +[info] Iteration 3: 9091.937 us/op +[info] Iteration 4: 9155.140 us/op +[info] Iteration 5: 9143.925 us/op +[info] Result "benchmark.RegexBenchmark.email_accepting_regex": +[info] 9148.193 ±(99.9%) 261.101 us/op [Average] +[info] (min, avg, max) = (9091.937, 9148.193, 9257.820), stdev = 67.807 +[info] CI (99.9%): [8887.092, 9409.294] (assumes normal distribution) +[info] # JMH version: 1.32 +[info] # VM version: JDK 17.0.11, OpenJDK 64-Bit Server VM, 17.0.11+9 +[info] # VM invoker: /usr/lib/jvm/temurin-17-jdk-amd64/bin/java +[info] # VM options: +[info] # Blackhole mode: full + dont-inline hint +[info] # Warmup: 5 iterations, 10 s each +[info] # Measurement: 5 iterations, 10 s each +[info] # Timeout: 10 min per iteration +[info] # Threads: 1 thread, will synchronize iterations +[info] # Benchmark mode: Average time, time/op +[info] # Benchmark: benchmark.RegexBenchmark.email_accepting_regex +[info] # Parameters: (size = 85) +[info] # Run progress: 60.00% complete, ETA 01:47:07 +[info] # Fork: 1 of 1 +[info] # Warmup Iteration 1: 12178.962 us/op +[info] # Warmup Iteration 2: 11444.864 us/op +[info] # Warmup Iteration 3: 11416.167 us/op +[info] # Warmup Iteration 4: 11379.580 us/op +[info] # Warmup Iteration 5: 11295.787 us/op +[info] Iteration 1: 11351.947 us/op +[info] Iteration 2: 11448.398 us/op +[info] Iteration 3: 11451.889 us/op +[info] Iteration 4: 11366.263 us/op +[info] Iteration 5: 11366.907 us/op +[info] Result "benchmark.RegexBenchmark.email_accepting_regex": +[info] 11397.081 ±(99.9%) 187.999 us/op [Average] +[info] (min, avg, max) = (11351.947, 11397.081, 11451.889), stdev = 48.823 +[info] CI (99.9%): [11209.082, 11585.080] (assumes normal distribution) +[info] # JMH version: 1.32 +[info] # VM version: JDK 17.0.11, OpenJDK 64-Bit Server VM, 17.0.11+9 +[info] # VM invoker: /usr/lib/jvm/temurin-17-jdk-amd64/bin/java +[info] # VM options: +[info] # Blackhole mode: full + dont-inline hint +[info] # Warmup: 5 iterations, 10 s each +[info] # Measurement: 5 iterations, 10 s each +[info] # Timeout: 10 min per iteration +[info] # Threads: 1 thread, will synchronize iterations +[info] # Benchmark mode: Average time, time/op +[info] # Benchmark: benchmark.RegexBenchmark.email_accepting_regex +[info] # Parameters: (size = 90) +[info] # Run progress: 60.62% complete, ETA 01:45:27 +[info] # Fork: 1 of 1 +[info] # Warmup Iteration 1: 14613.387 us/op +[info] # Warmup Iteration 2: 13852.344 us/op +[info] # Warmup Iteration 3: 13834.350 us/op +[info] # Warmup Iteration 4: 13584.187 us/op +[info] # Warmup Iteration 5: 13619.888 us/op +[info] Iteration 1: 13593.530 us/op +[info] Iteration 2: 13528.767 us/op +[info] Iteration 3: 13643.525 us/op +[info] Iteration 4: 13444.408 us/op +[info] Iteration 5: 13696.685 us/op +[info] Result "benchmark.RegexBenchmark.email_accepting_regex": +[info] 13581.383 ±(99.9%) 379.412 us/op [Average] +[info] (min, avg, max) = (13444.408, 13581.383, 13696.685), stdev = 98.532 +[info] CI (99.9%): [13201.971, 13960.795] (assumes normal distribution) +[info] # JMH version: 1.32 +[info] # VM version: JDK 17.0.11, OpenJDK 64-Bit Server VM, 17.0.11+9 +[info] # VM invoker: /usr/lib/jvm/temurin-17-jdk-amd64/bin/java +[info] # VM options: +[info] # Blackhole mode: full + dont-inline hint +[info] # Warmup: 5 iterations, 10 s each +[info] # Measurement: 5 iterations, 10 s each +[info] # Timeout: 10 min per iteration +[info] # Threads: 1 thread, will synchronize iterations +[info] # Benchmark mode: Average time, time/op +[info] # Benchmark: benchmark.RegexBenchmark.email_accepting_regex +[info] # Parameters: (size = 95) +[info] # Run progress: 61.25% complete, ETA 01:43:46 +[info] # Fork: 1 of 1 +[info] # Warmup Iteration 1: 17306.191 us/op +[info] # Warmup Iteration 2: 16182.188 us/op +[info] # Warmup Iteration 3: 16508.593 us/op +[info] # Warmup Iteration 4: 16295.805 us/op +[info] # Warmup Iteration 5: 16034.989 us/op +[info] Iteration 1: 15768.108 us/op +[info] Iteration 2: 15839.584 us/op +[info] Iteration 3: 16049.614 us/op +[info] Iteration 4: 15810.976 us/op +[info] Iteration 5: 15789.707 us/op +[info] Result "benchmark.RegexBenchmark.email_accepting_regex": +[info] 15851.598 ±(99.9%) 438.220 us/op [Average] +[info] (min, avg, max) = (15768.108, 15851.598, 16049.614), stdev = 113.804 +[info] CI (99.9%): [15413.378, 16289.818] (assumes normal distribution) +[info] # JMH version: 1.32 +[info] # VM version: JDK 17.0.11, OpenJDK 64-Bit Server VM, 17.0.11+9 +[info] # VM invoker: /usr/lib/jvm/temurin-17-jdk-amd64/bin/java +[info] # VM options: +[info] # Blackhole mode: full + dont-inline hint +[info] # Warmup: 5 iterations, 10 s each +[info] # Measurement: 5 iterations, 10 s each +[info] # Timeout: 10 min per iteration +[info] # Threads: 1 thread, will synchronize iterations +[info] # Benchmark mode: Average time, time/op +[info] # Benchmark: benchmark.RegexBenchmark.email_accepting_regex +[info] # Parameters: (size = 100) +[info] # Run progress: 61.88% complete, ETA 01:42:06 +[info] # Fork: 1 of 1 +[info] # Warmup Iteration 1: 18923.516 us/op +[info] # Warmup Iteration 2: 17539.810 us/op +[info] # Warmup Iteration 3: 17622.653 us/op +[info] # Warmup Iteration 4: 17340.056 us/op +[info] # Warmup Iteration 5: 17645.175 us/op +[info] Iteration 1: 17410.213 us/op +[info] Iteration 2: 17594.211 us/op +[info] Iteration 3: 17479.591 us/op +[info] Iteration 4: 17317.897 us/op +[info] Iteration 5: 17320.808 us/op +[info] Result "benchmark.RegexBenchmark.email_accepting_regex": +[info] 17424.544 ±(99.9%) 448.055 us/op [Average] +[info] (min, avg, max) = (17317.897, 17424.544, 17594.211), stdev = 116.359 +[info] CI (99.9%): [16976.489, 17872.599] (assumes normal distribution) +[info] # JMH version: 1.32 +[info] # VM version: JDK 17.0.11, OpenJDK 64-Bit Server VM, 17.0.11+9 +[info] # VM invoker: /usr/lib/jvm/temurin-17-jdk-amd64/bin/java +[info] # VM options: +[info] # Blackhole mode: full + dont-inline hint +[info] # Warmup: 5 iterations, 10 s each +[info] # Measurement: 5 iterations, 10 s each +[info] # Timeout: 10 min per iteration +[info] # Threads: 1 thread, will synchronize iterations +[info] # Benchmark mode: Average time, time/op +[info] # Benchmark: benchmark.RegexBenchmark.email_accepting_regex_mem +[info] # Parameters: (size = 5) +[info] # Run progress: 62.50% complete, ETA 01:40:26 +[info] # Fork: 1 of 1 +[info] # Warmup Iteration 1: 100.477 us/op +[info] # Warmup Iteration 2: 97.554 us/op +[info] # Warmup Iteration 3: 97.723 us/op +[info] # Warmup Iteration 4: 97.688 us/op +[info] # Warmup Iteration 5: 97.657 us/op +[info] Iteration 1: 97.751 us/op +[info] Iteration 2: 97.974 us/op +[info] Iteration 3: 97.872 us/op +[info] Iteration 4: 98.078 us/op +[info] Iteration 5: 97.954 us/op +[info] Result "benchmark.RegexBenchmark.email_accepting_regex_mem": +[info] 97.926 ±(99.9%) 0.471 us/op [Average] +[info] (min, avg, max) = (97.751, 97.926, 98.078), stdev = 0.122 +[info] CI (99.9%): [97.455, 98.397] (assumes normal distribution) +[info] # JMH version: 1.32 +[info] # VM version: JDK 17.0.11, OpenJDK 64-Bit Server VM, 17.0.11+9 +[info] # VM invoker: /usr/lib/jvm/temurin-17-jdk-amd64/bin/java +[info] # VM options: +[info] # Blackhole mode: full + dont-inline hint +[info] # Warmup: 5 iterations, 10 s each +[info] # Measurement: 5 iterations, 10 s each +[info] # Timeout: 10 min per iteration +[info] # Threads: 1 thread, will synchronize iterations +[info] # Benchmark mode: Average time, time/op +[info] # Benchmark: benchmark.RegexBenchmark.email_accepting_regex_mem +[info] # Parameters: (size = 10) +[info] # Run progress: 63.13% complete, ETA 01:38:45 +[info] # Fork: 1 of 1 +[info] # Warmup Iteration 1: 351.585 us/op +[info] # Warmup Iteration 2: 343.477 us/op +[info] # Warmup Iteration 3: 342.347 us/op +[info] # Warmup Iteration 4: 337.541 us/op +[info] # Warmup Iteration 5: 335.959 us/op +[info] Iteration 1: 336.903 us/op +[info] Iteration 2: 337.657 us/op +[info] Iteration 3: 335.683 us/op +[info] Iteration 4: 335.716 us/op +[info] Iteration 5: 336.305 us/op +[info] Result "benchmark.RegexBenchmark.email_accepting_regex_mem": +[info] 336.453 ±(99.9%) 3.227 us/op [Average] +[info] (min, avg, max) = (335.683, 336.453, 337.657), stdev = 0.838 +[info] CI (99.9%): [333.225, 339.680] (assumes normal distribution) +[info] # JMH version: 1.32 +[info] # VM version: JDK 17.0.11, OpenJDK 64-Bit Server VM, 17.0.11+9 +[info] # VM invoker: /usr/lib/jvm/temurin-17-jdk-amd64/bin/java +[info] # VM options: +[info] # Blackhole mode: full + dont-inline hint +[info] # Warmup: 5 iterations, 10 s each +[info] # Measurement: 5 iterations, 10 s each +[info] # Timeout: 10 min per iteration +[info] # Threads: 1 thread, will synchronize iterations +[info] # Benchmark mode: Average time, time/op +[info] # Benchmark: benchmark.RegexBenchmark.email_accepting_regex_mem +[info] # Parameters: (size = 15) +[info] # Run progress: 63.75% complete, ETA 01:37:05 +[info] # Fork: 1 of 1 +[info] # Warmup Iteration 1: 704.729 us/op +[info] # Warmup Iteration 2: 676.631 us/op +[info] # Warmup Iteration 3: 672.354 us/op +[info] # Warmup Iteration 4: 675.244 us/op +[info] # Warmup Iteration 5: 674.854 us/op +[info] Iteration 1: 674.795 us/op +[info] Iteration 2: 674.396 us/op +[info] Iteration 3: 673.297 us/op +[info] Iteration 4: 672.836 us/op +[info] Iteration 5: 672.388 us/op +[info] Result "benchmark.RegexBenchmark.email_accepting_regex_mem": +[info] 673.542 ±(99.9%) 3.941 us/op [Average] +[info] (min, avg, max) = (672.388, 673.542, 674.795), stdev = 1.023 +[info] CI (99.9%): [669.601, 677.483] (assumes normal distribution) +[info] # JMH version: 1.32 +[info] # VM version: JDK 17.0.11, OpenJDK 64-Bit Server VM, 17.0.11+9 +[info] # VM invoker: /usr/lib/jvm/temurin-17-jdk-amd64/bin/java +[info] # VM options: +[info] # Blackhole mode: full + dont-inline hint +[info] # Warmup: 5 iterations, 10 s each +[info] # Measurement: 5 iterations, 10 s each +[info] # Timeout: 10 min per iteration +[info] # Threads: 1 thread, will synchronize iterations +[info] # Benchmark mode: Average time, time/op +[info] # Benchmark: benchmark.RegexBenchmark.email_accepting_regex_mem +[info] # Parameters: (size = 20) +[info] # Run progress: 64.38% complete, ETA 01:35:24 +[info] # Fork: 1 of 1 +[info] # Warmup Iteration 1: 1199.939 us/op +[info] # Warmup Iteration 2: 1153.249 us/op +[info] # Warmup Iteration 3: 1142.679 us/op +[info] # Warmup Iteration 4: 1150.173 us/op +[info] # Warmup Iteration 5: 1154.849 us/op +[info] Iteration 1: 1152.845 us/op +[info] Iteration 2: 1155.650 us/op +[info] Iteration 3: 1158.450 us/op +[info] Iteration 4: 1156.631 us/op +[info] Iteration 5: 1158.342 us/op +[info] Result "benchmark.RegexBenchmark.email_accepting_regex_mem": +[info] 1156.384 ±(99.9%) 8.869 us/op [Average] +[info] (min, avg, max) = (1152.845, 1156.384, 1158.450), stdev = 2.303 +[info] CI (99.9%): [1147.514, 1165.253] (assumes normal distribution) +[info] # JMH version: 1.32 +[info] # VM version: JDK 17.0.11, OpenJDK 64-Bit Server VM, 17.0.11+9 +[info] # VM invoker: /usr/lib/jvm/temurin-17-jdk-amd64/bin/java +[info] # VM options: +[info] # Blackhole mode: full + dont-inline hint +[info] # Warmup: 5 iterations, 10 s each +[info] # Measurement: 5 iterations, 10 s each +[info] # Timeout: 10 min per iteration +[info] # Threads: 1 thread, will synchronize iterations +[info] # Benchmark mode: Average time, time/op +[info] # Benchmark: benchmark.RegexBenchmark.email_accepting_regex_mem +[info] # Parameters: (size = 25) +[info] # Run progress: 65.00% complete, ETA 01:33:44 +[info] # Fork: 1 of 1 +[info] # Warmup Iteration 1: 1940.983 us/op +[info] # Warmup Iteration 2: 1864.592 us/op +[info] # Warmup Iteration 3: 1852.188 us/op +[info] # Warmup Iteration 4: 1863.763 us/op +[info] # Warmup Iteration 5: 1875.955 us/op +[info] Iteration 1: 1841.304 us/op +[info] Iteration 2: 1836.644 us/op +[info] Iteration 3: 1838.558 us/op +[info] Iteration 4: 1837.548 us/op +[info] Iteration 5: 1831.557 us/op +[info] Result "benchmark.RegexBenchmark.email_accepting_regex_mem": +[info] 1837.122 ±(99.9%) 13.741 us/op [Average] +[info] (min, avg, max) = (1831.557, 1837.122, 1841.304), stdev = 3.568 +[info] CI (99.9%): [1823.381, 1850.863] (assumes normal distribution) +[info] # JMH version: 1.32 +[info] # VM version: JDK 17.0.11, OpenJDK 64-Bit Server VM, 17.0.11+9 +[info] # VM invoker: /usr/lib/jvm/temurin-17-jdk-amd64/bin/java +[info] # VM options: +[info] # Blackhole mode: full + dont-inline hint +[info] # Warmup: 5 iterations, 10 s each +[info] # Measurement: 5 iterations, 10 s each +[info] # Timeout: 10 min per iteration +[info] # Threads: 1 thread, will synchronize iterations +[info] # Benchmark mode: Average time, time/op +[info] # Benchmark: benchmark.RegexBenchmark.email_accepting_regex_mem +[info] # Parameters: (size = 30) +[info] # Run progress: 65.63% complete, ETA 01:32:03 +[info] # Fork: 1 of 1 +[info] # Warmup Iteration 1: 3133.521 us/op +[info] # Warmup Iteration 2: 2990.577 us/op +[info] # Warmup Iteration 3: 2974.111 us/op +[info] # Warmup Iteration 4: 2959.848 us/op +[info] # Warmup Iteration 5: 2962.865 us/op +[info] Iteration 1: 2966.282 us/op +[info] Iteration 2: 2976.415 us/op +[info] Iteration 3: 2964.309 us/op +[info] Iteration 4: 2969.627 us/op +[info] Iteration 5: 2962.069 us/op +[info] Result "benchmark.RegexBenchmark.email_accepting_regex_mem": +[info] 2967.740 ±(99.9%) 21.515 us/op [Average] +[info] (min, avg, max) = (2962.069, 2967.740, 2976.415), stdev = 5.588 +[info] CI (99.9%): [2946.225, 2989.256] (assumes normal distribution) +[info] # JMH version: 1.32 +[info] # VM version: JDK 17.0.11, OpenJDK 64-Bit Server VM, 17.0.11+9 +[info] # VM invoker: /usr/lib/jvm/temurin-17-jdk-amd64/bin/java +[info] # VM options: +[info] # Blackhole mode: full + dont-inline hint +[info] # Warmup: 5 iterations, 10 s each +[info] # Measurement: 5 iterations, 10 s each +[info] # Timeout: 10 min per iteration +[info] # Threads: 1 thread, will synchronize iterations +[info] # Benchmark mode: Average time, time/op +[info] # Benchmark: benchmark.RegexBenchmark.email_accepting_regex_mem +[info] # Parameters: (size = 35) +[info] # Run progress: 66.25% complete, ETA 01:30:23 +[info] # Fork: 1 of 1 +[info] # Warmup Iteration 1: 3966.414 us/op +[info] # Warmup Iteration 2: 3747.557 us/op +[info] # Warmup Iteration 3: 3719.132 us/op +[info] # Warmup Iteration 4: 3688.067 us/op +[info] # Warmup Iteration 5: 3691.605 us/op +[info] Iteration 1: 3693.461 us/op +[info] Iteration 2: 3692.166 us/op +[info] Iteration 3: 3713.677 us/op +[info] Iteration 4: 3701.310 us/op +[info] Iteration 5: 3705.610 us/op +[info] Result "benchmark.RegexBenchmark.email_accepting_regex_mem": +[info] 3701.245 ±(99.9%) 34.258 us/op [Average] +[info] (min, avg, max) = (3692.166, 3701.245, 3713.677), stdev = 8.897 +[info] CI (99.9%): [3666.987, 3735.503] (assumes normal distribution) +[info] # JMH version: 1.32 +[info] # VM version: JDK 17.0.11, OpenJDK 64-Bit Server VM, 17.0.11+9 +[info] # VM invoker: /usr/lib/jvm/temurin-17-jdk-amd64/bin/java +[info] # VM options: +[info] # Blackhole mode: full + dont-inline hint +[info] # Warmup: 5 iterations, 10 s each +[info] # Measurement: 5 iterations, 10 s each +[info] # Timeout: 10 min per iteration +[info] # Threads: 1 thread, will synchronize iterations +[info] # Benchmark mode: Average time, time/op +[info] # Benchmark: benchmark.RegexBenchmark.email_accepting_regex_mem +[info] # Parameters: (size = 40) +[info] # Run progress: 66.88% complete, ETA 01:28:42 +[info] # Fork: 1 of 1 +[info] # Warmup Iteration 1: 8549.842 us/op +[info] # Warmup Iteration 2: 7998.262 us/op +[info] # Warmup Iteration 3: 8022.403 us/op +[info] # Warmup Iteration 4: 7949.163 us/op +[info] # Warmup Iteration 5: 7874.702 us/op +[info] Iteration 1: 7786.443 us/op +[info] Iteration 2: 7815.501 us/op +[info] Iteration 3: 7795.589 us/op +[info] Iteration 4: 7799.746 us/op +[info] Iteration 5: 7824.877 us/op +[info] Result "benchmark.RegexBenchmark.email_accepting_regex_mem": +[info] 7804.431 ±(99.9%) 59.788 us/op [Average] +[info] (min, avg, max) = (7786.443, 7804.431, 7824.877), stdev = 15.527 +[info] CI (99.9%): [7744.644, 7864.219] (assumes normal distribution) +[info] # JMH version: 1.32 +[info] # VM version: JDK 17.0.11, OpenJDK 64-Bit Server VM, 17.0.11+9 +[info] # VM invoker: /usr/lib/jvm/temurin-17-jdk-amd64/bin/java +[info] # VM options: +[info] # Blackhole mode: full + dont-inline hint +[info] # Warmup: 5 iterations, 10 s each +[info] # Measurement: 5 iterations, 10 s each +[info] # Timeout: 10 min per iteration +[info] # Threads: 1 thread, will synchronize iterations +[info] # Benchmark mode: Average time, time/op +[info] # Benchmark: benchmark.RegexBenchmark.email_accepting_regex_mem +[info] # Parameters: (size = 45) +[info] # Run progress: 67.50% complete, ETA 01:27:02 +[info] # Fork: 1 of 1 +[info] # Warmup Iteration 1: 7470.799 us/op +[info] # Warmup Iteration 2: 6687.043 us/op +[info] # Warmup Iteration 3: 6705.346 us/op +[info] # Warmup Iteration 4: 6653.787 us/op +[info] # Warmup Iteration 5: 6644.840 us/op +[info] Iteration 1: 6635.838 us/op +[info] Iteration 2: 6643.638 us/op +[info] Iteration 3: 6639.177 us/op +[info] Iteration 4: 6629.124 us/op +[info] Iteration 5: 6651.111 us/op +[info] Result "benchmark.RegexBenchmark.email_accepting_regex_mem": +[info] 6639.778 ±(99.9%) 31.797 us/op [Average] +[info] (min, avg, max) = (6629.124, 6639.778, 6651.111), stdev = 8.257 +[info] CI (99.9%): [6607.981, 6671.574] (assumes normal distribution) +[info] # JMH version: 1.32 +[info] # VM version: JDK 17.0.11, OpenJDK 64-Bit Server VM, 17.0.11+9 +[info] # VM invoker: /usr/lib/jvm/temurin-17-jdk-amd64/bin/java +[info] # VM options: +[info] # Blackhole mode: full + dont-inline hint +[info] # Warmup: 5 iterations, 10 s each +[info] # Measurement: 5 iterations, 10 s each +[info] # Timeout: 10 min per iteration +[info] # Threads: 1 thread, will synchronize iterations +[info] # Benchmark mode: Average time, time/op +[info] # Benchmark: benchmark.RegexBenchmark.email_accepting_regex_mem +[info] # Parameters: (size = 50) +[info] # Run progress: 68.13% complete, ETA 01:25:22 +[info] # Fork: 1 of 1 +[info] # Warmup Iteration 1: 13364.694 us/op +[info] # Warmup Iteration 2: 11707.379 us/op +[info] # Warmup Iteration 3: 11721.477 us/op +[info] # Warmup Iteration 4: 11704.387 us/op +[info] # Warmup Iteration 5: 11721.742 us/op +[info] Iteration 1: 11604.850 us/op +[info] Iteration 2: 11641.419 us/op +[info] Iteration 3: 11615.536 us/op +[info] Iteration 4: 11589.444 us/op +[info] Iteration 5: 11574.476 us/op +[info] Result "benchmark.RegexBenchmark.email_accepting_regex_mem": +[info] 11605.145 ±(99.9%) 98.380 us/op [Average] +[info] (min, avg, max) = (11574.476, 11605.145, 11641.419), stdev = 25.549 +[info] CI (99.9%): [11506.765, 11703.525] (assumes normal distribution) +[info] # JMH version: 1.32 +[info] # VM version: JDK 17.0.11, OpenJDK 64-Bit Server VM, 17.0.11+9 +[info] # VM invoker: /usr/lib/jvm/temurin-17-jdk-amd64/bin/java +[info] # VM options: +[info] # Blackhole mode: full + dont-inline hint +[info] # Warmup: 5 iterations, 10 s each +[info] # Measurement: 5 iterations, 10 s each +[info] # Timeout: 10 min per iteration +[info] # Threads: 1 thread, will synchronize iterations +[info] # Benchmark mode: Average time, time/op +[info] # Benchmark: benchmark.RegexBenchmark.email_accepting_regex_mem +[info] # Parameters: (size = 55) +[info] # Run progress: 68.75% complete, ETA 01:23:41 +[info] # Fork: 1 of 1 +[info] # Warmup Iteration 1: 14540.779 us/op +[info] # Warmup Iteration 2: 12323.876 us/op +[info] # Warmup Iteration 3: 12443.109 us/op +[info] # Warmup Iteration 4: 12331.735 us/op +[info] # Warmup Iteration 5: 12265.287 us/op +[info] Iteration 1: 12216.385 us/op +[info] Iteration 2: 12210.939 us/op +[info] Iteration 3: 12195.536 us/op +[info] Iteration 4: 12218.500 us/op +[info] Iteration 5: 12232.808 us/op +[info] Result "benchmark.RegexBenchmark.email_accepting_regex_mem": +[info] 12214.834 ±(99.9%) 51.894 us/op [Average] +[info] (min, avg, max) = (12195.536, 12214.834, 12232.808), stdev = 13.477 +[info] CI (99.9%): [12162.940, 12266.728] (assumes normal distribution) +[info] # JMH version: 1.32 +[info] # VM version: JDK 17.0.11, OpenJDK 64-Bit Server VM, 17.0.11+9 +[info] # VM invoker: /usr/lib/jvm/temurin-17-jdk-amd64/bin/java +[info] # VM options: +[info] # Blackhole mode: full + dont-inline hint +[info] # Warmup: 5 iterations, 10 s each +[info] # Measurement: 5 iterations, 10 s each +[info] # Timeout: 10 min per iteration +[info] # Threads: 1 thread, will synchronize iterations +[info] # Benchmark mode: Average time, time/op +[info] # Benchmark: benchmark.RegexBenchmark.email_accepting_regex_mem +[info] # Parameters: (size = 60) +[info] # Run progress: 69.38% complete, ETA 01:22:01 +[info] # Fork: 1 of 1 +[info] # Warmup Iteration 1: 15138.494 us/op +[info] # Warmup Iteration 2: 12166.785 us/op +[info] # Warmup Iteration 3: 12158.514 us/op +[info] # Warmup Iteration 4: 12168.363 us/op +[info] # Warmup Iteration 5: 12066.135 us/op +[info] Iteration 1: 12024.675 us/op +[info] Iteration 2: 12016.989 us/op +[info] Iteration 3: 12011.403 us/op +[info] Iteration 4: 12016.091 us/op +[info] Iteration 5: 12012.448 us/op +[info] Result "benchmark.RegexBenchmark.email_accepting_regex_mem": +[info] 12016.321 ±(99.9%) 20.146 us/op [Average] +[info] (min, avg, max) = (12011.403, 12016.321, 12024.675), stdev = 5.232 +[info] CI (99.9%): [11996.175, 12036.467] (assumes normal distribution) +[info] # JMH version: 1.32 +[info] # VM version: JDK 17.0.11, OpenJDK 64-Bit Server VM, 17.0.11+9 +[info] # VM invoker: /usr/lib/jvm/temurin-17-jdk-amd64/bin/java +[info] # VM options: +[info] # Blackhole mode: full + dont-inline hint +[info] # Warmup: 5 iterations, 10 s each +[info] # Measurement: 5 iterations, 10 s each +[info] # Timeout: 10 min per iteration +[info] # Threads: 1 thread, will synchronize iterations +[info] # Benchmark mode: Average time, time/op +[info] # Benchmark: benchmark.RegexBenchmark.email_accepting_regex_mem +[info] # Parameters: (size = 65) +[info] # Run progress: 70.00% complete, ETA 01:20:20 +[info] # Fork: 1 of 1 +[info] # Warmup Iteration 1: 20469.105 us/op +[info] # Warmup Iteration 2: 14486.904 us/op +[info] # Warmup Iteration 3: 14459.304 us/op +[info] # Warmup Iteration 4: 14468.363 us/op +[info] # Warmup Iteration 5: 14481.112 us/op +[info] Iteration 1: 14329.729 us/op +[info] Iteration 2: 14349.281 us/op +[info] Iteration 3: 14351.612 us/op +[info] Iteration 4: 14346.172 us/op +[info] Iteration 5: 14434.785 us/op +[info] Result "benchmark.RegexBenchmark.email_accepting_regex_mem": +[info] 14362.316 ±(99.9%) 159.451 us/op [Average] +[info] (min, avg, max) = (14329.729, 14362.316, 14434.785), stdev = 41.409 +[info] CI (99.9%): [14202.865, 14521.767] (assumes normal distribution) +[info] # JMH version: 1.32 +[info] # VM version: JDK 17.0.11, OpenJDK 64-Bit Server VM, 17.0.11+9 +[info] # VM invoker: /usr/lib/jvm/temurin-17-jdk-amd64/bin/java +[info] # VM options: +[info] # Blackhole mode: full + dont-inline hint +[info] # Warmup: 5 iterations, 10 s each +[info] # Measurement: 5 iterations, 10 s each +[info] # Timeout: 10 min per iteration +[info] # Threads: 1 thread, will synchronize iterations +[info] # Benchmark mode: Average time, time/op +[info] # Benchmark: benchmark.RegexBenchmark.email_accepting_regex_mem +[info] # Parameters: (size = 70) +[info] # Run progress: 70.63% complete, ETA 01:18:40 +[info] # Fork: 1 of 1 +[info] # Warmup Iteration 1: 31603.617 us/op +[info] # Warmup Iteration 2: 21043.553 us/op +[info] # Warmup Iteration 3: 21000.001 us/op +[info] # Warmup Iteration 4: 20883.357 us/op +[info] # Warmup Iteration 5: 20441.265 us/op +[info] Iteration 1: 20387.907 us/op +[info] Iteration 2: 20419.273 us/op +[info] Iteration 3: 20364.769 us/op +[info] Iteration 4: 20407.834 us/op +[info] Iteration 5: 20425.326 us/op +[info] Result "benchmark.RegexBenchmark.email_accepting_regex_mem": +[info] 20401.022 ±(99.9%) 95.424 us/op [Average] +[info] (min, avg, max) = (20364.769, 20401.022, 20425.326), stdev = 24.781 +[info] CI (99.9%): [20305.597, 20496.446] (assumes normal distribution) +[info] # JMH version: 1.32 +[info] # VM version: JDK 17.0.11, OpenJDK 64-Bit Server VM, 17.0.11+9 +[info] # VM invoker: /usr/lib/jvm/temurin-17-jdk-amd64/bin/java +[info] # VM options: +[info] # Blackhole mode: full + dont-inline hint +[info] # Warmup: 5 iterations, 10 s each +[info] # Measurement: 5 iterations, 10 s each +[info] # Timeout: 10 min per iteration +[info] # Threads: 1 thread, will synchronize iterations +[info] # Benchmark mode: Average time, time/op +[info] # Benchmark: benchmark.RegexBenchmark.email_accepting_regex_mem +[info] # Parameters: (size = 75) +[info] # Run progress: 71.25% complete, ETA 01:17:00 +[info] # Fork: 1 of 1 +[info] # Warmup Iteration 1: 47186.825 us/op +[info] # Warmup Iteration 2: 30151.324 us/op +[info] # Warmup Iteration 3: 30176.582 us/op +[info] # Warmup Iteration 4: 30182.051 us/op +[info] # Warmup Iteration 5: 30217.972 us/op +[info] Iteration 1: 30232.948 us/op +[info] Iteration 2: 30022.381 us/op +[info] Iteration 3: 29915.639 us/op +[info] Iteration 4: 29944.706 us/op +[info] Iteration 5: 29860.173 us/op +[info] Result "benchmark.RegexBenchmark.email_accepting_regex_mem": +[info] 29995.169 ±(99.9%) 559.255 us/op [Average] +[info] (min, avg, max) = (29860.173, 29995.169, 30232.948), stdev = 145.237 +[info] CI (99.9%): [29435.914, 30554.425] (assumes normal distribution) +[info] # JMH version: 1.32 +[info] # VM version: JDK 17.0.11, OpenJDK 64-Bit Server VM, 17.0.11+9 +[info] # VM invoker: /usr/lib/jvm/temurin-17-jdk-amd64/bin/java +[info] # VM options: +[info] # Blackhole mode: full + dont-inline hint +[info] # Warmup: 5 iterations, 10 s each +[info] # Measurement: 5 iterations, 10 s each +[info] # Timeout: 10 min per iteration +[info] # Threads: 1 thread, will synchronize iterations +[info] # Benchmark mode: Average time, time/op +[info] # Benchmark: benchmark.RegexBenchmark.email_accepting_regex_mem +[info] # Parameters: (size = 80) +[info] # Run progress: 71.88% complete, ETA 01:15:19 +[info] # Fork: 1 of 1 +[info] # Warmup Iteration 1: 52642.320 us/op +[info] # Warmup Iteration 2: 32155.574 us/op +[info] # Warmup Iteration 3: 32162.521 us/op +[info] # Warmup Iteration 4: 32116.602 us/op +[info] # Warmup Iteration 5: 32174.060 us/op +[info] Iteration 1: 32217.966 us/op +[info] Iteration 2: 32174.844 us/op +[info] Iteration 3: 31969.130 us/op +[info] Iteration 4: 32037.202 us/op +[info] Iteration 5: 32703.430 us/op +[info] Result "benchmark.RegexBenchmark.email_accepting_regex_mem": +[info] 32220.514 ±(99.9%) 1109.523 us/op [Average] +[info] (min, avg, max) = (31969.130, 32220.514, 32703.430), stdev = 288.140 +[info] CI (99.9%): [31110.991, 33330.037] (assumes normal distribution) +[info] # JMH version: 1.32 +[info] # VM version: JDK 17.0.11, OpenJDK 64-Bit Server VM, 17.0.11+9 +[info] # VM invoker: /usr/lib/jvm/temurin-17-jdk-amd64/bin/java +[info] # VM options: +[info] # Blackhole mode: full + dont-inline hint +[info] # Warmup: 5 iterations, 10 s each +[info] # Measurement: 5 iterations, 10 s each +[info] # Timeout: 10 min per iteration +[info] # Threads: 1 thread, will synchronize iterations +[info] # Benchmark mode: Average time, time/op +[info] # Benchmark: benchmark.RegexBenchmark.email_accepting_regex_mem +[info] # Parameters: (size = 85) +[info] # Run progress: 72.50% complete, ETA 01:13:39 +[info] # Fork: 1 of 1 +[info] # Warmup Iteration 1: 78797.771 us/op +[info] # Warmup Iteration 2: 35308.136 us/op +[info] # Warmup Iteration 3: 35324.019 us/op +[info] # Warmup Iteration 4: 34844.616 us/op +[info] # Warmup Iteration 5: 34506.204 us/op +[info] Iteration 1: 34450.308 us/op +[info] Iteration 2: 34252.408 us/op +[info] Iteration 3: 34233.567 us/op +[info] Iteration 4: 34214.403 us/op +[info] Iteration 5: 34206.000 us/op +[info] Result "benchmark.RegexBenchmark.email_accepting_regex_mem": +[info] 34271.337 ±(99.9%) 391.395 us/op [Average] +[info] (min, avg, max) = (34206.000, 34271.337, 34450.308), stdev = 101.644 +[info] CI (99.9%): [33879.942, 34662.732] (assumes normal distribution) +[info] # JMH version: 1.32 +[info] # VM version: JDK 17.0.11, OpenJDK 64-Bit Server VM, 17.0.11+9 +[info] # VM invoker: /usr/lib/jvm/temurin-17-jdk-amd64/bin/java +[info] # VM options: +[info] # Blackhole mode: full + dont-inline hint +[info] # Warmup: 5 iterations, 10 s each +[info] # Measurement: 5 iterations, 10 s each +[info] # Timeout: 10 min per iteration +[info] # Threads: 1 thread, will synchronize iterations +[info] # Benchmark mode: Average time, time/op +[info] # Benchmark: benchmark.RegexBenchmark.email_accepting_regex_mem +[info] # Parameters: (size = 90) +[info] # Run progress: 73.13% complete, ETA 01:11:58 +[info] # Fork: 1 of 1 +[info] # Warmup Iteration 1: 123711.719 us/op +[info] # Warmup Iteration 2: 38404.424 us/op +[info] # Warmup Iteration 3: 38377.964 us/op +[info] # Warmup Iteration 4: 38283.190 us/op +[info] # Warmup Iteration 5: 38204.194 us/op +[info] Iteration 1: 38144.184 us/op +[info] Iteration 2: 38161.868 us/op +[info] Iteration 3: 38043.646 us/op +[info] Iteration 4: 38015.024 us/op +[info] Iteration 5: 37969.378 us/op +[info] Result "benchmark.RegexBenchmark.email_accepting_regex_mem": +[info] 38066.820 ±(99.9%) 320.635 us/op [Average] +[info] (min, avg, max) = (37969.378, 38066.820, 38161.868), stdev = 83.268 +[info] CI (99.9%): [37746.185, 38387.455] (assumes normal distribution) +[info] # JMH version: 1.32 +[info] # VM version: JDK 17.0.11, OpenJDK 64-Bit Server VM, 17.0.11+9 +[info] # VM invoker: /usr/lib/jvm/temurin-17-jdk-amd64/bin/java +[info] # VM options: +[info] # Blackhole mode: full + dont-inline hint +[info] # Warmup: 5 iterations, 10 s each +[info] # Measurement: 5 iterations, 10 s each +[info] # Timeout: 10 min per iteration +[info] # Threads: 1 thread, will synchronize iterations +[info] # Benchmark mode: Average time, time/op +[info] # Benchmark: benchmark.RegexBenchmark.email_accepting_regex_mem +[info] # Parameters: (size = 95) +[info] # Run progress: 73.75% complete, ETA 01:10:18 +[info] # Fork: 1 of 1 +[info] # Warmup Iteration 1: 345333.557 us/op +[info] # Warmup Iteration 2: 40810.558 us/op +[info] # Warmup Iteration 3: 40837.849 us/op +[info] # Warmup Iteration 4: 40920.766 us/op +[info] # Warmup Iteration 5: 40918.210 us/op +[info] Iteration 1: 40885.270 us/op +[info] Iteration 2: 40843.038 us/op +[info] Iteration 3: 40621.682 us/op +[info] Iteration 4: 40618.817 us/op +[info] Iteration 5: 40581.796 us/op +[info] Result "benchmark.RegexBenchmark.email_accepting_regex_mem": +[info] 40710.121 ±(99.9%) 547.851 us/op [Average] +[info] (min, avg, max) = (40581.796, 40710.121, 40885.270), stdev = 142.275 +[info] CI (99.9%): [40162.269, 41257.972] (assumes normal distribution) +[info] # JMH version: 1.32 +[info] # VM version: JDK 17.0.11, OpenJDK 64-Bit Server VM, 17.0.11+9 +[info] # VM invoker: /usr/lib/jvm/temurin-17-jdk-amd64/bin/java +[info] # VM options: +[info] # Blackhole mode: full + dont-inline hint +[info] # Warmup: 5 iterations, 10 s each +[info] # Measurement: 5 iterations, 10 s each +[info] # Timeout: 10 min per iteration +[info] # Threads: 1 thread, will synchronize iterations +[info] # Benchmark mode: Average time, time/op +[info] # Benchmark: benchmark.RegexBenchmark.email_accepting_regex_mem +[info] # Parameters: (size = 100) +[info] # Run progress: 74.38% complete, ETA 01:08:38 +[info] # Fork: 1 of 1 +[info] # Warmup Iteration 1: 400633.244 us/op +[info] # Warmup Iteration 2: 55881.875 us/op +[info] # Warmup Iteration 3: 55828.108 us/op +[info] # Warmup Iteration 4: 55920.780 us/op +[info] # Warmup Iteration 5: 55836.719 us/op +[info] Iteration 1: 55628.816 us/op +[info] Iteration 2: 55599.953 us/op +[info] Iteration 3: 55433.824 us/op +[info] Iteration 4: 55510.249 us/op +[info] Iteration 5: 55511.428 us/op +[info] Result "benchmark.RegexBenchmark.email_accepting_regex_mem": +[info] 55536.854 ±(99.9%) 300.793 us/op [Average] +[info] (min, avg, max) = (55433.824, 55536.854, 55628.816), stdev = 78.115 +[info] CI (99.9%): [55236.061, 55837.647] (assumes normal distribution) +[info] # JMH version: 1.32 +[info] # VM version: JDK 17.0.11, OpenJDK 64-Bit Server VM, 17.0.11+9 +[info] # VM invoker: /usr/lib/jvm/temurin-17-jdk-amd64/bin/java +[info] # VM options: +[info] # Blackhole mode: full + dont-inline hint +[info] # Warmup: 5 iterations, 10 s each +[info] # Measurement: 5 iterations, 10 s each +[info] # Timeout: 10 min per iteration +[info] # Threads: 1 thread, will synchronize iterations +[info] # Benchmark mode: Average time, time/op +[info] # Benchmark: benchmark.RegexBenchmark.email_accepting_zipper +[info] # Parameters: (size = 5) +[info] # Run progress: 75.00% complete, ETA 01:06:57 +[info] # Fork: 1 of 1 +[info] # Warmup Iteration 1: 28.184 us/op +[info] # Warmup Iteration 2: 26.035 us/op +[info] # Warmup Iteration 3: 25.969 us/op +[info] # Warmup Iteration 4: 25.750 us/op +[info] # Warmup Iteration 5: 25.656 us/op +[info] Iteration 1: 25.481 us/op +[info] Iteration 2: 25.553 us/op +[info] Iteration 3: 25.875 us/op +[info] Iteration 4: 25.500 us/op +[info] Iteration 5: 25.453 us/op +[info] Result "benchmark.RegexBenchmark.email_accepting_zipper": +[info] 25.572 ±(99.9%) 0.665 us/op [Average] +[info] (min, avg, max) = (25.453, 25.572, 25.875), stdev = 0.173 +[info] CI (99.9%): [24.907, 26.238] (assumes normal distribution) +[info] # JMH version: 1.32 +[info] # VM version: JDK 17.0.11, OpenJDK 64-Bit Server VM, 17.0.11+9 +[info] # VM invoker: /usr/lib/jvm/temurin-17-jdk-amd64/bin/java +[info] # VM options: +[info] # Blackhole mode: full + dont-inline hint +[info] # Warmup: 5 iterations, 10 s each +[info] # Measurement: 5 iterations, 10 s each +[info] # Timeout: 10 min per iteration +[info] # Threads: 1 thread, will synchronize iterations +[info] # Benchmark mode: Average time, time/op +[info] # Benchmark: benchmark.RegexBenchmark.email_accepting_zipper +[info] # Parameters: (size = 10) +[info] # Run progress: 75.63% complete, ETA 01:05:17 +[info] # Fork: 1 of 1 +[info] # Warmup Iteration 1: 69.800 us/op +[info] # Warmup Iteration 2: 64.274 us/op +[info] # Warmup Iteration 3: 64.928 us/op +[info] # Warmup Iteration 4: 63.330 us/op +[info] # Warmup Iteration 5: 64.292 us/op +[info] Iteration 1: 63.804 us/op +[info] Iteration 2: 63.516 us/op +[info] Iteration 3: 63.393 us/op +[info] Iteration 4: 63.880 us/op +[info] Iteration 5: 63.218 us/op +[info] Result "benchmark.RegexBenchmark.email_accepting_zipper": +[info] 63.562 ±(99.9%) 1.069 us/op [Average] +[info] (min, avg, max) = (63.218, 63.562, 63.880), stdev = 0.278 +[info] CI (99.9%): [62.493, 64.631] (assumes normal distribution) +[info] # JMH version: 1.32 +[info] # VM version: JDK 17.0.11, OpenJDK 64-Bit Server VM, 17.0.11+9 +[info] # VM invoker: /usr/lib/jvm/temurin-17-jdk-amd64/bin/java +[info] # VM options: +[info] # Blackhole mode: full + dont-inline hint +[info] # Warmup: 5 iterations, 10 s each +[info] # Measurement: 5 iterations, 10 s each +[info] # Timeout: 10 min per iteration +[info] # Threads: 1 thread, will synchronize iterations +[info] # Benchmark mode: Average time, time/op +[info] # Benchmark: benchmark.RegexBenchmark.email_accepting_zipper +[info] # Parameters: (size = 15) +[info] # Run progress: 76.25% complete, ETA 01:03:36 +[info] # Fork: 1 of 1 +[info] # Warmup Iteration 1: 76.442 us/op +[info] # Warmup Iteration 2: 70.444 us/op +[info] # Warmup Iteration 3: 72.072 us/op +[info] # Warmup Iteration 4: 71.012 us/op +[info] # Warmup Iteration 5: 70.263 us/op +[info] Iteration 1: 69.227 us/op +[info] Iteration 2: 70.159 us/op +[info] Iteration 3: 69.248 us/op +[info] Iteration 4: 69.937 us/op +[info] Iteration 5: 69.322 us/op +[info] Result "benchmark.RegexBenchmark.email_accepting_zipper": +[info] 69.579 ±(99.9%) 1.683 us/op [Average] +[info] (min, avg, max) = (69.227, 69.579, 70.159), stdev = 0.437 +[info] CI (99.9%): [67.895, 71.262] (assumes normal distribution) +[info] # JMH version: 1.32 +[info] # VM version: JDK 17.0.11, OpenJDK 64-Bit Server VM, 17.0.11+9 +[info] # VM invoker: /usr/lib/jvm/temurin-17-jdk-amd64/bin/java +[info] # VM options: +[info] # Blackhole mode: full + dont-inline hint +[info] # Warmup: 5 iterations, 10 s each +[info] # Measurement: 5 iterations, 10 s each +[info] # Timeout: 10 min per iteration +[info] # Threads: 1 thread, will synchronize iterations +[info] # Benchmark mode: Average time, time/op +[info] # Benchmark: benchmark.RegexBenchmark.email_accepting_zipper +[info] # Parameters: (size = 20) +[info] # Run progress: 76.88% complete, ETA 01:01:56 +[info] # Fork: 1 of 1 +[info] # Warmup Iteration 1: 103.166 us/op +[info] # Warmup Iteration 2: 96.112 us/op +[info] # Warmup Iteration 3: 95.945 us/op +[info] # Warmup Iteration 4: 96.342 us/op +[info] # Warmup Iteration 5: 95.925 us/op +[info] Iteration 1: 95.570 us/op +[info] Iteration 2: 95.233 us/op +[info] Iteration 3: 96.440 us/op +[info] Iteration 4: 94.819 us/op +[info] Iteration 5: 95.120 us/op +[info] Result "benchmark.RegexBenchmark.email_accepting_zipper": +[info] 95.436 ±(99.9%) 2.396 us/op [Average] +[info] (min, avg, max) = (94.819, 95.436, 96.440), stdev = 0.622 +[info] CI (99.9%): [93.041, 97.832] (assumes normal distribution) +[info] # JMH version: 1.32 +[info] # VM version: JDK 17.0.11, OpenJDK 64-Bit Server VM, 17.0.11+9 +[info] # VM invoker: /usr/lib/jvm/temurin-17-jdk-amd64/bin/java +[info] # VM options: +[info] # Blackhole mode: full + dont-inline hint +[info] # Warmup: 5 iterations, 10 s each +[info] # Measurement: 5 iterations, 10 s each +[info] # Timeout: 10 min per iteration +[info] # Threads: 1 thread, will synchronize iterations +[info] # Benchmark mode: Average time, time/op +[info] # Benchmark: benchmark.RegexBenchmark.email_accepting_zipper +[info] # Parameters: (size = 25) +[info] # Run progress: 77.50% complete, ETA 01:00:15 +[info] # Fork: 1 of 1 +[info] # Warmup Iteration 1: 153.241 us/op +[info] # Warmup Iteration 2: 144.379 us/op +[info] # Warmup Iteration 3: 144.535 us/op +[info] # Warmup Iteration 4: 143.978 us/op +[info] # Warmup Iteration 5: 142.062 us/op +[info] Iteration 1: 143.677 us/op +[info] Iteration 2: 143.069 us/op +[info] Iteration 3: 142.750 us/op +[info] Iteration 4: 141.672 us/op +[info] Iteration 5: 145.143 us/op +[info] Result "benchmark.RegexBenchmark.email_accepting_zipper": +[info] 143.262 ±(99.9%) 4.923 us/op [Average] +[info] (min, avg, max) = (141.672, 143.262, 145.143), stdev = 1.279 +[info] CI (99.9%): [138.339, 148.186] (assumes normal distribution) +[info] # JMH version: 1.32 +[info] # VM version: JDK 17.0.11, OpenJDK 64-Bit Server VM, 17.0.11+9 +[info] # VM invoker: /usr/lib/jvm/temurin-17-jdk-amd64/bin/java +[info] # VM options: +[info] # Blackhole mode: full + dont-inline hint +[info] # Warmup: 5 iterations, 10 s each +[info] # Measurement: 5 iterations, 10 s each +[info] # Timeout: 10 min per iteration +[info] # Threads: 1 thread, will synchronize iterations +[info] # Benchmark mode: Average time, time/op +[info] # Benchmark: benchmark.RegexBenchmark.email_accepting_zipper +[info] # Parameters: (size = 30) +[info] # Run progress: 78.13% complete, ETA 00:58:35 +[info] # Fork: 1 of 1 +[info] # Warmup Iteration 1: 207.316 us/op +[info] # Warmup Iteration 2: 190.819 us/op +[info] # Warmup Iteration 3: 195.058 us/op +[info] # Warmup Iteration 4: 190.929 us/op +[info] # Warmup Iteration 5: 188.109 us/op +[info] Iteration 1: 187.580 us/op +[info] Iteration 2: 187.216 us/op +[info] Iteration 3: 185.825 us/op +[info] Iteration 4: 186.036 us/op +[info] Iteration 5: 185.760 us/op +[info] Result "benchmark.RegexBenchmark.email_accepting_zipper": +[info] 186.483 ±(99.9%) 3.277 us/op [Average] +[info] (min, avg, max) = (185.760, 186.483, 187.580), stdev = 0.851 +[info] CI (99.9%): [183.207, 189.760] (assumes normal distribution) +[info] # JMH version: 1.32 +[info] # VM version: JDK 17.0.11, OpenJDK 64-Bit Server VM, 17.0.11+9 +[info] # VM invoker: /usr/lib/jvm/temurin-17-jdk-amd64/bin/java +[info] # VM options: +[info] # Blackhole mode: full + dont-inline hint +[info] # Warmup: 5 iterations, 10 s each +[info] # Measurement: 5 iterations, 10 s each +[info] # Timeout: 10 min per iteration +[info] # Threads: 1 thread, will synchronize iterations +[info] # Benchmark mode: Average time, time/op +[info] # Benchmark: benchmark.RegexBenchmark.email_accepting_zipper +[info] # Parameters: (size = 35) +[info] # Run progress: 78.75% complete, ETA 00:56:55 +[info] # Fork: 1 of 1 +[info] # Warmup Iteration 1: 180.725 us/op +[info] # Warmup Iteration 2: 169.778 us/op +[info] # Warmup Iteration 3: 169.730 us/op +[info] # Warmup Iteration 4: 169.152 us/op +[info] # Warmup Iteration 5: 166.637 us/op +[info] Iteration 1: 169.256 us/op +[info] Iteration 2: 168.438 us/op +[info] Iteration 3: 167.909 us/op +[info] Iteration 4: 166.101 us/op +[info] Iteration 5: 166.218 us/op +[info] Result "benchmark.RegexBenchmark.email_accepting_zipper": +[info] 167.585 ±(99.9%) 5.342 us/op [Average] +[info] (min, avg, max) = (166.101, 167.585, 169.256), stdev = 1.387 +[info] CI (99.9%): [162.243, 172.926] (assumes normal distribution) +[info] # JMH version: 1.32 +[info] # VM version: JDK 17.0.11, OpenJDK 64-Bit Server VM, 17.0.11+9 +[info] # VM invoker: /usr/lib/jvm/temurin-17-jdk-amd64/bin/java +[info] # VM options: +[info] # Blackhole mode: full + dont-inline hint +[info] # Warmup: 5 iterations, 10 s each +[info] # Measurement: 5 iterations, 10 s each +[info] # Timeout: 10 min per iteration +[info] # Threads: 1 thread, will synchronize iterations +[info] # Benchmark mode: Average time, time/op +[info] # Benchmark: benchmark.RegexBenchmark.email_accepting_zipper +[info] # Parameters: (size = 40) +[info] # Run progress: 79.38% complete, ETA 00:55:14 +[info] # Fork: 1 of 1 +[info] # Warmup Iteration 1: 355.211 us/op +[info] # Warmup Iteration 2: 327.502 us/op +[info] # Warmup Iteration 3: 328.642 us/op +[info] # Warmup Iteration 4: 326.480 us/op +[info] # Warmup Iteration 5: 325.115 us/op +[info] Iteration 1: 324.017 us/op +[info] Iteration 2: 323.067 us/op +[info] Iteration 3: 327.579 us/op +[info] Iteration 4: 326.534 us/op +[info] Iteration 5: 332.772 us/op +[info] Result "benchmark.RegexBenchmark.email_accepting_zipper": +[info] 326.794 ±(99.9%) 14.666 us/op [Average] +[info] (min, avg, max) = (323.067, 326.794, 332.772), stdev = 3.809 +[info] CI (99.9%): [312.128, 341.460] (assumes normal distribution) +[info] # JMH version: 1.32 +[info] # VM version: JDK 17.0.11, OpenJDK 64-Bit Server VM, 17.0.11+9 +[info] # VM invoker: /usr/lib/jvm/temurin-17-jdk-amd64/bin/java +[info] # VM options: +[info] # Blackhole mode: full + dont-inline hint +[info] # Warmup: 5 iterations, 10 s each +[info] # Measurement: 5 iterations, 10 s each +[info] # Timeout: 10 min per iteration +[info] # Threads: 1 thread, will synchronize iterations +[info] # Benchmark mode: Average time, time/op +[info] # Benchmark: benchmark.RegexBenchmark.email_accepting_zipper +[info] # Parameters: (size = 45) +[info] # Run progress: 80.00% complete, ETA 00:53:34 +[info] # Fork: 1 of 1 +[info] # Warmup Iteration 1: 289.460 us/op +[info] # Warmup Iteration 2: 264.369 us/op +[info] # Warmup Iteration 3: 267.850 us/op +[info] # Warmup Iteration 4: 260.243 us/op +[info] # Warmup Iteration 5: 260.710 us/op +[info] Iteration 1: 260.295 us/op +[info] Iteration 2: 260.195 us/op +[info] Iteration 3: 261.785 us/op +[info] Iteration 4: 258.634 us/op +[info] Iteration 5: 258.982 us/op +[info] Result "benchmark.RegexBenchmark.email_accepting_zipper": +[info] 259.978 ±(99.9%) 4.798 us/op [Average] +[info] (min, avg, max) = (258.634, 259.978, 261.785), stdev = 1.246 +[info] CI (99.9%): [255.180, 264.776] (assumes normal distribution) +[info] # JMH version: 1.32 +[info] # VM version: JDK 17.0.11, OpenJDK 64-Bit Server VM, 17.0.11+9 +[info] # VM invoker: /usr/lib/jvm/temurin-17-jdk-amd64/bin/java +[info] # VM options: +[info] # Blackhole mode: full + dont-inline hint +[info] # Warmup: 5 iterations, 10 s each +[info] # Measurement: 5 iterations, 10 s each +[info] # Timeout: 10 min per iteration +[info] # Threads: 1 thread, will synchronize iterations +[info] # Benchmark mode: Average time, time/op +[info] # Benchmark: benchmark.RegexBenchmark.email_accepting_zipper +[info] # Parameters: (size = 50) +[info] # Run progress: 80.63% complete, ETA 00:51:53 +[info] # Fork: 1 of 1 +[info] # Warmup Iteration 1: 422.703 us/op +[info] # Warmup Iteration 2: 390.443 us/op +[info] # Warmup Iteration 3: 389.101 us/op +[info] # Warmup Iteration 4: 390.131 us/op +[info] # Warmup Iteration 5: 385.956 us/op +[info] Iteration 1: 386.232 us/op +[info] Iteration 2: 383.872 us/op +[info] Iteration 3: 388.063 us/op +[info] Iteration 4: 382.687 us/op +[info] Iteration 5: 383.846 us/op +[info] Result "benchmark.RegexBenchmark.email_accepting_zipper": +[info] 384.940 ±(99.9%) 8.356 us/op [Average] +[info] (min, avg, max) = (382.687, 384.940, 388.063), stdev = 2.170 +[info] CI (99.9%): [376.584, 393.296] (assumes normal distribution) +[info] # JMH version: 1.32 +[info] # VM version: JDK 17.0.11, OpenJDK 64-Bit Server VM, 17.0.11+9 +[info] # VM invoker: /usr/lib/jvm/temurin-17-jdk-amd64/bin/java +[info] # VM options: +[info] # Blackhole mode: full + dont-inline hint +[info] # Warmup: 5 iterations, 10 s each +[info] # Measurement: 5 iterations, 10 s each +[info] # Timeout: 10 min per iteration +[info] # Threads: 1 thread, will synchronize iterations +[info] # Benchmark mode: Average time, time/op +[info] # Benchmark: benchmark.RegexBenchmark.email_accepting_zipper +[info] # Parameters: (size = 55) +[info] # Run progress: 81.25% complete, ETA 00:50:13 +[info] # Fork: 1 of 1 +[info] # Warmup Iteration 1: 420.984 us/op +[info] # Warmup Iteration 2: 384.213 us/op +[info] # Warmup Iteration 3: 390.245 us/op +[info] # Warmup Iteration 4: 386.869 us/op +[info] # Warmup Iteration 5: 385.014 us/op +[info] Iteration 1: 381.183 us/op +[info] Iteration 2: 387.640 us/op +[info] Iteration 3: 380.728 us/op +[info] Iteration 4: 381.506 us/op +[info] Iteration 5: 384.683 us/op +[info] Result "benchmark.RegexBenchmark.email_accepting_zipper": +[info] 383.148 ±(99.9%) 11.382 us/op [Average] +[info] (min, avg, max) = (380.728, 383.148, 387.640), stdev = 2.956 +[info] CI (99.9%): [371.765, 394.530] (assumes normal distribution) +[info] # JMH version: 1.32 +[info] # VM version: JDK 17.0.11, OpenJDK 64-Bit Server VM, 17.0.11+9 +[info] # VM invoker: /usr/lib/jvm/temurin-17-jdk-amd64/bin/java +[info] # VM options: +[info] # Blackhole mode: full + dont-inline hint +[info] # Warmup: 5 iterations, 10 s each +[info] # Measurement: 5 iterations, 10 s each +[info] # Timeout: 10 min per iteration +[info] # Threads: 1 thread, will synchronize iterations +[info] # Benchmark mode: Average time, time/op +[info] # Benchmark: benchmark.RegexBenchmark.email_accepting_zipper +[info] # Parameters: (size = 60) +[info] # Run progress: 81.88% complete, ETA 00:48:32 +[info] # Fork: 1 of 1 +[info] # Warmup Iteration 1: 345.902 us/op +[info] # Warmup Iteration 2: 321.967 us/op +[info] # Warmup Iteration 3: 315.542 us/op +[info] # Warmup Iteration 4: 317.021 us/op +[info] # Warmup Iteration 5: 312.184 us/op +[info] Iteration 1: 306.610 us/op +[info] Iteration 2: 309.528 us/op +[info] Iteration 3: 308.354 us/op +[info] Iteration 4: 306.739 us/op +[info] Iteration 5: 310.893 us/op +[info] Result "benchmark.RegexBenchmark.email_accepting_zipper": +[info] 308.425 ±(99.9%) 7.061 us/op [Average] +[info] (min, avg, max) = (306.610, 308.425, 310.893), stdev = 1.834 +[info] CI (99.9%): [301.364, 315.486] (assumes normal distribution) +[info] # JMH version: 1.32 +[info] # VM version: JDK 17.0.11, OpenJDK 64-Bit Server VM, 17.0.11+9 +[info] # VM invoker: /usr/lib/jvm/temurin-17-jdk-amd64/bin/java +[info] # VM options: +[info] # Blackhole mode: full + dont-inline hint +[info] # Warmup: 5 iterations, 10 s each +[info] # Measurement: 5 iterations, 10 s each +[info] # Timeout: 10 min per iteration +[info] # Threads: 1 thread, will synchronize iterations +[info] # Benchmark mode: Average time, time/op +[info] # Benchmark: benchmark.RegexBenchmark.email_accepting_zipper +[info] # Parameters: (size = 65) +[info] # Run progress: 82.50% complete, ETA 00:46:52 +[info] # Fork: 1 of 1 +[info] # Warmup Iteration 1: 330.025 us/op +[info] # Warmup Iteration 2: 308.328 us/op +[info] # Warmup Iteration 3: 300.433 us/op +[info] # Warmup Iteration 4: 306.565 us/op +[info] # Warmup Iteration 5: 305.774 us/op +[info] Iteration 1: 301.368 us/op +[info] Iteration 2: 305.593 us/op +[info] Iteration 3: 300.768 us/op +[info] Iteration 4: 300.911 us/op +[info] Iteration 5: 301.710 us/op +[info] Result "benchmark.RegexBenchmark.email_accepting_zipper": +[info] 302.070 ±(99.9%) 7.719 us/op [Average] +[info] (min, avg, max) = (300.768, 302.070, 305.593), stdev = 2.005 +[info] CI (99.9%): [294.351, 309.789] (assumes normal distribution) +[info] # JMH version: 1.32 +[info] # VM version: JDK 17.0.11, OpenJDK 64-Bit Server VM, 17.0.11+9 +[info] # VM invoker: /usr/lib/jvm/temurin-17-jdk-amd64/bin/java +[info] # VM options: +[info] # Blackhole mode: full + dont-inline hint +[info] # Warmup: 5 iterations, 10 s each +[info] # Measurement: 5 iterations, 10 s each +[info] # Timeout: 10 min per iteration +[info] # Threads: 1 thread, will synchronize iterations +[info] # Benchmark mode: Average time, time/op +[info] # Benchmark: benchmark.RegexBenchmark.email_accepting_zipper +[info] # Parameters: (size = 70) +[info] # Run progress: 83.13% complete, ETA 00:45:11 +[info] # Fork: 1 of 1 +[info] # Warmup Iteration 1: 543.578 us/op +[info] # Warmup Iteration 2: 502.094 us/op +[info] # Warmup Iteration 3: 495.595 us/op +[info] # Warmup Iteration 4: 502.743 us/op +[info] # Warmup Iteration 5: 492.672 us/op +[info] Iteration 1: 502.186 us/op +[info] Iteration 2: 496.236 us/op +[info] Iteration 3: 492.680 us/op +[info] Iteration 4: 498.657 us/op +[info] Iteration 5: 501.158 us/op +[info] Result "benchmark.RegexBenchmark.email_accepting_zipper": +[info] 498.183 ±(99.9%) 14.809 us/op [Average] +[info] (min, avg, max) = (492.680, 498.183, 502.186), stdev = 3.846 +[info] CI (99.9%): [483.374, 512.993] (assumes normal distribution) +[info] # JMH version: 1.32 +[info] # VM version: JDK 17.0.11, OpenJDK 64-Bit Server VM, 17.0.11+9 +[info] # VM invoker: /usr/lib/jvm/temurin-17-jdk-amd64/bin/java +[info] # VM options: +[info] # Blackhole mode: full + dont-inline hint +[info] # Warmup: 5 iterations, 10 s each +[info] # Measurement: 5 iterations, 10 s each +[info] # Timeout: 10 min per iteration +[info] # Threads: 1 thread, will synchronize iterations +[info] # Benchmark mode: Average time, time/op +[info] # Benchmark: benchmark.RegexBenchmark.email_accepting_zipper +[info] # Parameters: (size = 75) +[info] # Run progress: 83.75% complete, ETA 00:43:31 +[info] # Fork: 1 of 1 +[info] # Warmup Iteration 1: 671.411 us/op +[info] # Warmup Iteration 2: 619.064 us/op +[info] # Warmup Iteration 3: 610.533 us/op +[info] # Warmup Iteration 4: 598.814 us/op +[info] # Warmup Iteration 5: 609.869 us/op +[info] Iteration 1: 600.752 us/op +[info] Iteration 2: 609.137 us/op +[info] Iteration 3: 605.453 us/op +[info] Iteration 4: 601.137 us/op +[info] Iteration 5: 600.129 us/op +[info] Result "benchmark.RegexBenchmark.email_accepting_zipper": +[info] 603.322 ±(99.9%) 14.904 us/op [Average] +[info] (min, avg, max) = (600.129, 603.322, 609.137), stdev = 3.871 +[info] CI (99.9%): [588.417, 618.226] (assumes normal distribution) +[info] # JMH version: 1.32 +[info] # VM version: JDK 17.0.11, OpenJDK 64-Bit Server VM, 17.0.11+9 +[info] # VM invoker: /usr/lib/jvm/temurin-17-jdk-amd64/bin/java +[info] # VM options: +[info] # Blackhole mode: full + dont-inline hint +[info] # Warmup: 5 iterations, 10 s each +[info] # Measurement: 5 iterations, 10 s each +[info] # Timeout: 10 min per iteration +[info] # Threads: 1 thread, will synchronize iterations +[info] # Benchmark mode: Average time, time/op +[info] # Benchmark: benchmark.RegexBenchmark.email_accepting_zipper +[info] # Parameters: (size = 80) +[info] # Run progress: 84.38% complete, ETA 00:41:51 +[info] # Fork: 1 of 1 +[info] # Warmup Iteration 1: 728.636 us/op +[info] # Warmup Iteration 2: 678.521 us/op +[info] # Warmup Iteration 3: 669.452 us/op +[info] # Warmup Iteration 4: 676.114 us/op +[info] # Warmup Iteration 5: 680.157 us/op +[info] Iteration 1: 665.299 us/op +[info] Iteration 2: 677.489 us/op +[info] Iteration 3: 666.645 us/op +[info] Iteration 4: 664.572 us/op +[info] Iteration 5: 665.137 us/op +[info] Result "benchmark.RegexBenchmark.email_accepting_zipper": +[info] 667.829 ±(99.9%) 21.001 us/op [Average] +[info] (min, avg, max) = (664.572, 667.829, 677.489), stdev = 5.454 +[info] CI (99.9%): [646.828, 688.829] (assumes normal distribution) +[info] # JMH version: 1.32 +[info] # VM version: JDK 17.0.11, OpenJDK 64-Bit Server VM, 17.0.11+9 +[info] # VM invoker: /usr/lib/jvm/temurin-17-jdk-amd64/bin/java +[info] # VM options: +[info] # Blackhole mode: full + dont-inline hint +[info] # Warmup: 5 iterations, 10 s each +[info] # Measurement: 5 iterations, 10 s each +[info] # Timeout: 10 min per iteration +[info] # Threads: 1 thread, will synchronize iterations +[info] # Benchmark mode: Average time, time/op +[info] # Benchmark: benchmark.RegexBenchmark.email_accepting_zipper +[info] # Parameters: (size = 85) +[info] # Run progress: 85.00% complete, ETA 00:40:10 +[info] # Fork: 1 of 1 +[info] # Warmup Iteration 1: 712.914 us/op +[info] # Warmup Iteration 2: 660.407 us/op +[info] # Warmup Iteration 3: 654.548 us/op +[info] # Warmup Iteration 4: 653.327 us/op +[info] # Warmup Iteration 5: 657.296 us/op +[info] Iteration 1: 653.864 us/op +[info] Iteration 2: 652.762 us/op +[info] Iteration 3: 648.883 us/op +[info] Iteration 4: 661.532 us/op +[info] Iteration 5: 668.393 us/op +[info] Result "benchmark.RegexBenchmark.email_accepting_zipper": +[info] 657.087 ±(99.9%) 30.074 us/op [Average] +[info] (min, avg, max) = (648.883, 657.087, 668.393), stdev = 7.810 +[info] CI (99.9%): [627.013, 687.161] (assumes normal distribution) +[info] # JMH version: 1.32 +[info] # VM version: JDK 17.0.11, OpenJDK 64-Bit Server VM, 17.0.11+9 +[info] # VM invoker: /usr/lib/jvm/temurin-17-jdk-amd64/bin/java +[info] # VM options: +[info] # Blackhole mode: full + dont-inline hint +[info] # Warmup: 5 iterations, 10 s each +[info] # Measurement: 5 iterations, 10 s each +[info] # Timeout: 10 min per iteration +[info] # Threads: 1 thread, will synchronize iterations +[info] # Benchmark mode: Average time, time/op +[info] # Benchmark: benchmark.RegexBenchmark.email_accepting_zipper +[info] # Parameters: (size = 90) +[info] # Run progress: 85.63% complete, ETA 00:38:30 +[info] # Fork: 1 of 1 +[info] # Warmup Iteration 1: 717.791 us/op +[info] # Warmup Iteration 2: 656.188 us/op +[info] # Warmup Iteration 3: 653.390 us/op +[info] # Warmup Iteration 4: 642.622 us/op +[info] # Warmup Iteration 5: 651.385 us/op +[info] Iteration 1: 652.606 us/op +[info] Iteration 2: 643.483 us/op +[info] Iteration 3: 655.183 us/op +[info] Iteration 4: 641.699 us/op +[info] Iteration 5: 643.063 us/op +[info] Result "benchmark.RegexBenchmark.email_accepting_zipper": +[info] 647.207 ±(99.9%) 23.904 us/op [Average] +[info] (min, avg, max) = (641.699, 647.207, 655.183), stdev = 6.208 +[info] CI (99.9%): [623.303, 671.111] (assumes normal distribution) +[info] # JMH version: 1.32 +[info] # VM version: JDK 17.0.11, OpenJDK 64-Bit Server VM, 17.0.11+9 +[info] # VM invoker: /usr/lib/jvm/temurin-17-jdk-amd64/bin/java +[info] # VM options: +[info] # Blackhole mode: full + dont-inline hint +[info] # Warmup: 5 iterations, 10 s each +[info] # Measurement: 5 iterations, 10 s each +[info] # Timeout: 10 min per iteration +[info] # Threads: 1 thread, will synchronize iterations +[info] # Benchmark mode: Average time, time/op +[info] # Benchmark: benchmark.RegexBenchmark.email_accepting_zipper +[info] # Parameters: (size = 95) +[info] # Run progress: 86.25% complete, ETA 00:36:49 +[info] # Fork: 1 of 1 +[info] # Warmup Iteration 1: 714.689 us/op +[info] # Warmup Iteration 2: 653.449 us/op +[info] # Warmup Iteration 3: 662.370 us/op +[info] # Warmup Iteration 4: 663.041 us/op +[info] # Warmup Iteration 5: 662.551 us/op +[info] Iteration 1: 654.282 us/op +[info] Iteration 2: 654.603 us/op +[info] Iteration 3: 665.952 us/op +[info] Iteration 4: 651.694 us/op +[info] Iteration 5: 650.934 us/op +[info] Result "benchmark.RegexBenchmark.email_accepting_zipper": +[info] 655.493 ±(99.9%) 23.333 us/op [Average] +[info] (min, avg, max) = (650.934, 655.493, 665.952), stdev = 6.060 +[info] CI (99.9%): [632.160, 678.826] (assumes normal distribution) +[info] # JMH version: 1.32 +[info] # VM version: JDK 17.0.11, OpenJDK 64-Bit Server VM, 17.0.11+9 +[info] # VM invoker: /usr/lib/jvm/temurin-17-jdk-amd64/bin/java +[info] # VM options: +[info] # Blackhole mode: full + dont-inline hint +[info] # Warmup: 5 iterations, 10 s each +[info] # Measurement: 5 iterations, 10 s each +[info] # Timeout: 10 min per iteration +[info] # Threads: 1 thread, will synchronize iterations +[info] # Benchmark mode: Average time, time/op +[info] # Benchmark: benchmark.RegexBenchmark.email_accepting_zipper +[info] # Parameters: (size = 100) +[info] # Run progress: 86.88% complete, ETA 00:35:09 +[info] # Fork: 1 of 1 +[info] # Warmup Iteration 1: 803.018 us/op +[info] # Warmup Iteration 2: 753.013 us/op +[info] # Warmup Iteration 3: 755.009 us/op +[info] # Warmup Iteration 4: 752.277 us/op +[info] # Warmup Iteration 5: 754.620 us/op +[info] Iteration 1: 743.472 us/op +[info] Iteration 2: 752.865 us/op +[info] Iteration 3: 740.854 us/op +[info] Iteration 4: 744.127 us/op +[info] Iteration 5: 762.911 us/op +[info] Result "benchmark.RegexBenchmark.email_accepting_zipper": +[info] 748.846 ±(99.9%) 34.922 us/op [Average] +[info] (min, avg, max) = (740.854, 748.846, 762.911), stdev = 9.069 +[info] CI (99.9%): [713.924, 783.768] (assumes normal distribution) +[info] # JMH version: 1.32 +[info] # VM version: JDK 17.0.11, OpenJDK 64-Bit Server VM, 17.0.11+9 +[info] # VM invoker: /usr/lib/jvm/temurin-17-jdk-amd64/bin/java +[info] # VM options: +[info] # Blackhole mode: full + dont-inline hint +[info] # Warmup: 5 iterations, 10 s each +[info] # Measurement: 5 iterations, 10 s each +[info] # Timeout: 10 min per iteration +[info] # Threads: 1 thread, will synchronize iterations +[info] # Benchmark mode: Average time, time/op +[info] # Benchmark: benchmark.RegexBenchmark.email_accepting_zipper_mem +[info] # Parameters: (size = 5) +[info] # Run progress: 87.50% complete, ETA 00:33:28 +[info] # Fork: 1 of 1 +[info] # Warmup Iteration 1: 28.563 us/op +[info] # Warmup Iteration 2: 26.107 us/op +[info] # Warmup Iteration 3: 25.975 us/op +[info] # Warmup Iteration 4: 25.439 us/op +[info] # Warmup Iteration 5: 25.689 us/op +[info] Iteration 1: 25.660 us/op +[info] Iteration 2: 25.419 us/op +[info] Iteration 3: 25.410 us/op +[info] Iteration 4: 25.384 us/op +[info] Iteration 5: 25.735 us/op +[info] Result "benchmark.RegexBenchmark.email_accepting_zipper_mem": +[info] 25.522 ±(99.9%) 0.628 us/op [Average] +[info] (min, avg, max) = (25.384, 25.522, 25.735), stdev = 0.163 +[info] CI (99.9%): [24.894, 26.150] (assumes normal distribution) +[info] # JMH version: 1.32 +[info] # VM version: JDK 17.0.11, OpenJDK 64-Bit Server VM, 17.0.11+9 +[info] # VM invoker: /usr/lib/jvm/temurin-17-jdk-amd64/bin/java +[info] # VM options: +[info] # Blackhole mode: full + dont-inline hint +[info] # Warmup: 5 iterations, 10 s each +[info] # Measurement: 5 iterations, 10 s each +[info] # Timeout: 10 min per iteration +[info] # Threads: 1 thread, will synchronize iterations +[info] # Benchmark mode: Average time, time/op +[info] # Benchmark: benchmark.RegexBenchmark.email_accepting_zipper_mem +[info] # Parameters: (size = 10) +[info] # Run progress: 88.13% complete, ETA 00:31:48 +[info] # Fork: 1 of 1 +[info] # Warmup Iteration 1: 69.602 us/op +[info] # Warmup Iteration 2: 64.048 us/op +[info] # Warmup Iteration 3: 64.875 us/op +[info] # Warmup Iteration 4: 63.523 us/op +[info] # Warmup Iteration 5: 64.246 us/op +[info] Iteration 1: 63.739 us/op +[info] Iteration 2: 63.461 us/op +[info] Iteration 3: 62.969 us/op +[info] Iteration 4: 62.956 us/op +[info] Iteration 5: 62.929 us/op +[info] Result "benchmark.RegexBenchmark.email_accepting_zipper_mem": +[info] 63.211 ±(99.9%) 1.420 us/op [Average] +[info] (min, avg, max) = (62.929, 63.211, 63.739), stdev = 0.369 +[info] CI (99.9%): [61.790, 64.631] (assumes normal distribution) +[info] # JMH version: 1.32 +[info] # VM version: JDK 17.0.11, OpenJDK 64-Bit Server VM, 17.0.11+9 +[info] # VM invoker: /usr/lib/jvm/temurin-17-jdk-amd64/bin/java +[info] # VM options: +[info] # Blackhole mode: full + dont-inline hint +[info] # Warmup: 5 iterations, 10 s each +[info] # Measurement: 5 iterations, 10 s each +[info] # Timeout: 10 min per iteration +[info] # Threads: 1 thread, will synchronize iterations +[info] # Benchmark mode: Average time, time/op +[info] # Benchmark: benchmark.RegexBenchmark.email_accepting_zipper_mem +[info] # Parameters: (size = 15) +[info] # Run progress: 88.75% complete, ETA 00:30:07 +[info] # Fork: 1 of 1 +[info] # Warmup Iteration 1: 75.646 us/op +[info] # Warmup Iteration 2: 69.935 us/op +[info] # Warmup Iteration 3: 70.506 us/op +[info] # Warmup Iteration 4: 69.517 us/op +[info] # Warmup Iteration 5: 70.260 us/op +[info] Iteration 1: 70.350 us/op +[info] Iteration 2: 69.126 us/op +[info] Iteration 3: 69.698 us/op +[info] Iteration 4: 69.372 us/op +[info] Iteration 5: 69.055 us/op +[info] Result "benchmark.RegexBenchmark.email_accepting_zipper_mem": +[info] 69.520 ±(99.9%) 2.032 us/op [Average] +[info] (min, avg, max) = (69.055, 69.520, 70.350), stdev = 0.528 +[info] CI (99.9%): [67.488, 71.552] (assumes normal distribution) +[info] # JMH version: 1.32 +[info] # VM version: JDK 17.0.11, OpenJDK 64-Bit Server VM, 17.0.11+9 +[info] # VM invoker: /usr/lib/jvm/temurin-17-jdk-amd64/bin/java +[info] # VM options: +[info] # Blackhole mode: full + dont-inline hint +[info] # Warmup: 5 iterations, 10 s each +[info] # Measurement: 5 iterations, 10 s each +[info] # Timeout: 10 min per iteration +[info] # Threads: 1 thread, will synchronize iterations +[info] # Benchmark mode: Average time, time/op +[info] # Benchmark: benchmark.RegexBenchmark.email_accepting_zipper_mem +[info] # Parameters: (size = 20) +[info] # Run progress: 89.38% complete, ETA 00:28:27 +[info] # Fork: 1 of 1 +[info] # Warmup Iteration 1: 105.194 us/op +[info] # Warmup Iteration 2: 97.489 us/op +[info] # Warmup Iteration 3: 97.766 us/op +[info] # Warmup Iteration 4: 96.405 us/op +[info] # Warmup Iteration 5: 95.709 us/op +[info] Iteration 1: 95.938 us/op +[info] Iteration 2: 95.839 us/op +[info] Iteration 3: 94.797 us/op +[info] Iteration 4: 95.134 us/op +[info] Iteration 5: 95.147 us/op +[info] Result "benchmark.RegexBenchmark.email_accepting_zipper_mem": +[info] 95.371 ±(99.9%) 1.902 us/op [Average] +[info] (min, avg, max) = (94.797, 95.371, 95.938), stdev = 0.494 +[info] CI (99.9%): [93.469, 97.273] (assumes normal distribution) +[info] # JMH version: 1.32 +[info] # VM version: JDK 17.0.11, OpenJDK 64-Bit Server VM, 17.0.11+9 +[info] # VM invoker: /usr/lib/jvm/temurin-17-jdk-amd64/bin/java +[info] # VM options: +[info] # Blackhole mode: full + dont-inline hint +[info] # Warmup: 5 iterations, 10 s each +[info] # Measurement: 5 iterations, 10 s each +[info] # Timeout: 10 min per iteration +[info] # Threads: 1 thread, will synchronize iterations +[info] # Benchmark mode: Average time, time/op +[info] # Benchmark: benchmark.RegexBenchmark.email_accepting_zipper_mem +[info] # Parameters: (size = 25) +[info] # Run progress: 90.00% complete, ETA 00:26:47 +[info] # Fork: 1 of 1 +[info] # Warmup Iteration 1: 154.790 us/op +[info] # Warmup Iteration 2: 145.102 us/op +[info] # Warmup Iteration 3: 144.810 us/op +[info] # Warmup Iteration 4: 144.223 us/op +[info] # Warmup Iteration 5: 142.267 us/op +[info] Iteration 1: 144.021 us/op +[info] Iteration 2: 142.705 us/op +[info] Iteration 3: 144.333 us/op +[info] Iteration 4: 141.947 us/op +[info] Iteration 5: 144.118 us/op +[info] Result "benchmark.RegexBenchmark.email_accepting_zipper_mem": +[info] 143.425 ±(99.9%) 4.022 us/op [Average] +[info] (min, avg, max) = (141.947, 143.425, 144.333), stdev = 1.044 +[info] CI (99.9%): [139.403, 147.446] (assumes normal distribution) +[info] # JMH version: 1.32 +[info] # VM version: JDK 17.0.11, OpenJDK 64-Bit Server VM, 17.0.11+9 +[info] # VM invoker: /usr/lib/jvm/temurin-17-jdk-amd64/bin/java +[info] # VM options: +[info] # Blackhole mode: full + dont-inline hint +[info] # Warmup: 5 iterations, 10 s each +[info] # Measurement: 5 iterations, 10 s each +[info] # Timeout: 10 min per iteration +[info] # Threads: 1 thread, will synchronize iterations +[info] # Benchmark mode: Average time, time/op +[info] # Benchmark: benchmark.RegexBenchmark.email_accepting_zipper_mem +[info] # Parameters: (size = 30) +[info] # Run progress: 90.63% complete, ETA 00:25:06 +[info] # Fork: 1 of 1 +[info] # Warmup Iteration 1: 202.866 us/op +[info] # Warmup Iteration 2: 190.109 us/op +[info] # Warmup Iteration 3: 190.850 us/op +[info] # Warmup Iteration 4: 187.026 us/op +[info] # Warmup Iteration 5: 188.455 us/op +[info] Iteration 1: 187.546 us/op +[info] Iteration 2: 189.833 us/op +[info] Iteration 3: 187.632 us/op +[info] Iteration 4: 190.307 us/op +[info] Iteration 5: 190.337 us/op +[info] Result "benchmark.RegexBenchmark.email_accepting_zipper_mem": +[info] 189.131 ±(99.9%) 5.476 us/op [Average] +[info] (min, avg, max) = (187.546, 189.131, 190.337), stdev = 1.422 +[info] CI (99.9%): [183.655, 194.607] (assumes normal distribution) +[info] # JMH version: 1.32 +[info] # VM version: JDK 17.0.11, OpenJDK 64-Bit Server VM, 17.0.11+9 +[info] # VM invoker: /usr/lib/jvm/temurin-17-jdk-amd64/bin/java +[info] # VM options: +[info] # Blackhole mode: full + dont-inline hint +[info] # Warmup: 5 iterations, 10 s each +[info] # Measurement: 5 iterations, 10 s each +[info] # Timeout: 10 min per iteration +[info] # Threads: 1 thread, will synchronize iterations +[info] # Benchmark mode: Average time, time/op +[info] # Benchmark: benchmark.RegexBenchmark.email_accepting_zipper_mem +[info] # Parameters: (size = 35) +[info] # Run progress: 91.25% complete, ETA 00:23:26 +[info] # Fork: 1 of 1 +[info] # Warmup Iteration 1: 183.039 us/op +[info] # Warmup Iteration 2: 170.897 us/op +[info] # Warmup Iteration 3: 169.345 us/op +[info] # Warmup Iteration 4: 168.087 us/op +[info] # Warmup Iteration 5: 168.660 us/op +[info] Iteration 1: 165.390 us/op +[info] Iteration 2: 169.331 us/op +[info] Iteration 3: 166.340 us/op +[info] Iteration 4: 169.194 us/op +[info] Iteration 5: 166.718 us/op +[info] Result "benchmark.RegexBenchmark.email_accepting_zipper_mem": +[info] 167.394 ±(99.9%) 6.828 us/op [Average] +[info] (min, avg, max) = (165.390, 167.394, 169.331), stdev = 1.773 +[info] CI (99.9%): [160.567, 174.222] (assumes normal distribution) +[info] # JMH version: 1.32 +[info] # VM version: JDK 17.0.11, OpenJDK 64-Bit Server VM, 17.0.11+9 +[info] # VM invoker: /usr/lib/jvm/temurin-17-jdk-amd64/bin/java +[info] # VM options: +[info] # Blackhole mode: full + dont-inline hint +[info] # Warmup: 5 iterations, 10 s each +[info] # Measurement: 5 iterations, 10 s each +[info] # Timeout: 10 min per iteration +[info] # Threads: 1 thread, will synchronize iterations +[info] # Benchmark mode: Average time, time/op +[info] # Benchmark: benchmark.RegexBenchmark.email_accepting_zipper_mem +[info] # Parameters: (size = 40) +[info] # Run progress: 91.88% complete, ETA 00:21:45 +[info] # Fork: 1 of 1 +[info] # Warmup Iteration 1: 345.337 us/op +[info] # Warmup Iteration 2: 317.233 us/op +[info] # Warmup Iteration 3: 319.654 us/op +[info] # Warmup Iteration 4: 319.719 us/op +[info] # Warmup Iteration 5: 313.661 us/op +[info] Iteration 1: 318.881 us/op +[info] Iteration 2: 314.212 us/op +[info] Iteration 3: 318.207 us/op +[info] Iteration 4: 313.062 us/op +[info] Iteration 5: 313.211 us/op +[info] Result "benchmark.RegexBenchmark.email_accepting_zipper_mem": +[info] 315.515 ±(99.9%) 10.823 us/op [Average] +[info] (min, avg, max) = (313.062, 315.515, 318.881), stdev = 2.811 +[info] CI (99.9%): [304.692, 326.338] (assumes normal distribution) +[info] # JMH version: 1.32 +[info] # VM version: JDK 17.0.11, OpenJDK 64-Bit Server VM, 17.0.11+9 +[info] # VM invoker: /usr/lib/jvm/temurin-17-jdk-amd64/bin/java +[info] # VM options: +[info] # Blackhole mode: full + dont-inline hint +[info] # Warmup: 5 iterations, 10 s each +[info] # Measurement: 5 iterations, 10 s each +[info] # Timeout: 10 min per iteration +[info] # Threads: 1 thread, will synchronize iterations +[info] # Benchmark mode: Average time, time/op +[info] # Benchmark: benchmark.RegexBenchmark.email_accepting_zipper_mem +[info] # Parameters: (size = 45) +[info] # Run progress: 92.50% complete, ETA 00:20:05 +[info] # Fork: 1 of 1 +[info] # Warmup Iteration 1: 278.552 us/op +[info] # Warmup Iteration 2: 260.531 us/op +[info] # Warmup Iteration 3: 260.605 us/op +[info] # Warmup Iteration 4: 260.614 us/op +[info] # Warmup Iteration 5: 255.152 us/op +[info] Iteration 1: 259.993 us/op +[info] Iteration 2: 255.305 us/op +[info] Iteration 3: 260.313 us/op +[info] Iteration 4: 258.216 us/op +[info] Iteration 5: 262.100 us/op +[info] Result "benchmark.RegexBenchmark.email_accepting_zipper_mem": +[info] 259.185 ±(99.9%) 9.896 us/op [Average] +[info] (min, avg, max) = (255.305, 259.185, 262.100), stdev = 2.570 +[info] CI (99.9%): [249.289, 269.081] (assumes normal distribution) +[info] # JMH version: 1.32 +[info] # VM version: JDK 17.0.11, OpenJDK 64-Bit Server VM, 17.0.11+9 +[info] # VM invoker: /usr/lib/jvm/temurin-17-jdk-amd64/bin/java +[info] # VM options: +[info] # Blackhole mode: full + dont-inline hint +[info] # Warmup: 5 iterations, 10 s each +[info] # Measurement: 5 iterations, 10 s each +[info] # Timeout: 10 min per iteration +[info] # Threads: 1 thread, will synchronize iterations +[info] # Benchmark mode: Average time, time/op +[info] # Benchmark: benchmark.RegexBenchmark.email_accepting_zipper_mem +[info] # Parameters: (size = 50) +[info] # Run progress: 93.13% complete, ETA 00:18:24 +[info] # Fork: 1 of 1 +[info] # Warmup Iteration 1: 423.608 us/op +[info] # Warmup Iteration 2: 398.114 us/op +[info] # Warmup Iteration 3: 398.942 us/op +[info] # Warmup Iteration 4: 390.402 us/op +[info] # Warmup Iteration 5: 387.938 us/op +[info] Iteration 1: 386.495 us/op +[info] Iteration 2: 387.577 us/op +[info] Iteration 3: 386.050 us/op +[info] Iteration 4: 383.631 us/op +[info] Iteration 5: 387.283 us/op +[info] Result "benchmark.RegexBenchmark.email_accepting_zipper_mem": +[info] 386.207 ±(99.9%) 6.020 us/op [Average] +[info] (min, avg, max) = (383.631, 386.207, 387.577), stdev = 1.564 +[info] CI (99.9%): [380.187, 392.228] (assumes normal distribution) +[info] # JMH version: 1.32 +[info] # VM version: JDK 17.0.11, OpenJDK 64-Bit Server VM, 17.0.11+9 +[info] # VM invoker: /usr/lib/jvm/temurin-17-jdk-amd64/bin/java +[info] # VM options: +[info] # Blackhole mode: full + dont-inline hint +[info] # Warmup: 5 iterations, 10 s each +[info] # Measurement: 5 iterations, 10 s each +[info] # Timeout: 10 min per iteration +[info] # Threads: 1 thread, will synchronize iterations +[info] # Benchmark mode: Average time, time/op +[info] # Benchmark: benchmark.RegexBenchmark.email_accepting_zipper_mem +[info] # Parameters: (size = 55) +[info] # Run progress: 93.75% complete, ETA 00:16:44 +[info] # Fork: 1 of 1 +[info] # Warmup Iteration 1: 419.771 us/op +[info] # Warmup Iteration 2: 386.706 us/op +[info] # Warmup Iteration 3: 381.554 us/op +[info] # Warmup Iteration 4: 386.363 us/op +[info] # Warmup Iteration 5: 387.984 us/op +[info] Iteration 1: 381.680 us/op +[info] Iteration 2: 387.159 us/op +[info] Iteration 3: 381.319 us/op +[info] Iteration 4: 382.756 us/op +[info] Iteration 5: 387.345 us/op +[info] Result "benchmark.RegexBenchmark.email_accepting_zipper_mem": +[info] 384.052 ±(99.9%) 11.435 us/op [Average] +[info] (min, avg, max) = (381.319, 384.052, 387.345), stdev = 2.970 +[info] CI (99.9%): [372.617, 395.487] (assumes normal distribution) +[info] # JMH version: 1.32 +[info] # VM version: JDK 17.0.11, OpenJDK 64-Bit Server VM, 17.0.11+9 +[info] # VM invoker: /usr/lib/jvm/temurin-17-jdk-amd64/bin/java +[info] # VM options: +[info] # Blackhole mode: full + dont-inline hint +[info] # Warmup: 5 iterations, 10 s each +[info] # Measurement: 5 iterations, 10 s each +[info] # Timeout: 10 min per iteration +[info] # Threads: 1 thread, will synchronize iterations +[info] # Benchmark mode: Average time, time/op +[info] # Benchmark: benchmark.RegexBenchmark.email_accepting_zipper_mem +[info] # Parameters: (size = 60) +[info] # Run progress: 94.38% complete, ETA 00:15:03 +[info] # Fork: 1 of 1 +[info] # Warmup Iteration 1: 337.419 us/op +[info] # Warmup Iteration 2: 311.579 us/op +[info] # Warmup Iteration 3: 313.032 us/op +[info] # Warmup Iteration 4: 306.562 us/op +[info] # Warmup Iteration 5: 310.609 us/op +[info] Iteration 1: 308.760 us/op +[info] Iteration 2: 307.961 us/op +[info] Iteration 3: 305.653 us/op +[info] Iteration 4: 312.918 us/op +[info] Iteration 5: 316.626 us/op +[info] Result "benchmark.RegexBenchmark.email_accepting_zipper_mem": +[info] 310.384 ±(99.9%) 16.814 us/op [Average] +[info] (min, avg, max) = (305.653, 310.384, 316.626), stdev = 4.367 +[info] CI (99.9%): [293.570, 327.198] (assumes normal distribution) +[info] # JMH version: 1.32 +[info] # VM version: JDK 17.0.11, OpenJDK 64-Bit Server VM, 17.0.11+9 +[info] # VM invoker: /usr/lib/jvm/temurin-17-jdk-amd64/bin/java +[info] # VM options: +[info] # Blackhole mode: full + dont-inline hint +[info] # Warmup: 5 iterations, 10 s each +[info] # Measurement: 5 iterations, 10 s each +[info] # Timeout: 10 min per iteration +[info] # Threads: 1 thread, will synchronize iterations +[info] # Benchmark mode: Average time, time/op +[info] # Benchmark: benchmark.RegexBenchmark.email_accepting_zipper_mem +[info] # Parameters: (size = 65) +[info] # Run progress: 95.00% complete, ETA 00:13:23 +[info] # Fork: 1 of 1 +[info] # Warmup Iteration 1: 331.307 us/op +[info] # Warmup Iteration 2: 311.921 us/op +[info] # Warmup Iteration 3: 305.790 us/op +[info] # Warmup Iteration 4: 304.694 us/op +[info] # Warmup Iteration 5: 301.097 us/op +[info] Iteration 1: 303.590 us/op +[info] Iteration 2: 302.112 us/op +[info] Iteration 3: 302.393 us/op +[info] Iteration 4: 299.887 us/op +[info] Iteration 5: 305.723 us/op +[info] Result "benchmark.RegexBenchmark.email_accepting_zipper_mem": +[info] 302.741 ±(99.9%) 8.232 us/op [Average] +[info] (min, avg, max) = (299.887, 302.741, 305.723), stdev = 2.138 +[info] CI (99.9%): [294.510, 310.973] (assumes normal distribution) +[info] # JMH version: 1.32 +[info] # VM version: JDK 17.0.11, OpenJDK 64-Bit Server VM, 17.0.11+9 +[info] # VM invoker: /usr/lib/jvm/temurin-17-jdk-amd64/bin/java +[info] # VM options: +[info] # Blackhole mode: full + dont-inline hint +[info] # Warmup: 5 iterations, 10 s each +[info] # Measurement: 5 iterations, 10 s each +[info] # Timeout: 10 min per iteration +[info] # Threads: 1 thread, will synchronize iterations +[info] # Benchmark mode: Average time, time/op +[info] # Benchmark: benchmark.RegexBenchmark.email_accepting_zipper_mem +[info] # Parameters: (size = 70) +[info] # Run progress: 95.63% complete, ETA 00:11:43 +[info] # Fork: 1 of 1 +[info] # Warmup Iteration 1: 500.809 us/op +[info] # Warmup Iteration 2: 463.080 us/op +[info] # Warmup Iteration 3: 463.309 us/op +[info] # Warmup Iteration 4: 454.922 us/op +[info] # Warmup Iteration 5: 457.931 us/op +[info] Iteration 1: 457.987 us/op +[info] Iteration 2: 460.228 us/op +[info] Iteration 3: 453.527 us/op +[info] Iteration 4: 454.309 us/op +[info] Iteration 5: 453.260 us/op +[info] Result "benchmark.RegexBenchmark.email_accepting_zipper_mem": +[info] 455.862 ±(99.9%) 11.901 us/op [Average] +[info] (min, avg, max) = (453.260, 455.862, 460.228), stdev = 3.091 +[info] CI (99.9%): [443.961, 467.763] (assumes normal distribution) +[info] # JMH version: 1.32 +[info] # VM version: JDK 17.0.11, OpenJDK 64-Bit Server VM, 17.0.11+9 +[info] # VM invoker: /usr/lib/jvm/temurin-17-jdk-amd64/bin/java +[info] # VM options: +[info] # Blackhole mode: full + dont-inline hint +[info] # Warmup: 5 iterations, 10 s each +[info] # Measurement: 5 iterations, 10 s each +[info] # Timeout: 10 min per iteration +[info] # Threads: 1 thread, will synchronize iterations +[info] # Benchmark mode: Average time, time/op +[info] # Benchmark: benchmark.RegexBenchmark.email_accepting_zipper_mem +[info] # Parameters: (size = 75) +[info] # Run progress: 96.25% complete, ETA 00:10:02 +[info] # Fork: 1 of 1 +[info] # Warmup Iteration 1: 657.067 us/op +[info] # Warmup Iteration 2: 612.980 us/op +[info] # Warmup Iteration 3: 612.259 us/op +[info] # Warmup Iteration 4: 605.712 us/op +[info] # Warmup Iteration 5: 610.746 us/op +[info] Iteration 1: 603.309 us/op +[info] Iteration 2: 604.211 us/op +[info] Iteration 3: 618.969 us/op +[info] Iteration 4: 616.110 us/op +[info] Iteration 5: 621.002 us/op +[info] Result "benchmark.RegexBenchmark.email_accepting_zipper_mem": +[info] 612.720 ±(99.9%) 32.223 us/op [Average] +[info] (min, avg, max) = (603.309, 612.720, 621.002), stdev = 8.368 +[info] CI (99.9%): [580.497, 644.943] (assumes normal distribution) +[info] # JMH version: 1.32 +[info] # VM version: JDK 17.0.11, OpenJDK 64-Bit Server VM, 17.0.11+9 +[info] # VM invoker: /usr/lib/jvm/temurin-17-jdk-amd64/bin/java +[info] # VM options: +[info] # Blackhole mode: full + dont-inline hint +[info] # Warmup: 5 iterations, 10 s each +[info] # Measurement: 5 iterations, 10 s each +[info] # Timeout: 10 min per iteration +[info] # Threads: 1 thread, will synchronize iterations +[info] # Benchmark mode: Average time, time/op +[info] # Benchmark: benchmark.RegexBenchmark.email_accepting_zipper_mem +[info] # Parameters: (size = 80) +[info] # Run progress: 96.88% complete, ETA 00:08:22 +[info] # Fork: 1 of 1 +[info] # Warmup Iteration 1: 742.894 us/op +[info] # Warmup Iteration 2: 686.900 us/op +[info] # Warmup Iteration 3: 679.445 us/op +[info] # Warmup Iteration 4: 671.970 us/op +[info] # Warmup Iteration 5: 674.305 us/op +[info] Iteration 1: 669.689 us/op +[info] Iteration 2: 673.878 us/op +[info] Iteration 3: 671.827 us/op +[info] Iteration 4: 672.871 us/op +[info] Iteration 5: 671.076 us/op +[info] Result "benchmark.RegexBenchmark.email_accepting_zipper_mem": +[info] 671.868 ±(99.9%) 6.215 us/op [Average] +[info] (min, avg, max) = (669.689, 671.868, 673.878), stdev = 1.614 +[info] CI (99.9%): [665.653, 678.084] (assumes normal distribution) +[info] # JMH version: 1.32 +[info] # VM version: JDK 17.0.11, OpenJDK 64-Bit Server VM, 17.0.11+9 +[info] # VM invoker: /usr/lib/jvm/temurin-17-jdk-amd64/bin/java +[info] # VM options: +[info] # Blackhole mode: full + dont-inline hint +[info] # Warmup: 5 iterations, 10 s each +[info] # Measurement: 5 iterations, 10 s each +[info] # Timeout: 10 min per iteration +[info] # Threads: 1 thread, will synchronize iterations +[info] # Benchmark mode: Average time, time/op +[info] # Benchmark: benchmark.RegexBenchmark.email_accepting_zipper_mem +[info] # Parameters: (size = 85) +[info] # Run progress: 97.50% complete, ETA 00:06:41 +[info] # Fork: 1 of 1 +[info] # Warmup Iteration 1: 708.027 us/op +[info] # Warmup Iteration 2: 663.156 us/op +[info] # Warmup Iteration 3: 647.091 us/op +[info] # Warmup Iteration 4: 656.535 us/op +[info] # Warmup Iteration 5: 649.002 us/op +[info] Iteration 1: 655.996 us/op +[info] Iteration 2: 654.692 us/op +[info] Iteration 3: 651.706 us/op +[info] Iteration 4: 647.531 us/op +[info] Iteration 5: 645.976 us/op +[info] Result "benchmark.RegexBenchmark.email_accepting_zipper_mem": +[info] 651.181 ±(99.9%) 16.807 us/op [Average] +[info] (min, avg, max) = (645.976, 651.181, 655.996), stdev = 4.365 +[info] CI (99.9%): [634.374, 667.987] (assumes normal distribution) +[info] # JMH version: 1.32 +[info] # VM version: JDK 17.0.11, OpenJDK 64-Bit Server VM, 17.0.11+9 +[info] # VM invoker: /usr/lib/jvm/temurin-17-jdk-amd64/bin/java +[info] # VM options: +[info] # Blackhole mode: full + dont-inline hint +[info] # Warmup: 5 iterations, 10 s each +[info] # Measurement: 5 iterations, 10 s each +[info] # Timeout: 10 min per iteration +[info] # Threads: 1 thread, will synchronize iterations +[info] # Benchmark mode: Average time, time/op +[info] # Benchmark: benchmark.RegexBenchmark.email_accepting_zipper_mem +[info] # Parameters: (size = 90) +[info] # Run progress: 98.13% complete, ETA 00:05:01 +[info] # Fork: 1 of 1 +[info] # Warmup Iteration 1: 702.935 us/op +[info] # Warmup Iteration 2: 653.970 us/op +[info] # Warmup Iteration 3: 651.455 us/op +[info] # Warmup Iteration 4: 638.091 us/op +[info] # Warmup Iteration 5: 650.281 us/op +[info] Iteration 1: 639.115 us/op +[info] Iteration 2: 639.427 us/op +[info] Iteration 3: 649.930 us/op +[info] Iteration 4: 638.117 us/op +[info] Iteration 5: 645.536 us/op +[info] Result "benchmark.RegexBenchmark.email_accepting_zipper_mem": +[info] 642.425 ±(99.9%) 19.682 us/op [Average] +[info] (min, avg, max) = (638.117, 642.425, 649.930), stdev = 5.111 +[info] CI (99.9%): [622.743, 662.107] (assumes normal distribution) +[info] # JMH version: 1.32 +[info] # VM version: JDK 17.0.11, OpenJDK 64-Bit Server VM, 17.0.11+9 +[info] # VM invoker: /usr/lib/jvm/temurin-17-jdk-amd64/bin/java +[info] # VM options: +[info] # Blackhole mode: full + dont-inline hint +[info] # Warmup: 5 iterations, 10 s each +[info] # Measurement: 5 iterations, 10 s each +[info] # Timeout: 10 min per iteration +[info] # Threads: 1 thread, will synchronize iterations +[info] # Benchmark mode: Average time, time/op +[info] # Benchmark: benchmark.RegexBenchmark.email_accepting_zipper_mem +[info] # Parameters: (size = 95) +[info] # Run progress: 98.75% complete, ETA 00:03:20 +[info] # Fork: 1 of 1 +[info] # Warmup Iteration 1: 661.607 us/op +[info] # Warmup Iteration 2: 615.876 us/op +[info] # Warmup Iteration 3: 609.710 us/op +[info] # Warmup Iteration 4: 601.990 us/op +[info] # Warmup Iteration 5: 606.741 us/op +[info] Iteration 1: 598.580 us/op +[info] Iteration 2: 602.759 us/op +[info] Iteration 3: 601.572 us/op +[info] Iteration 4: 595.558 us/op +[info] Iteration 5: 607.158 us/op +[info] Result "benchmark.RegexBenchmark.email_accepting_zipper_mem": +[info] 601.126 ±(99.9%) 16.864 us/op [Average] +[info] (min, avg, max) = (595.558, 601.126, 607.158), stdev = 4.380 +[info] CI (99.9%): [584.261, 617.990] (assumes normal distribution) +[info] # JMH version: 1.32 +[info] # VM version: JDK 17.0.11, OpenJDK 64-Bit Server VM, 17.0.11+9 +[info] # VM invoker: /usr/lib/jvm/temurin-17-jdk-amd64/bin/java +[info] # VM options: +[info] # Blackhole mode: full + dont-inline hint +[info] # Warmup: 5 iterations, 10 s each +[info] # Measurement: 5 iterations, 10 s each +[info] # Timeout: 10 min per iteration +[info] # Threads: 1 thread, will synchronize iterations +[info] # Benchmark mode: Average time, time/op +[info] # Benchmark: benchmark.RegexBenchmark.email_accepting_zipper_mem +[info] # Parameters: (size = 100) +[info] # Run progress: 99.38% complete, ETA 00:01:40 +[info] # Fork: 1 of 1 +[info] # Warmup Iteration 1: 808.517 us/op +[info] # Warmup Iteration 2: 757.732 us/op +[info] # Warmup Iteration 3: 756.756 us/op +[info] # Warmup Iteration 4: 754.716 us/op +[info] # Warmup Iteration 5: 747.030 us/op +[info] Iteration 1: 755.683 us/op +[info] Iteration 2: 753.843 us/op +[info] Iteration 3: 749.083 us/op +[info] Iteration 4: 744.530 us/op +[info] Iteration 5: 742.462 us/op +[info] Result "benchmark.RegexBenchmark.email_accepting_zipper_mem": +[info] 749.120 ±(99.9%) 22.018 us/op [Average] +[info] (min, avg, max) = (742.462, 749.120, 755.683), stdev = 5.718 +[info] CI (99.9%): [727.102, 771.138] (assumes normal distribution) +[info] # Run complete. Total time: 04:27:50 +[info] REMEMBER: The numbers below are just data. To gain reusable insights, you need to follow up on +[info] why the numbers are the way they are. Use profilers (see -prof, -lprof), design factorial +[info] experiments, perform baseline and negative tests that provide experimental control, make sure +[info] the benchmarking environment is safe on JVM/OS/HW level, ask for reviews from the domain experts. +[info] Do not assume the numbers tell you what you want them to tell. +[info] Benchmark (size) Mode Cnt Score Error Units +[info] RegexBenchmark.abStar_accepting_regex 5 avgt 5 1.718 ± 0.185 us/op +[info] RegexBenchmark.abStar_accepting_regex 10 avgt 5 10.116 ± 0.283 us/op +[info] RegexBenchmark.abStar_accepting_regex 15 avgt 5 31.786 ± 0.882 us/op +[info] RegexBenchmark.abStar_accepting_regex 20 avgt 5 74.264 ± 2.577 us/op +[info] RegexBenchmark.abStar_accepting_regex 25 avgt 5 146.090 ± 3.507 us/op +[info] RegexBenchmark.abStar_accepting_regex 30 avgt 5 256.076 ± 9.722 us/op +[info] RegexBenchmark.abStar_accepting_regex 35 avgt 5 410.827 ± 4.600 us/op +[info] RegexBenchmark.abStar_accepting_regex 40 avgt 5 618.044 ± 11.066 us/op +[info] RegexBenchmark.abStar_accepting_regex 45 avgt 5 895.693 ± 22.044 us/op +[info] RegexBenchmark.abStar_accepting_regex 50 avgt 5 1239.258 ± 46.194 us/op +[info] RegexBenchmark.abStar_accepting_regex 55 avgt 5 1660.993 ± 32.586 us/op +[info] RegexBenchmark.abStar_accepting_regex 60 avgt 5 2205.404 ± 79.386 us/op +[info] RegexBenchmark.abStar_accepting_regex 65 avgt 5 2804.502 ± 73.930 us/op +[info] RegexBenchmark.abStar_accepting_regex 70 avgt 5 3533.339 ± 91.019 us/op +[info] RegexBenchmark.abStar_accepting_regex 75 avgt 5 4411.665 ± 181.586 us/op +[info] RegexBenchmark.abStar_accepting_regex 80 avgt 5 5317.288 ± 149.315 us/op +[info] RegexBenchmark.abStar_accepting_regex 85 avgt 5 6386.131 ± 161.737 us/op +[info] RegexBenchmark.abStar_accepting_regex 90 avgt 5 7766.553 ± 257.710 us/op +[info] RegexBenchmark.abStar_accepting_regex 95 avgt 5 9045.615 ± 368.430 us/op +[info] RegexBenchmark.abStar_accepting_regex 100 avgt 5 10531.468 ± 302.107 us/op +[info] RegexBenchmark.abStar_accepting_regex_mem 5 avgt 5 10.257 ± 0.099 us/op +[info] RegexBenchmark.abStar_accepting_regex_mem 10 avgt 5 59.411 ± 0.405 us/op +[info] RegexBenchmark.abStar_accepting_regex_mem 15 avgt 5 165.373 ± 0.745 us/op +[info] RegexBenchmark.abStar_accepting_regex_mem 20 avgt 5 347.550 ± 8.042 us/op +[info] RegexBenchmark.abStar_accepting_regex_mem 25 avgt 5 608.218 ± 3.153 us/op +[info] RegexBenchmark.abStar_accepting_regex_mem 30 avgt 5 981.560 ± 6.240 us/op +[info] RegexBenchmark.abStar_accepting_regex_mem 35 avgt 5 1556.398 ± 62.844 us/op +[info] RegexBenchmark.abStar_accepting_regex_mem 40 avgt 5 2148.328 ± 7.370 us/op +[info] RegexBenchmark.abStar_accepting_regex_mem 45 avgt 5 2972.931 ± 13.578 us/op +[info] RegexBenchmark.abStar_accepting_regex_mem 50 avgt 5 4040.328 ± 159.279 us/op +[info] RegexBenchmark.abStar_accepting_regex_mem 55 avgt 5 5060.148 ± 25.422 us/op +[info] RegexBenchmark.abStar_accepting_regex_mem 60 avgt 5 6724.749 ± 52.200 us/op +[info] RegexBenchmark.abStar_accepting_regex_mem 65 avgt 5 8363.355 ± 276.350 us/op +[info] RegexBenchmark.abStar_accepting_regex_mem 70 avgt 5 10321.880 ± 19.532 us/op +[info] RegexBenchmark.abStar_accepting_regex_mem 75 avgt 5 12303.517 ± 93.941 us/op +[info] RegexBenchmark.abStar_accepting_regex_mem 80 avgt 5 15142.450 ± 794.036 us/op +[info] RegexBenchmark.abStar_accepting_regex_mem 85 avgt 5 17798.814 ± 151.100 us/op +[info] RegexBenchmark.abStar_accepting_regex_mem 90 avgt 5 20900.861 ± 1227.450 us/op +[info] RegexBenchmark.abStar_accepting_regex_mem 95 avgt 5 24692.210 ± 3273.667 us/op +[info] RegexBenchmark.abStar_accepting_regex_mem 100 avgt 5 28660.343 ± 3526.225 us/op +[info] RegexBenchmark.abStar_accepting_zipper 5 avgt 5 1.069 ± 0.043 us/op +[info] RegexBenchmark.abStar_accepting_zipper 10 avgt 5 2.148 ± 0.094 us/op +[info] RegexBenchmark.abStar_accepting_zipper 15 avgt 5 3.403 ± 0.067 us/op +[info] RegexBenchmark.abStar_accepting_zipper 20 avgt 5 4.278 ± 0.103 us/op +[info] RegexBenchmark.abStar_accepting_zipper 25 avgt 5 5.623 ± 0.159 us/op +[info] RegexBenchmark.abStar_accepting_zipper 30 avgt 5 6.615 ± 0.165 us/op +[info] RegexBenchmark.abStar_accepting_zipper 35 avgt 5 7.813 ± 0.265 us/op +[info] RegexBenchmark.abStar_accepting_zipper 40 avgt 5 8.966 ± 0.387 us/op +[info] RegexBenchmark.abStar_accepting_zipper 45 avgt 5 10.132 ± 0.440 us/op +[info] RegexBenchmark.abStar_accepting_zipper 50 avgt 5 11.333 ± 0.361 us/op +[info] RegexBenchmark.abStar_accepting_zipper 55 avgt 5 12.820 ± 0.667 us/op +[info] RegexBenchmark.abStar_accepting_zipper 60 avgt 5 13.595 ± 0.414 us/op +[info] RegexBenchmark.abStar_accepting_zipper 65 avgt 5 12.067 ± 0.404 us/op +[info] RegexBenchmark.abStar_accepting_zipper 70 avgt 5 13.196 ± 0.515 us/op +[info] RegexBenchmark.abStar_accepting_zipper 75 avgt 5 13.803 ± 0.478 us/op +[info] RegexBenchmark.abStar_accepting_zipper 80 avgt 5 14.918 ± 0.685 us/op +[info] RegexBenchmark.abStar_accepting_zipper 85 avgt 5 16.216 ± 0.831 us/op +[info] RegexBenchmark.abStar_accepting_zipper 90 avgt 5 16.469 ± 0.596 us/op +[info] RegexBenchmark.abStar_accepting_zipper 95 avgt 5 17.290 ± 0.540 us/op +[info] RegexBenchmark.abStar_accepting_zipper 100 avgt 5 18.978 ± 0.857 us/op +[info] RegexBenchmark.abStar_accepting_zipper_mem 5 avgt 5 2.669 ± 0.066 us/op +[info] RegexBenchmark.abStar_accepting_zipper_mem 10 avgt 5 5.471 ± 0.057 us/op +[info] RegexBenchmark.abStar_accepting_zipper_mem 15 avgt 5 8.247 ± 0.241 us/op +[info] RegexBenchmark.abStar_accepting_zipper_mem 20 avgt 5 10.573 ± 0.231 us/op +[info] RegexBenchmark.abStar_accepting_zipper_mem 25 avgt 5 14.218 ± 0.314 us/op +[info] RegexBenchmark.abStar_accepting_zipper_mem 30 avgt 5 19.685 ± 0.329 us/op +[info] RegexBenchmark.abStar_accepting_zipper_mem 35 avgt 5 23.082 ± 0.626 us/op +[info] RegexBenchmark.abStar_accepting_zipper_mem 40 avgt 5 25.858 ± 0.477 us/op +[info] RegexBenchmark.abStar_accepting_zipper_mem 45 avgt 5 29.805 ± 0.868 us/op +[info] RegexBenchmark.abStar_accepting_zipper_mem 50 avgt 5 32.617 ± 1.067 us/op +[info] RegexBenchmark.abStar_accepting_zipper_mem 55 avgt 5 31.025 ± 0.543 us/op +[info] RegexBenchmark.abStar_accepting_zipper_mem 60 avgt 5 39.397 ± 1.475 us/op +[info] RegexBenchmark.abStar_accepting_zipper_mem 65 avgt 5 49.392 ± 1.140 us/op +[info] RegexBenchmark.abStar_accepting_zipper_mem 70 avgt 5 45.483 ± 0.936 us/op +[info] RegexBenchmark.abStar_accepting_zipper_mem 75 avgt 5 48.622 ± 1.293 us/op +[info] RegexBenchmark.abStar_accepting_zipper_mem 80 avgt 5 53.248 ± 1.408 us/op +[info] RegexBenchmark.abStar_accepting_zipper_mem 85 avgt 5 54.534 ± 1.146 us/op +[info] RegexBenchmark.abStar_accepting_zipper_mem 90 avgt 5 58.530 ± 2.906 us/op +[info] RegexBenchmark.abStar_accepting_zipper_mem 95 avgt 5 62.675 ± 1.273 us/op +[info] RegexBenchmark.abStar_accepting_zipper_mem 100 avgt 5 65.660 ± 1.576 us/op +[info] RegexBenchmark.email_accepting_regex 5 avgt 5 14.479 ± 0.582 us/op +[info] RegexBenchmark.email_accepting_regex 10 avgt 5 63.107 ± 1.784 us/op +[info] RegexBenchmark.email_accepting_regex 15 avgt 5 140.479 ± 4.823 us/op +[info] RegexBenchmark.email_accepting_regex 20 avgt 5 271.257 ± 4.290 us/op +[info] RegexBenchmark.email_accepting_regex 25 avgt 5 418.221 ± 13.027 us/op +[info] RegexBenchmark.email_accepting_regex 30 avgt 5 750.992 ± 23.385 us/op +[info] RegexBenchmark.email_accepting_regex 35 avgt 5 1025.407 ± 39.910 us/op +[info] RegexBenchmark.email_accepting_regex 40 avgt 5 1690.905 ± 38.652 us/op +[info] RegexBenchmark.email_accepting_regex 45 avgt 5 2119.223 ± 67.212 us/op +[info] RegexBenchmark.email_accepting_regex 50 avgt 5 3013.542 ± 116.997 us/op +[info] RegexBenchmark.email_accepting_regex 55 avgt 5 3534.553 ± 82.552 us/op +[info] RegexBenchmark.email_accepting_regex 60 avgt 5 4176.843 ± 93.465 us/op +[info] RegexBenchmark.email_accepting_regex 65 avgt 5 5587.623 ± 280.479 us/op +[info] RegexBenchmark.email_accepting_regex 70 avgt 5 6862.979 ± 176.748 us/op +[info] RegexBenchmark.email_accepting_regex 75 avgt 5 8650.853 ± 142.532 us/op +[info] RegexBenchmark.email_accepting_regex 80 avgt 5 9148.193 ± 261.101 us/op +[info] RegexBenchmark.email_accepting_regex 85 avgt 5 11397.081 ± 187.999 us/op +[info] RegexBenchmark.email_accepting_regex 90 avgt 5 13581.383 ± 379.412 us/op +[info] RegexBenchmark.email_accepting_regex 95 avgt 5 15851.598 ± 438.220 us/op +[info] RegexBenchmark.email_accepting_regex 100 avgt 5 17424.544 ± 448.055 us/op +[info] RegexBenchmark.email_accepting_regex_mem 5 avgt 5 97.926 ± 0.471 us/op +[info] RegexBenchmark.email_accepting_regex_mem 10 avgt 5 336.453 ± 3.227 us/op +[info] RegexBenchmark.email_accepting_regex_mem 15 avgt 5 673.542 ± 3.941 us/op +[info] RegexBenchmark.email_accepting_regex_mem 20 avgt 5 1156.384 ± 8.869 us/op +[info] RegexBenchmark.email_accepting_regex_mem 25 avgt 5 1837.122 ± 13.741 us/op +[info] RegexBenchmark.email_accepting_regex_mem 30 avgt 5 2967.740 ± 21.515 us/op +[info] RegexBenchmark.email_accepting_regex_mem 35 avgt 5 3701.245 ± 34.258 us/op +[info] RegexBenchmark.email_accepting_regex_mem 40 avgt 5 7804.431 ± 59.788 us/op +[info] RegexBenchmark.email_accepting_regex_mem 45 avgt 5 6639.778 ± 31.797 us/op +[info] RegexBenchmark.email_accepting_regex_mem 50 avgt 5 11605.145 ± 98.380 us/op +[info] RegexBenchmark.email_accepting_regex_mem 55 avgt 5 12214.834 ± 51.894 us/op +[info] RegexBenchmark.email_accepting_regex_mem 60 avgt 5 12016.321 ± 20.146 us/op +[info] RegexBenchmark.email_accepting_regex_mem 65 avgt 5 14362.316 ± 159.451 us/op +[info] RegexBenchmark.email_accepting_regex_mem 70 avgt 5 20401.022 ± 95.424 us/op +[info] RegexBenchmark.email_accepting_regex_mem 75 avgt 5 29995.169 ± 559.255 us/op +[info] RegexBenchmark.email_accepting_regex_mem 80 avgt 5 32220.514 ± 1109.523 us/op +[info] RegexBenchmark.email_accepting_regex_mem 85 avgt 5 34271.337 ± 391.395 us/op +[info] RegexBenchmark.email_accepting_regex_mem 90 avgt 5 38066.820 ± 320.635 us/op +[info] RegexBenchmark.email_accepting_regex_mem 95 avgt 5 40710.121 ± 547.851 us/op +[info] RegexBenchmark.email_accepting_regex_mem 100 avgt 5 55536.854 ± 300.793 us/op +[info] RegexBenchmark.email_accepting_zipper 5 avgt 5 25.572 ± 0.665 us/op +[info] RegexBenchmark.email_accepting_zipper 10 avgt 5 63.562 ± 1.069 us/op +[info] RegexBenchmark.email_accepting_zipper 15 avgt 5 69.579 ± 1.683 us/op +[info] RegexBenchmark.email_accepting_zipper 20 avgt 5 95.436 ± 2.396 us/op +[info] RegexBenchmark.email_accepting_zipper 25 avgt 5 143.262 ± 4.923 us/op +[info] RegexBenchmark.email_accepting_zipper 30 avgt 5 186.483 ± 3.277 us/op +[info] RegexBenchmark.email_accepting_zipper 35 avgt 5 167.585 ± 5.342 us/op +[info] RegexBenchmark.email_accepting_zipper 40 avgt 5 326.794 ± 14.666 us/op +[info] RegexBenchmark.email_accepting_zipper 45 avgt 5 259.978 ± 4.798 us/op +[info] RegexBenchmark.email_accepting_zipper 50 avgt 5 384.940 ± 8.356 us/op +[info] RegexBenchmark.email_accepting_zipper 55 avgt 5 383.148 ± 11.382 us/op +[info] RegexBenchmark.email_accepting_zipper 60 avgt 5 308.425 ± 7.061 us/op +[info] RegexBenchmark.email_accepting_zipper 65 avgt 5 302.070 ± 7.719 us/op +[info] RegexBenchmark.email_accepting_zipper 70 avgt 5 498.183 ± 14.809 us/op +[info] RegexBenchmark.email_accepting_zipper 75 avgt 5 603.322 ± 14.904 us/op +[info] RegexBenchmark.email_accepting_zipper 80 avgt 5 667.829 ± 21.001 us/op +[info] RegexBenchmark.email_accepting_zipper 85 avgt 5 657.087 ± 30.074 us/op +[info] RegexBenchmark.email_accepting_zipper 90 avgt 5 647.207 ± 23.904 us/op +[info] RegexBenchmark.email_accepting_zipper 95 avgt 5 655.493 ± 23.333 us/op +[info] RegexBenchmark.email_accepting_zipper 100 avgt 5 748.846 ± 34.922 us/op +[info] RegexBenchmark.email_accepting_zipper_mem 5 avgt 5 25.522 ± 0.628 us/op +[info] RegexBenchmark.email_accepting_zipper_mem 10 avgt 5 63.211 ± 1.420 us/op +[info] RegexBenchmark.email_accepting_zipper_mem 15 avgt 5 69.520 ± 2.032 us/op +[info] RegexBenchmark.email_accepting_zipper_mem 20 avgt 5 95.371 ± 1.902 us/op +[info] RegexBenchmark.email_accepting_zipper_mem 25 avgt 5 143.425 ± 4.022 us/op +[info] RegexBenchmark.email_accepting_zipper_mem 30 avgt 5 189.131 ± 5.476 us/op +[info] RegexBenchmark.email_accepting_zipper_mem 35 avgt 5 167.394 ± 6.828 us/op +[info] RegexBenchmark.email_accepting_zipper_mem 40 avgt 5 315.515 ± 10.823 us/op +[info] RegexBenchmark.email_accepting_zipper_mem 45 avgt 5 259.185 ± 9.896 us/op +[info] RegexBenchmark.email_accepting_zipper_mem 50 avgt 5 386.207 ± 6.020 us/op +[info] RegexBenchmark.email_accepting_zipper_mem 55 avgt 5 384.052 ± 11.435 us/op +[info] RegexBenchmark.email_accepting_zipper_mem 60 avgt 5 310.384 ± 16.814 us/op +[info] RegexBenchmark.email_accepting_zipper_mem 65 avgt 5 302.741 ± 8.232 us/op +[info] RegexBenchmark.email_accepting_zipper_mem 70 avgt 5 455.862 ± 11.901 us/op +[info] RegexBenchmark.email_accepting_zipper_mem 75 avgt 5 612.720 ± 32.223 us/op +[info] RegexBenchmark.email_accepting_zipper_mem 80 avgt 5 671.868 ± 6.215 us/op +[info] RegexBenchmark.email_accepting_zipper_mem 85 avgt 5 651.181 ± 16.807 us/op +[info] RegexBenchmark.email_accepting_zipper_mem 90 avgt 5 642.425 ± 19.682 us/op +[info] RegexBenchmark.email_accepting_zipper_mem 95 avgt 5 601.126 ± 16.864 us/op +[info] RegexBenchmark.email_accepting_zipper_mem 100 avgt 5 749.120 ± 22.018 us/op +[success] Total time: 16114 s (04:28:34), completed Nov 28, 2024, 5:38:54 PM From 80d9328efc4fff6f8fbd2be17d98eb61444580a8 Mon Sep 17 00:00:00 2001 From: Samuel Chassot Date: Tue, 3 Dec 2024 11:24:41 +0100 Subject: [PATCH 75/78] remove one main test --- .../verifiedlexer/src/main/scala/ch/epfl/lexer/Main.scala | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/Main.scala b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/Main.scala index d563a60a..259d69c4 100644 --- a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/Main.scala +++ b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/Main.scala @@ -191,11 +191,19 @@ object RegexBenchmark { // println(s"Matching $s with r -> $match31") // assert(match31) +<<<<<<< Updated upstream // val s2 = "abbbabbabbababccaaaabababbababbbababa" // val match32 = matchRMemSimp(r, s2.toStainless)(cache) // println(s"Matching $s2 with r -> $match32") // assert(match32) // } +======= + // val s2 = "abbbabbabbababccaaaabababbababbbababa" + // val match32 = matchRMemSimp(r, s2.toStainless)(cache) + // println(s"Matching $s2 with r -> $match32") + // assert(match32) + } +>>>>>>> Stashed changes def testSimp(): Unit = { val r = Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Concat(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Concat(EmptyLang(),EmptyExpr()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Concat(EmptyLang(),EmptyExpr()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang())),Star(Union(Concat(ElementMatch('a'),EmptyExpr()),Concat(ElementMatch('b'),EmptyExpr())))),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Concat(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Concat(EmptyLang(),EmptyExpr()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Concat(EmptyLang(),EmptyExpr()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang())),Star(Union(Concat(ElementMatch('a'),EmptyExpr()),Concat(ElementMatch('b'),EmptyExpr())))),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Concat(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Concat(EmptyLang(),EmptyExpr()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Concat(EmptyLang(),EmptyExpr()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang())),Star(Union(Concat(ElementMatch('a'),EmptyExpr()),Concat(ElementMatch('b'),EmptyExpr())))),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Concat(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Concat(EmptyLang(),EmptyExpr()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Concat(EmptyLang(),EmptyExpr()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang())),Star(Union(Concat(ElementMatch('a'),EmptyExpr()),Concat(ElementMatch('b'),EmptyExpr())))),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Concat(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Concat(EmptyLang(),EmptyExpr()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Concat(EmptyLang(),EmptyExpr()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang())),Star(Union(Concat(ElementMatch('a'),EmptyExpr()),Concat(ElementMatch('b'),EmptyExpr())))),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Concat(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Concat(EmptyLang(),EmptyExpr()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Concat(EmptyLang(),EmptyExpr()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang())),Star(Union(Concat(ElementMatch('a'),EmptyExpr()),Concat(ElementMatch('b'),EmptyExpr())))),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Concat(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Concat(EmptyLang(),EmptyExpr()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Union(Concat(EmptyLang(),EmptyExpr()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang())),Star(Union(Concat(ElementMatch('a'),EmptyExpr()),Concat(ElementMatch('b'),EmptyExpr())))),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()),EmptyLang()))))))) From e1c5fb96585f16d9273df907b1cd47bf71a26cbb Mon Sep 17 00:00:00 2001 From: Samuel Chassot Date: Tue, 3 Dec 2024 17:38:30 +0100 Subject: [PATCH 76/78] new benchmark --- .../Regex_benchmark_results.ipynb | 292 ++++++++++++++++-- 1 file changed, 265 insertions(+), 27 deletions(-) diff --git a/lexers/regex/verifiedlexer/benchmark_results/Regex_benchmark_results.ipynb b/lexers/regex/verifiedlexer/benchmark_results/Regex_benchmark_results.ipynb index dc8a6f55..5ee7ff6b 100644 --- a/lexers/regex/verifiedlexer/benchmark_results/Regex_benchmark_results.ipynb +++ b/lexers/regex/verifiedlexer/benchmark_results/Regex_benchmark_results.ipynb @@ -16,7 +16,7 @@ }, { "cell_type": "code", - "execution_count": 37, + "execution_count": 60, "metadata": {}, "outputs": [], "source": [ @@ -72,11 +72,11 @@ }, { "cell_type": "code", - "execution_count": 38, + "execution_count": 61, "metadata": {}, "outputs": [], "source": [ - "regex_data = [( 5, 0.967),\n", + "ab_star_regex_data = [( 5, 0.967),\n", " (10, 5.389),\n", " (15, 15.300),\n", " (20, 32.653),\n", @@ -94,7 +94,7 @@ " (80, 3120.272)]\n", " \n", " \n", - "regex_mem_data = [(5, 4.212),\n", + "ab_star_regex_mem_data = [(5, 4.212),\n", " (10, 28.118),\n", " (15, 84.303),\n", " (20, 189.262),\n", @@ -111,7 +111,7 @@ " (75, 8247.552),\n", " (80, 10033.315)]\n", "\n", - "zipper_data = [ (5 , 0.360),\n", + "ab_star_zipper_data = [ (5 , 0.360),\n", " (10 , 0.732),\n", " (15 , 1.192),\n", " (20 , 1.452),\n", @@ -126,12 +126,94 @@ " (65 , 4.958),\n", " (70 , 5.331),\n", " (75 , 5.596),\n", - " (80 , 6.061)]" + " (80 , 6.061)]\n", + "\n", + "\n", + "email_regex = [(5, 14.479),\n", + " (10, 63.107),\n", + " (15, 140.479),\n", + " (20, 271.257),\n", + " (25, 418.221),\n", + " (30, 750.992),\n", + " (35, 1025.407),\n", + " (40, 1690.905),\n", + " (45, 2119.223),\n", + " (50, 3013.542),\n", + " (55, 3534.553),\n", + " (60, 4176.843),\n", + " (65, 5587.623),\n", + " (70, 6862.979),\n", + " (75, 8650.853),\n", + " (80, 9148.193),\n", + " (85, 11397.081),\n", + " (90, 13581.383),\n", + " (95, 15851.598),\n", + " (100, 17424.544)]\n", + "email_regex_mem = [(5, 97.926),\n", + " (10, 336.453),\n", + " (15, 673.542),\n", + " (20, 1156.384),\n", + " (25, 1837.122),\n", + " (30, 2967.740),\n", + " (35, 3701.245),\n", + " (40, 7804.431),\n", + " (45, 6639.778),\n", + " (50, 11605.145),\n", + " (55, 12214.834),\n", + " (60, 12016.321),\n", + " (65, 14362.316),\n", + " (70, 20401.022),\n", + " (75, 29995.169),\n", + " (80, 32220.514),\n", + " (85, 34271.337),\n", + " (90, 38066.820),\n", + " (95, 40710.121),\n", + " (100, 55536.854)]\n", + "email_zipper = [(5 , 25.572),\n", + " (10 , 63.562),\n", + " (15 , 69.579),\n", + " (20 , 95.436),\n", + " (25 , 143.262),\n", + " (30 , 186.483),\n", + " (35 , 167.585),\n", + " (40 , 326.794),\n", + " (45 , 259.978),\n", + " (50 , 384.940),\n", + " (55 , 383.148),\n", + " (60 , 308.425),\n", + " (65 , 302.070),\n", + " (70 , 498.183),\n", + " (75 , 603.322),\n", + " (80 , 667.829),\n", + " (85 , 657.087),\n", + " (90 , 647.207),\n", + " (95 , 655.493),\n", + " (100 , 748.846)]\n", + "email_zipper_mem = [( 5, 25.522),\n", + " ( 10, 63.211),\n", + " ( 15, 69.520),\n", + " ( 20, 95.371),\n", + " ( 25, 143.425),\n", + " ( 30, 189.131),\n", + " ( 35, 167.394),\n", + " ( 40, 315.515),\n", + " ( 45, 259.185),\n", + " ( 50, 386.207),\n", + " ( 55, 384.052),\n", + " ( 60, 310.384),\n", + " ( 65, 302.741),\n", + " ( 70, 455.862),\n", + " ( 75, 612.720),\n", + " ( 80, 671.868),\n", + " ( 85, 651.181),\n", + " ( 90, 642.425),\n", + " ( 95, 601.126),\n", + " (100, 749.120)]" ] }, { "cell_type": "code", - "execution_count": 39, + "execution_count": 62, "metadata": {}, "outputs": [], "source": [ @@ -141,10 +223,42 @@ }, { "cell_type": "code", - "execution_count": 40, + "execution_count": 63, "metadata": {}, "outputs": [], "source": [ + "def plot_data_comparison4(\n", + " plot_title: str,\n", + " data1: list[tuple[int, float]],\n", + " data1_label: str,\n", + " data2: list[tuple[int, float]],\n", + " data2_label: str,\n", + " data3: list[tuple[int, float]],\n", + " data3_label: str,\n", + " data4: list[tuple[int, float]],\n", + " data4_label: str,\n", + " x_label: str,\n", + " y_label: str):\n", + " x = [x for x, _ in data1]\n", + " y1 = [y for _, y in data1]\n", + " y2 = [y for _, y in data2]\n", + " y3 = [y for _, y in data3]\n", + " y4 = [y for _, y in data4]\n", + "\n", + " plt.plot(x, y1, label=data1_label, color='tab:blue')\n", + " plt.plot(x, y2, label=data2_label, color='tab:orange')\n", + " plt.plot(x, y3, label=data3_label, color='black')\n", + " plt.plot(x, y4, label=data4_label, color='tab:green')\n", + "\n", + " # Set to log scale\n", + " plt.yscale('log')\n", + " plt.xlabel(x_label)\n", + " plt.ylabel(y_label)\n", + " plt.title(plot_title)\n", + " plt.legend()\n", + "\n", + " plt.show()\n", + "\n", "def plot_data_comparison3(\n", " plot_title: str, \n", " data1: list[tuple[int, float]], \n", @@ -195,20 +309,23 @@ " plt.show()\n", " \n", "\n", - "def plot_data(plot_title, data: list[tuple[int, float]], data_label: str, x_label: str, y_label: str):\n", + "def plot_data_with_regression(plot_title, data: list[tuple[int, float]], data_label: str, x_label: str, y_label: str, degree: int):\n", " x = [x for x, _ in data]\n", " y = [y for _, y in data]\n", " plt.plot(x, y, label=data_label)\n", " plt.xlabel(x_label)\n", " plt.ylabel(y_label)\n", - " plt.title(plot_title)\n", + " z = np.polyfit(x, y, degree)\n", + " p = np.poly1d(z)\n", + " plt.plot(x, p(x), \"r--\") \n", + " plt.title(plot_title + \", with regression degree \" + str(degree))\n", " plt.legend()\n", " plt.show()\n" ] }, { "cell_type": "code", - "execution_count": 41, + "execution_count": 64, "metadata": {}, "outputs": [ { @@ -225,11 +342,11 @@ "source": [ "plot_data_comparison3(\n", " plot_title = \"Matching regex (a|b)* against accepting strings\", \n", - " data1 = regex_data,\n", + " data1 = ab_star_regex_data,\n", " data1_label = \"Regex\",\n", - " data2 = zipper_data,\n", + " data2 = ab_star_zipper_data,\n", " data2_label = \"Zipper\",\n", - " data3 = regex_mem_data,\n", + " data3 = ab_star_regex_mem_data,\n", " data3_label = \"Regex with memoization\",\n", " x_label = \"String length\",\n", " y_label = \"Time (us)\")" @@ -237,7 +354,7 @@ }, { "cell_type": "code", - "execution_count": 42, + "execution_count": 65, "metadata": {}, "outputs": [ { @@ -254,9 +371,9 @@ "source": [ "plot_data_comparison2(\n", " plot_title = \"Matching regex (a|b)* against accepting strings\", \n", - " data1 = regex_data,\n", + " data1 = ab_star_regex_data,\n", " data1_label = \"Regex\",\n", - " data2 = zipper_data,\n", + " data2 = ab_star_zipper_data,\n", " data2_label = \"Zipper\",\n", " x_label = \"String length\",\n", " y_label = \"Time (us)\")" @@ -264,12 +381,115 @@ }, { "cell_type": "code", - "execution_count": 43, + "execution_count": 66, "metadata": {}, "outputs": [ { "data": { - "image/png": "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nGBvb4/8/PwK2y6VZGxszJEhIqIqZOn+axAC6OqjvaNDgMSBaPr06ejevTvc3d2RkZGB9evXIzw8HLt374ZCocCIESMwZcoU1KhRA1ZWVhg/fjz8/f3RunVrAEDXrl3h4+ODd955B4sXL0ZCQgI++eQThISEqEd43nvvPSxbtgwffvghhg8fjv3792PDhg3YsWNHhffH0NCQX9ZERET/uZKYge1R2j13qJikgSgpKQlDhgzBvXv3oFAo4Ovri927d6NLly4Ais5xY2BggD59+iA3NxeBgYFYvny5+v6GhobYvn07xowZA39/f1SrVg3BwcGYN2+euo2Xlxd27NiByZMnY8mSJXB1dcWqVasQGBj40vtLRESkT5bsuwohgG4NHOHj/PTD3qWmdech0kblOY8BERERATEJGei25BCEAHZObAdvp5f//Vklz0NEREREumPJvisQAuje0FGSMFReDERERERUoS4nKPHP+aLzAU7U4iPLHsVARERERBVqyd6rAICgRk6o76j9o0MAAxERERFVoIvxSuyMToBMVnVGhwAGIiIiIqpAS/ZdAVA0OlTXwVLiasqOgYiIiIgqxIX4dOy+kFg0OtS56owOAQxEREREVEG++W/uUA9fZ9SpQqNDAAMRERERVYDou+kIu1g8OlRb6nLKjYGIiIiIXljx6FDPxs6obV+1RocABiIiIiJ6QefvpGPvpUQYyIDxnarW3KFiDERERET0Qr7ZW3RkWdHokIXE1TwfBiIiIiJ6budup2Hf5SQYyIAJVezIskcxEBEREdFzW7KvaO5QryYuqGlXNUeHAAYiIiIiek6Rt9Ow/3ISDA1kGF+FR4cABiIiIiJ6TsVzh3o1cYGXbTWJq3kxDERERERUbmfiHiA85n7R6FCnqnfeoccxEBEREVG5FZ936M2mLvCs4qNDAAMRERERldPpWw9w6IrujA4BDERERERUTsVzh/o0c4GHTdUfHQIYiIiIiKgcTt9KxeGryTAykFXZs1KXhoGIiIiIyuzrsKK5Q339XOFWw1ziaioOAxERERGVycmbqThyrWh0KKSjbswdKsZARERERGVSPHeoX3PdGh0CGIiIiIioDE7EpuLotRQYG+re6BDAQERERERl8HVY8eiQG1yr69boEMBARERERM/w740URNzQ3dEhgIGIiIiInqF4dKh/cze4WJtJXE3l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", + "image/png": "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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "plot_data_with_regression(\n", + " plot_title = \"Matching regex (a|b)* against accepting strings\", \n", + " data = ab_star_regex_data,\n", + " data_label = \"Regex\",\n", + " x_label = \"String length\",\n", + " y_label = \"Time (us)\",\n", + " degree = 3)\n" + ] + }, + { + "cell_type": "code", + "execution_count": 67, + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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i/c7pm/y5Mn2dZArVy769+/PtWvX4rWJW4bHXd9RUVEMGTKErFmz4ufnR6tWrfj333/v2TY1rn+wfR5WrFgRb29vMmXKRKdOnThz5sxDl93T0zPJOtPcHudB+fLlo3r16nz33Xc0b94csH1wh4WF0alTJz7//PMEj/nss89o1aoVXbt2JTo6mgULFtC+fXuWLFlCixYtAPj222/p3bs3VapUoW/fvgAULFgQsHXTV6lShWvXrtG3b1+KFSvG2bNnWbRoEREREfF+3Q0cOJCMGTPy7rvvcvLkST799FMGDBjA999//9BlGzt2LC4uLgwdOpSwsDA++ugjunbtypYtW+xtpkyZwoABA6hduzZDhgzh5MmTtGnThowZMz40DL344ov2MBJn2bJlzJs3j2zZsj3wsbGxsTRr1oxq1arx0UcfsWzZMt59911u377N6NGj47WdM2cO169fp3///kRGRvLZZ5/RoEED9u7dS/bs2QHYv38/NWvWJHfu3AwfPhwfHx8WLlxImzZt+PHHH3nmmWfizbNfv35kzZqVd95554E9fWfPnuX06dMJPqDB9qHh6+vLq6++iq+vL3/++SfvvPMO4eHhif4Fk5hlAwgICKBgwYJs3LiRIUOG2Ke7uLjEC6OJ+dW6bds2/vrrLzp16kSePHk4efIkU6ZMoV69ehw4cIAMGTLYl71+/fpYLBZGjBiBj48P06dPv2dP3ZOui/nz53P9+nVefPFFLBYLH330EW3btuWff/7B3d0dgHbt2rF//34GDhxIvnz5CA0NZeXKlZw+fZp8+fLx6aefMnDgQHx9fXnrrbcA4q3De0nMexlg1KhRjBw5kho1ajB69Gg8PDzYsmULf/75J02aNLGvgxdeeIGSJUsyYsQIAgMD2blzJ8uWLbP/gPzzzz9p3rw5FStW5N1338XFxYWZM2fSoEED1q9fT5UqVeLV16FDB/Lly8eYMWPYvHkzn3/+OVevXrUHmgd9ztzPsWPHePbZZ+nVqxc9evTgm2++oWfPnlSsWJGSJUsCj7bt7yUptlVi36NJvUyhoaE0adKErFmzMnz4cAIDAzl58iQ//fRTopb9TqVKlSIwMJB169bRqlUrANavX4+Liwu7d+8mPDwcf39/rFYrf/31l30b3q148eKMHj2ad955h759+1K7dm0AatSoYW9z9epVmjVrRtu2benQoQOLFi1i2LBhlC5d2v7d9iB//vknCxcuZMCAAWTJkoV8+fJx4cIFqlWrZg+FWbNmZenSpfTq1Yvw8HAGDx4M2HpiGzRoQEhICK+88go5cuRg/vz5rF69+p7PdfnyZZo3b06nTp3o1q0b2bNnx2q10qpVKzZs2EDfvn0pXrw4e/fuZeLEiRw5csR+CMP+/ftp2bIlZcqUYfTo0Xh6enLs2LF4P5ymTZvGoEGDePbZZ+0/lPbs2cOWLVvu25kDth9bo0aNolGjRrz88sscPnyYKVOmsG3bNjZu3Gj/LHrS9d27d2/mzp1Lly5dqFGjBn/++We8z5s4qXH9A3zwwQe8/fbbdOjQgd69e3Px4kW++OIL6tSpw86dOwkMDHzg8icZ8whmzpxpALNt2zYzadIk4+fnZyIiIowxxrRv397Ur1/fGGNMcHCwadGiRbzHxrWLEx0dbUqVKmUaNGgQb7qPj4/p0aNHgufu3r27cXFxMdu2bUtwn9VqjVdfo0aN7NOMMWbIkCHG1dXVXLt2zT6tbt26pm7duvbbq1evNoApXry4iYqKsk//7LPPDGD27t1rjDEmKirKZM6c2VSuXNnExMTY282aNcsA8eaZGEePHjUBAQGmcePG5vbt28YYY06cOGEAM3PmTHu7Hj16GMAMHDgw3nK3aNHCeHh4mIsXL8Z7rLe3t/n333/tbbds2WIAM2TIEPu0hg0bmtKlS5vIyMh486xRo4YpXLiwfVrceq1Vq5a9xgdZtWqVAcxvv/2W4L67XwfGGPPiiy+aDBkyxKsjbjlWr16dYFpili1OkyZNTPHixe3L1qVLF1OpUiWza9cuExwcbP755x/TuHFj07hxY3P9+vX7LtO96t60aZMBzJw5c+zTBg4caCwWi9m5c6d92uXLl02mTJkMYE6cOPHI66JHjx4mODg4wXrInDmzuXLlin36L7/8Em+9X7161QDm448/vu9yGWNMyZIlH+l1m5j38tGjR42Li4t55plnTGxsbLz2ce/Na9euGT8/P1O1alVz69ate7axWq2mcOHCpmnTpvHe0xERESZ//vymcePG9mnvvvuuAUyrVq3izatfv34GMLt377ZPu9/nTNxr/c7tFBwcbACzbt06+7TQ0FDj6elpXnvtNfu0R9n2d3vSbfWg92hKLNPPP/9s/25ICi1atDBVqlSx327btq1p27atcXV1NUuXLjXGGLNjxw4DmF9++cXe7u7P9W3btiX4LL2z7d3v36ioKJMjRw7Trl27h9YIGBcXF7N///5403v16mVy5sxpLl26FG96p06dTEBAgP39M378eAOYxYsX29vcunXLFCtWLMFnX1ytX331Vbx5fvvtt8bFxcWsX78+3vSvvvrKAGbjxo3GGGMmTpxoAPv3xL20bt3alCxZ8oHLfPdrKTQ01Hh4eJgmTZrEe59PmjTJAOabb75JsAyPs7537dplANOvX79407t06WIA8+6779qnpcb1f/LkSePq6mo++OCDeO327t1r3NzcEkx/kAe9phPjsYds6dChA7du3WLJkiVcv36dJUuWPPDXgLe3t/3/V69eJSwsjNq1a7Njx46HPpfVamXx4sU8/fTTVKpUKcH9d/fU9O3bN9602rVrExsbm6ihQ55//vl4vYZxvw7jjgvbvn07ly9fpk+fPri5/a+jtGvXrmTMmPGh87/TzZs3eeaZZ8iYMSPfffddoo7hGzBggP3/cb9koqOjWbVqVbx2bdq0IXfu3PbbVapUoWrVqvz3v/8F4MqVK/z555906NCB69evc+nSJS5dusTly5dp2rQpR48e5ezZs/Hm2adPn0TVePnyZYB7ro87Xwdxz1u7dm0iIiI4dOjQQ+edmGW7U8aMGbl06RKAfTfe5s2b7QfD5s+fnxUrVth72+7nzrpjYmK4fPkyhQoVIjAwMN5reNmyZVSvXp1y5crZp2XKlImuXbs+cJ6Psy46duwYbx3f/Vr19vbGw8ODNWvWcPXq1YfOL7ES815evHgxVquVd955BxeX+B8zce/NlStXcv36dYYPH46Xl9c92+zatYujR4/SpUsXLl++bH+d3rx5k4YNG7Ju3TqsVmu8x/bv3z/e7YEDBwLc8/WRWCVKlLCvX4CsWbNStGhR+7qGR9v2d0uqbZXY9ygk7TLF9VIsWbKEmJiYx64/TtzrKa63csOGDTz11FOUK1eO9evXA7beP4vFQq1atR77eXx9fenWrZv9toeHB1WqVIm3Dh6kbt26lChRwn7bGMOPP/7I008/jTHG/nq9dOkSTZs2JSwszP4+WbZsGblz57b3ZoLtJMk+ffrc87k8PT15/vnn40374YcfKF68OMWKFYv3XA0aNACw91rFbZ9ffvklwfslTmBgIP/++2+CQ0QeZNWqVURH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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "plot_data_with_regression(\n", + " plot_title = \"Matching zipper (a|b)* against accepting strings\", \n", + " data = ab_star_zipper_data,\n", + " data_label = \"Zipper\",\n", + " x_label = \"String length\",\n", + " y_label = \"Time (us)\",\n", + " degree = 1)" + ] + }, + { + "cell_type": "code", + "execution_count": 68, + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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cve94t7n6e7f8s6dO43bIiMjFUtLS+Xtt982bnuS5/6/nvW5ult3o0aNlKysrAI/pj/++MP4GSCyyakrE9arVy9SU1NZt24diYmJrFu37qGnrQCsra2NP9++fZv4+HgaN27MkSNHHntfBoOBVatW0bFjR+rUqXPf5f89Jz1kyJAc2xo3boxer+fq1auPva9BgwblaB1q3LgxACEhIUB2s21MTAyDBw/GzOzfRsuXXnqJEiVKPPb2f//9dwwGA7169SI6Otr45enpSUBAANu2bcuxv52dHX369DH+XqFCBZycnAgMDKRevXrG7Xd/vlsn5HzMMzMziYmJwd/fHycnp1w97rlVtmxZ2rRpk2Pb8uXLady4MSVKlMhxnK1atUKv17Nz504A1q9fj7m5OYMHDzZeV6vVMnz48By3Fxsby9atW+nVqxeJiYnG24uJiaFNmzZcvHiRGzduGPefPXs2jo6O9OjRgw8//JB+/frRuXPnxx5Lbh+z9evX06BBA2rUqGHc5uzszEsvvfTI27xbe+PGjUlJSeHcuXOPral37945/rb++zdpbW2NhYUF27dv5/bt24+9vdzKzWt21apVGAwGPvroo/v6eN19DW7atInExETGjRuHlZXVA/c5duwYFy9epG/fvsTExBif3+TkZFq2bMnOnTvvO+X537+RkSNHAvDXX3899TFXqlTJ+PgCuLm5UaFChRyvqyd57v8rr56rwYMHo9PpcrVvXh7T3dbmdevWkZmZ+dT1mxI5dWXC3NzcaNWqFYsXLyYlJQW9Xk+PHj0euv+6dev49NNPOXbsGOnp6cbtuek4FxUVRUJCAlWqVMlVbb6+vjl+v/shkZs3lsdd925Y8vf3z7GfmZlZruZAuXjxIoqiEBAQ8MDL756KuKtUqVL3PUaOjo74+Pjct+3eOgFSU1OZMmUK8+fP58aNGzn6rMTHxz+21twqW7bsfdsuXrzIiRMncHNze+B1IiMjgezHs2TJkvedRvvv43vp0iUUReHDDz/kww8/fOhtent7A9lv0t988w09e/bEw8PjgadTHyS3j9nVq1dp0KDBfdf/b92Qfcrtf//7H1u3biUhISHHZbl5Hh73N2lpackXX3zB22+/jYeHB/Xr16dDhw7079//mQYG5OY1e/nyZbRaLZUqVXro7Vy+fBngka/fixcvAjzwlOVd8fHxOQLff19Dfn5+aLXaZ5pH5r+PNWQ/3ve+rp7kuf+vvHquHvSae5i8PKamTZvSvXt3Jk2axIwZM2jWrBldunShb9++xbYjvQQdE9e3b18GDx5MeHg47dq1y9G35F67du2iU6dONGnShDlz5lCyZEnMzc2ZP3/+A4epP6uH/adz74dWflw3NwwGAxqNhr///vuB92VnZ5erenJT58iRI5k/fz6jR4+mQYMGODo6otFo6NOnz33/HT+Le//zv8tgMPD8888zduzYB16nfPnyT3Qfd+t955137ms9uuu/b8obNmwAsgPB9evXH/r3ea+8fszi4uJo2rQpDg4OfPzxx/j5+WFlZcWRI0d47733cnWbuXmuR48eTceOHVm1ahUbNmzgww8/ZMqUKWzdupWaNWs+cd0F/Zq9+zhMnTo1R6vCvf772vivvBhtlN+vf8ib5+pBr7mHyctj0mg0rFixgv3797N27Vo2bNjAK6+8wvTp09m/f/9jnyNTJEHHxHXt2pWhQ4eyf//+R3b0XblyJVZWVmzYsCFH6p8/f/59+z7ozcrNzQ0HBwdOnTqVN4U/g7tzVFy6dInmzZsbt2dlZXHlyhWqVav2yOv7+fmhKAply5Z94g/7J7VixQoGDBjA9OnTjdvS0tKIi4vL1/uF7ONMSkqiVatWj9yvdOnSbNu2jZSUlBytOv8d6VauXDkgu8XrcbcJ2U3x8+bNY+zYsfz6668MGDCAf/75J8fpxgfJ7WNWunTpB47G+++27du3ExMTw++//55jnqLQ0NDHHsOT8vPz4+233+btt9/m4sWL1KhRg+nTp7No0SLgyYJAbl+zfn5+GAwGzpw589CAcrcD8KlTpx7a6nF3HwcHh1w9v5DdCnRvy8alS5cwGAw5WlbzY6h1bp/7R8nL5yovPOkx1a9fn/r16zN58mQWL17MSy+9xNKlS3nttdfyu9RCR/romDg7Ozvmzp3LxIkT6dix40P30+l0aDSaHMNpr1y58sCJvGxtbe/7UNFqtXTp0oW1a9c+cHmHvPxv63Hq1KmDi4sLP/zwQ455hH799ddcnRrr1q0bOp2OSZMm3Ve3oijExMTkWa06ne6++5g1a9Z9w5rzQ69evdi3b5+xVeVecXFxxseuTZs2ZGZm8sMPPxgvNxgMBAcH57iOu7s7zZo147vvvuPWrVv33ea90wfExcUZR9t89tlnzJs3jyNHjvDZZ589tu7cPmZt2rRh3759HDt2zLgtNjaWX3/99b7bg5x/oxkZGY8d/fUkUlJScgyrhuwPUnt7+xynnB702nqY3L5mu3Tpglar5eOPP76vderuMbdu3Rp7e3umTJlyX51396lduzZ+fn5MmzaNpKSk++r57/QQwH1/I7NmzQIwjgSFJzvm3Mrtc/8g+fFc5YXcHtPt27fve33cDbj31l+cSItOMfCoc+p3vfDCC3z11Ve0bduWvn37EhkZSXBwMP7+/pw4cSLHvrVr12bz5s189dVXeHl5UbZsWerVq8dnn33Gxo0badq0KUOGDCEwMJBbt26xfPlydu/enavTEnnBwsKCiRMnMnLkSFq0aEGvXr24cuUKCxYswM/P77H/ifn5+fHpp58yfvx447B0e3t7QkND+eOPPxgyZAjvvPNOntTaoUMHfvnlFxwdHalUqRL79u1j8+bNuLi45MntP8q7777LmjVr6NChg3Eoa3JyMidPnmTFihVcuXIFV1dXunTpQlBQEG+//TaXLl2iYsWKrFmzhtjYWCDnf7bBwcE0atSIqlWrMnjwYMqVK0dERAT79u3j+vXrHD9+HIA333yTmJgYNm/ejE6no23btrz22mt8+umndO7cmerVqz+07tw+ZmPHjmXRokU8//zzjBw50jgc19fXl9jYWGPdDRs2pESJEgwYMIBRo0ah0Wj45Zdf8jScX7hwgZYtW9KrVy8qVaqEmZkZf/zxBxERETk6steuXZu5c+fy6aef4u/vj7u7Oy1atHjgbeb2Nevv788HH3zAJ598QuPGjenWrRuWlpYcPHgQLy8vpkyZgoODAzNmzOC1116jbt269O3blxIlSnD8+HFSUlL4+eef0Wq1zJs3j3bt2lG5cmUGDRqEt7c3N27cYNu2bTg4OLB27docNYaGhtKpUyfatm3Lvn37WLRoEX379s3x/D7s/eRZ5Pa5f5D8eK7yQm6P6eeff2bOnDl07doVPz8/EhMT+eGHH3BwcKB9+/b5Vl+hVqBjvES+u3d4+aM8aHj5jz/+qAQEBCiWlpZKxYoVlfnz5xuHid7r3LlzSpMmTRRra2sFyDE09OrVq0r//v0VNzc3xdLSUilXrpwyfPhw43Dwh9X3oOHTDxtevnz58hzXvTvE995hzoqiKN98841SunRpxdLSUgkKClL27Nmj1K5dW2nbtu0jH5u7Vq5cqTRq1EixtbVVbG1tlYoVKyrDhw9Xzp8/n6PGypUr33fdBz2+iqIogDJ8+HDj77dv31YGDRqkuLq6KnZ2dkqbNm2Uc+fOKaVLl87xuD7r8PIH1aIoipKYmKiMHz9e8ff3VywsLBRXV1elYcOGyrRp05SMjAzjflFRUUrfvn0Ve3t7xdHRURk4cKCyZ88eBVCWLl2a4zYvX76s9O/fX/H09FTMzc0Vb29vpUOHDsqKFSsURfl36PX06dNzXC8hIUEpXbq0Ur169Rz3/V+5fcwURVGOHj2qNG7cWLG0tFRKlSqlTJkyRfnmm28UQAkPDzfut2fPHqV+/fqKtbW14uXlpYwdO9Y4HcCjHvO7f3sPGooMKBMmTFAURVGio6OV4cOHKxUrVlRsbW0VR0dHpV69espvv/2W4zrh4eHKCy+8oNjb2+dqKoTcvmYVRVF++uknpWbNmoqlpaVSokQJpWnTpsqmTZty7LNmzRqlYcOGirW1teLg4KAEBQUpS5Ysue8x7datm+Li4qJYWloqpUuXVnr16qVs2bLFuM/dGs6cOaP06NFDsbe3V0qUKKGMGDHivuHrD3s/edhQ7Af9Lf/3veJunbl57v/rWZ+rR70HF8QxHTlyRHnxxRcVX19fxdLSUnF3d1c6dOigHDp06KHHbOo0ilKA5xSEUJHBYMDNzY1u3brlOA0jns6qVavo2rUru3fv5rnnnlO7nFwbPXo03333HUlJSbke/iuezMSJE5k0aRJRUVG4urqqXY6RKT73pnhMeU366AiTlJaWdt+ph4ULFxIbG5vrJSDEv/67NpZer2fWrFk4ODhQq1Ytlap6vP/WHRMTwy+//EKjRo3kQ8HEmeJzb4rHVBCkj44wSfv37+ett96iZ8+euLi4cOTIEX788UeqVKlCz5491S6vyBk5ciSpqak0aNCA9PR0fv/9d/bu3ctnn332RMNoC1qDBg1o1qwZgYGBRERE8OOPP5KQkPDQeX6E6TDF594Uj6kgSNARJqlMmTL4+PjwzTffEBsbi7OzM/379+fzzz+XFdGfQosWLZg+fTrr1q0jLS0Nf39/Zs2axYgRI9Qu7ZHat2/PihUr+P7779FoNNSqVYsff/wxxzByYZpM8bk3xWMqCNJHRwghhBAmS/roCCGEEMJkSdARQgghhMkq9n10DAYDN2/exN7evsCn9BZCCCHE01EUhcTERLy8vNBqH95uU+yDzs2bN+9bZVoIIYQQRUNYWBilSpV66OXFPujY29sD2Q+Ug4ODytUIIYQQIjcSEhLw8fExfo4/TLENOsHBwQQHBxsXxHNwcJCgI4QQQhQxj+t2UuyHlyckJODo6Eh8fLwEHSGEEKKIyO3nt4y6EkIIIYTJkqAjhBBCCJMlQUcIIYQQJqvYBp3g4GAqVapE3bp11S5FCCGEEPlEOiNLZ2QhhBCiyJHOyEIIIYQo9iToCCGEEMJkSdARQgghhMmSoCOEEEIIkyVBRwghhBAmq9gGHRleLoQQQpg+GV4uw8uFEEKIfJGcnMyhQ4do2rRpnt+2DC8XQgghhCoyMjKYM2cOfn5+tG/fnvDwcNVqkaAjhBBCiDxhMBhYvHgxgYGBDB8+nIiICDw9Pbl27ZpqNUnQEUIIIcQzURSFv//+m1q1avHSSy8REhKCh4cHwcHBnD17lqCgINVqM1PtnoUQQghR5O3bt4/x48ezY8cOABwcHBg7dixvvvkmdnZ2KlcnQUcIIYQQT+HMmTO8//77rF69GgBLS0tGjBjB+PHjcXFxUbm6fxXbU1cyvFwIIYR4cteuXWPQoEFUrVqV1atXo9VqeeWVV7h48SLTpk0rVCEHZHi5DC8XQgghciE6OprPPvuM4OBgMjIyAOjatSuTJ08mMDCwwOvJ7ee3nLoSQgghxEMlJSUxY8YMpk6dSmJiIgDNmjXj888/p169eipX93gSdIQQQghxn4yMDL7//ns++eQTIiMjAahZsyZTpkyhdevWaDQalSvMHQk6QgghhDAyGAwsWbKEDz/8kNDQUAD8/Pz49NNP6dWrF1pt0ereK0FHCCGEECiKwl9//cX777/PiRMnAPD09GTChAm8+uqrmJubq1zh05GgI4QQQhRze/fuZdy4cezatQsAR0dH3nvvPUaNGoWtra3K1T0bCTpCCCFEMZKcnExYWBhhYWFcu3aNNWvWsGbNGgCsrKwYOXIk7733XqEbJv60JOgIIYQQJiIrK4ubN29y7do1Y5D57/fY2Nj7rnd3LpwJEyZQqlQpFSrPP8U26AQHBxMcHIxer1e7FCGEEOKxFEUhOjr6oSEmLCyMmzdvYjAYHntb9vb2+Pr64uPjQ0BAAMOGDaNixYoFcBQFTyYMlAkDhRBCFCJpaWmcOXOG48ePc+LECU6dOsXVq1cJCwsjLS3tsdc3NzfHx8cHHx8fY5j573dHR8cCOJL8JRMGCiGEEIWYoiiEhYVx4sSJHF/nz59/ZKuMp6fnQwOMr68v7u7uRW4IeH6SoCOEEELks+TkZE6dOnVfqImLi3vg/s7OzlSvXp3q1atTtWpV/Pz88PHxwdvbG0tLy4ItvoiToCOEEELkEYPBwJUrV3KEmePHj3P58mUe1FPEzMyMwMBAqlWrluOrZMmSRWbm4cJOgo4QQgjxBDIzMwkPD+fGjRvcuHGD69evc+7cOY4fP87JkydJSkp64PU8PT3vCzSBgYFYWFgU8BEULxJ0hBBCiDsSEhJyBJi7P9/7FRER8cDWmbssLCyoXLmyMczcPf3k7u5egEci7pKgI4QQwuTp9XoiIiIeGl7ufj2sNea/zMzM8PLywtvbG29vb/z9/alevTrVqlUjICCgyC6XYIok6AghhCjy0tPTuXbtGleuXOHq1atcvXrV+POVK1e4efNmrudNc3R0NAYYb29vSpUqleN3b29v3NzcZGRTESFBRwghRKGXnJz8wABzd9utW7ceexs6nQ5PT89HBhhvb+8iv7aTyEmCjhBCCNUlJCRw5cqV+0LM3e/R0dGPvQ0bGxvKlClD6dKlKV26tPHnMmXK4Ovri4eHBzqdrgCORhQmEnSEEEKoJjY2lrfeeotffvnlkR18ARwcHChTpsxDw4yLi4sMyRb3KbZBR9a6EkIIda1atYo33niD8PBwAFxcXHIEl/+GGScnJ3ULFkWSrHUla10JIUSBio6OZtSoUSxZsgSAihUrMn/+fOrXr69yZaIoye3nt3QZF0IIUWBWrFhB5cqVWbJkCVqtlnHjxnH06FEJOSLfFNtTV0IIIQpOZGQkw4cPZ8WKFQBUqVKF+fPnU6dOHZUrE6ZOWnSEEELkG0VRWLp0KZUqVWLFihXodDr+97//cejQIQk5okBIi44QQoh8ER4ezhtvvMGqVasAqF69OvPnz6dmzZrqFiaKFWnREUIIkacURWHRokVUqlSJVatWYWZmxsSJEzlw4ICEHFHgpEVHCCFEnrl58yZDhw5l3bp1ANSqVYv58+dTrVo1lSsTxZW06AghhHhmiqKwYMECKleuzLp167CwsGDy5Mns379fQo5QlbToCCGEeCZhYWEMHTqUv//+G4C6desyf/58KleurHJlQkiLjhBCiKekKAo//PADlStX5u+//8bS0pIvvviCvXv3SsgRhYa06AghhHhiV69eZfDgwWzatAmA+vXrM3/+fCpWrKhyZULkJC06Qgghcs1gMDB37lyqVKnCpk2bsLKyYvr06ezevVtCjiiUpEVHCCFEroSEhPDaa6+xbds2ABo1asSPP/5I+fLlVa5MiIeTFh0hhBCPpNfrmTVrFlWrVmXbtm3Y2Njw9ddfs2PHDgk5otCTFh0hhBAPdfDgQYYNG8ahQ4cAaNasGfPmzcPPz0/lyoTIHWnREUIIcZ+YmBiGDh1KvXr1OHToEA4ODgQHB7NlyxYJOaJIKbZBJzg4mEqVKlG3bl21SxFCiELDYDDwww8/UL58eb7//nsURaFfv36cP3+eYcOGodUW248NUURpFEVR1C5CTQkJCTg6OhIfH4+Dg4Pa5QghhGoOHz7MsGHDOHDgAABVqlQhODiYJk2aqFyZEPfL7ee3RHMhhCjmYmNjGTZsGHXr1uXAgQPY29szY8YMjhw5IiFHFHnSGVkIIYopg8HAggULeO+994iOjgagb9++TJs2jZIlS6pcnTAVmXoD5jr12lUk6AghRDF09OhRhg8fzr59+wCoVKkSwcHBNGvWTN3CRJGVmqHnclQSlyKzvy5GJnIpMomrMSkcm9AaO0t1IocEHSGEKEbi4uL48MMPmTNnDgaDATs7OyZOnMioUaMwNzdXuzxRBCSkZRrDzKXIJC5GJHIpKonrt1N5WK/fy5FJVPdxKtA675KgI4QQxYCiKCxcuJCxY8cSGRkJQO/evZk+fTre3t4qVycKo5ikdC7eE2juttJEJKQ/9DolbMwJcLfH38OOAHc7/N3tCHC3x8PBsgArz0mCjhBCmLgTJ04wfPhwdu/eDUDFihWZPXs2LVu2VLkyoTZFUYhISOdiZCIXI5K4FJXEpTvfY5MzHno9Twcr/O8Emewwk/3dxU69QPMwEnSEEMJExcfHM2HCBGbPno1er8fGxoYJEyYwevRoLCws1C5PqCQqMZ29l6PZfTGaPZeiuRmf9sD9NBooVcI6u4XmnlDj726Hg1XROc0pQUcIIUyMoij8+uuvvPPOO0RERADQo0cPvvrqK3x8fFSuThS05PQsDoTGsvtSdrA5F56Y43KdVkMZFxvjaaYADzv83LK/rC10KlWddyToCCGECTl16hTDhw9n586dAJQvX55Zs2bRunVrlSsTBSVTb+B4WJwx2By9FkeWIWcv4cpeDjTyd+U5f1fqlnE2iUDzMBJ0hBDCBCQkJDBp0iS+/vpr9Ho91tbWfPjhh4wZMwZLy8LXb0LkHUVRuBiZZDwVtT8khuQMfY59SpWwpnFAdrBpUM6lUPalyS8SdIQQooiKi4vj77//Zs2aNfz1118kJCQA0LVrV2bMmEHp0qVVrlDkl1vxqey+GM3eyzHsvhRNVGLOkVBONuY855cdbBr5u+LrYqNSpeqToCOEEEXIlStXWLNmDWvWrGHHjh1kZWUZLwsICODrr7+mXbt2KlYo8kN8aib7Q2LYcyma3ZeiCYlKznG5pZmWoLLOxtNRlUo6oNVqVKq2cJGgI4QQhZjBYODw4cOsWbOG1atXc/LkyRyXV6pUic6dO9OpUyeCgoJkdXEToSgKZ24lsOVsJFvPRXLiehz3drPRaqBqKSca+bvwnL8rtXxLYGVuuv1snoUEHSGEKGTS0tLYunUra9asYe3atdy8edN4mU6no3HjxnTq1ImOHTvi7++vYqUiL6Vl6tkXEsOWsxFsPRt537Dvcq62POf/bz8bR5uiM8RbTRJ0hBCiEIiOjubPP/9kzZo1bNiwgeTkf09N2NnZ0bZtWzp37ky7du1wcXFRsVKRl6IS09l2LpLNZyPYfSmalHs6EVuZa2nk70rLQA+alnfDy8laxUqLLgk6QgihkgsXLhhPSe3duxeDwWC8zNvbm06dOtG5c2eaNWsmI6dMhKIonAtPZMvZCDafjeT49bgc60N5OFjSMtCDVoHuNPRzldNReUCCjhCiWEpPTycyMpLIyEgiIiKIjIwkNjYWnU6HpaUllpaWWFlZGX++9+tR23W6h38w6fV69u/fbww358+fz3F5jRo1jOGmZs2aaDTSmdQUpGfp2R8Sy5azEWw5G8mNuNQcl1f1dqRloDutAj2o7OUgz3sek6AjhDAJiqKQkJBgDC2P+x4fH58vddwblP4bjG7evEl0dLRxX3Nzc5o1a0anTp3o1KkTvr6++VKTKHgxSelsPRfJlrOR7LoYlWNeG0uzf09JtajojqejlYqVmj4JOkKIIkFRFDZv3szJkyfvCy13v9LTH76q8oOYm5vj7u6Ou7s7Hh4euLi4oNfrSU9PJz09nbS0NOPPD9uWlpaGcs+5B71eT0pKCikpKQ+8TycnJ9q3b0+nTp1o27Ytjo6Oz/S4iMJBURQuRCSx+WwEW85GcDQs5ykpd3tLWga607KiB8/5u5r0TMSFjQQdIUShpygKY8aMYebMmY/d197e3hhcHvfdycnpmU8TKIpCVlbWY8NQeno6tra2BAUFYW4uo2VMxemb8fx+5AYbz4QTFpvzlFRlLwdjf5sqXo4yr41KTCbopKSkEBgYSM+ePZk2bZra5Qgh8ojBYOCNN97g+++/B6Bbt274+PgYw8q9wcXNzQ0bm4KdAVaj0WBubo65uTl2dnYFet9CHbHJGaw6eoMVh69z5laCcbuFmZbn/FxoGehBy0B3SjrKKKnCwGSCzuTJk6lfv77aZQgh8lBWVhaDBg1i0aJFaLVa5s2bx6BBg9QuSxRDmXoDO85HseLwdbaciyBTn31eykKn5flKHnSq4UXjAFdsLEzmY9VkmMQzcvHiRc6dO0fHjh05deqU2uUIIfJARkYGffv2ZeXKlZiZmbFo0SJ69+6tdlmimLkQkcjyQ2H8cfQm0Un/9gGrVsqRHrVL0am6F042FipWKB5H9bnCd+7cSceOHfHy8kKj0bBq1ar79gkODqZMmTJYWVlRr149Dhw4kOPyd955hylTphRQxUKI/JaamkrXrl1ZuXIlFhYWrFy5UkKOKDBxKRn8su8KnWbvpvWMnfywK5TopHRc7SwY3Lgs60c3Zs2IRvRvUEZCThGgeotOcnIy1atX55VXXqFbt273Xb5s2TLGjBnDt99+S7169Zg5cyZt2rTh/PnzuLu7s3r1asqXL0/58uXZu3evCkcghMhLSUlJdO7cma1bt2Jtbc2qVato3bq12mUJE6c3KOy6GMXyw9fZdDqCDH325I1mWg0tA93pUduHZhXcMNep3j4gnpBGuXdcpMo0Gg1//PEHXbp0MW6rV68edevWZfbs2UB2x0QfHx9GjhzJuHHjGD9+PIsWLUKn05GUlERmZiZvv/02H3300QPv4+4oiLsSEhLw8fEhPj4eBweHfD0+IcSjxcfH0759e/bu3YudnR1//vknTZo0UbssYcIuRyWx4vB1fj9ynYiEfz8bAks60LN2KTrX8MLFTmalLowSEhJwdHR87Oe36i06j5KRkcHhw4cZP368cZtWq6VVq1bs27cPgClTphhPWy1YsIBTp049NOTc3X/SpEn5W7gQ4onFxMTQpk0bDh8+jJOTE+vXr6devXpqlyVMUEJaJuuO32LF4TCOXIszbi9hY07nGt70rFOKyl4yv5GpKNRBJzo6Gr1ej4eHR47tHh4enDt37qluc/z48YwZM8b4+90WHSGEesLDw3n++ec5deoUrq6ubNq0iRo1aqhdljAhBoPC3ssxrDgcxvrT4aRlZp+a0mk1NCvvRs86pWhR0QMLMzk1ZWoKddB5UgMHDnzsPnenZBdCFA7Xr1+nZcuWXLhwgZIlS7JlyxYCAwPVLksUMYqikJSeRWRiOlGJ6fd8TyMqIZ39ITHcjE8z7h/gbkfPOqXoUtMbd3tZgsGUFeqg4+rqik6nIyIiIsf2iIgIPD09VapKCJFXQkJCaNmyJVeuXMHX15ctW7bg7++vdlmiENEbFGKS/g0uxvCSI8xkf0/N1D/ythyszOhcw5setUtRrZSjLJ5ZTBTqoGNhYUHt2rXZsmWLsYOywWBgy5YtjBgx4pluOzg4mODgYPT6R78whBD549y5c7Rq1YobN27g7+/Pli1bZFHLYiosNoVt5yOJSEgjMiFngIlNTsfwBENm7C3NcLO3NH6521vh7mBJGRdbmlVww8pc1pgqblQPOklJSVy6dMn4e2hoKMeOHcPZ2RlfX1/GjBnDgAEDqFOnDkFBQcycOZPk5ORnnh11+PDhDB8+3NhrWwhRcE6cOMHzzz9PZGQklSpVYvPmzZQsWVLtskQBysgysPlsBEsOXGPXxehH7qvVgIudJe7G8JIdYO7+7HbP77JYpvgv1YPOoUOHaN68ufH3ux2FBwwYwIIFC+jduzdRUVF89NFHhIeHU6NGDdavX39fB2UhRNFw8OBB2rRpw+3bt6lZsyYbN27E1dVV7bJEAbkSncySg9dYefg60UkZAGg00KCcCwHudrg7WN3TGpP93cXWEp0siCmeUqGaR0cNuR2HL4R4drt376Z9+/YkJiZSv359/v77b5ycnNQuS+Sz9Cw9G05HsPTANfZejjFud7e3pFcdH3rX9cHHuWAXYxVFn0nMoyOEyFsZGRmEhIRQvnx5tNqCHUa7efNmOnfuTEpKCs2aNWPNmjXY29sXaA2iYF2OSmLpgWusOHyd2ymZQHbrTbPybrwY5EuLiu6YyUzDIp8V26AjnZFFcZOVlUW7du3YunUrLi4uPP/887Rp04bWrVvj5eWVr/e9bt06evToQXp6Om3btuX333/H2to6X+9TqCMtU8/6U+EsPnCNA6Gxxu0lHa3oVceHXnV98HaS514UHDl1JaeuRDHxzjvvMH369AdeVrVqVdq0aUObNm1o1KgRVlZ5N6/I8uXL6du3L1lZWXTt2pUlS5bIXFYm6EJEIksOXOP3IzeIT81uvdFqoEVFD14M8qFpeTdpvRF5Kref3xJ0JOiIYmD58uX06tULyF4o18vLiw0bNrBhwwYOHTrEvW8D1tbWNGvWzBh8KlSo8NTzjSxcuJBBgwZhMBh48cUX+fnnnzE3N8+TYxLqS83Q8+fJWyw5cI3DV28bt3s7WdOnrg896/jg6SiT8Yn8IUEnlyToCFN39uxZ6tatS3JyMmPHjuWLL77IcXl0dDSbN282Bp9bt27luNzX19cYelq2bJnrzsPfffcdr7/+OgCvvvoq3333HTqdDP01BWdvJbDkwDX+OHqDxLQsIHsphVaB7rwY5EvjADcZJSXynQSdXJKgI0xZQkICQUFBnD9/nubNm7Nx40bMzB7eNU9RFE6dOmUMPbt27SI9/d8VnXU6HfXq1TMGnzp16jwwvMyYMcM4VcTIkSOZOXNmgXd+FnkrOT2LdSdusuRAGMfC4ozbfZyt6VPXl561S+HuIK03ouBI0HmMezsjX7hwQYKOMDmKotCzZ09WrlyJt7c3R44cwd3d/YluIyUlhR07dhiDz38X03V2dqZVq1bGTs3e3t5MnjyZDz/8EIBx48bx2WefyVT7RVSW3sDeyzGsOnaDDafCSc7IHrxhrtPQupInLwb50tDPBa203ggVSNDJJWnREaZq2rRpvPvuu5ibm7Nz507q16//zLd57do1Y+jZvHkz8fHxOS4vW7YsoaGhAHzyySd88MEHEnKKGEVROBoWx5pjN1l34qZxUj+AMi42vBjkS/fapXC1kw7lQl0SdHJJgo4wRdu2baNVq1YYDAbmzJnDG2+8kef3kZWVxYEDB4zB5+DBgxgMBgCmT59uPHUlioZLkYmsPnaT1cduci02xbjd2daCF6qWpEtNL2r5lpDgKgoNCTq5JEFHmJrr169Tq1YtoqKi6N+/PwsWLCiQD6fY2Fi2bt2Kk5MTrVq1yvf7E8/uVnwqa49nh5vTNxOM220sdLSu5EHnmt408nfFXIaFi0JIgk4uSdARpiQjI4OmTZuyf/9+qlevzt69e7Gxkan1xb/iUjL4+1Q4q4/d4J/QWO5+AphpNTQt70bnmt60CnTHxqLYzicrighZAkKIYmjMmDHs378fJycnVq5cKSFHANnz3Ww5F8GqozfZcSGSTP2//98GlXGmUw0vXqhakhK2FipWKUT+kKAjhIn45ZdfCA4OBmDRokX4+fmpXJFQU5bewO5L0aw5dpMNp/8dMQVQ0dOeLjW96VjdS5ZjECav2AYdWetKmJLjx48zdOhQAD766CNeeOEFlSsSarg7Ymr10RusO3GLmOR/R0x5O1nTuYYXnWt4U8FTFlMVxYf00ZE+OqKIu337NnXq1CEkJIS2bduybt06mYG4mIlPyWTB3iusOBJGWGyqcbuzrQUdqpWkcw0ZMSVMj/TREaIYMBgM9O/fn5CQEMqUKcOvv/4qIacYiUvJ4Kfdoczfc4XE9OylGGTElBA5SdARogj77LPPWLduHZaWlqxcuRJnZ2e1SxIF4EEBp6KnPa839aN1ZQ8ZMSXEPeTVIEQRtWHDBj766CMA5s6dS61atVSuSOS3uJQMfrwTcJLuCThvtgygTWVPWYpBiAeQoCNEEXTlyhX69u2LoigMGTKEQYMGqV2SyEcPCzijWwXQupIEHCEeRYKOEEVMWloa3bt3JzY2lrp16/LNN9+oXZLIJ3EpGczbFcqCvRJwhHhaxTboyPByUVSNGDGCI0eO4OLiwooVK7C0lMUVTc3DA055WlfykIAjxBOQ4eUyvFwUIfPmzWPw4MFotVo2bNgga0qZmNvJ2aeo7g04gSUdeLNlgAQcIf5DhpcLYWIOHjzI8OHDAfj0008l5JiQ28kZzNsdwoI9V4wzGAeWdGB0qwCeD5SAI8SzkKAjRBEQHR1Njx49yMjIoHPnzrz33ntqlyTywIMCTqWSDrwpAUeIPCNBR4hCTq/X07dvX65du4a/vz8///wzWq1MAleUxSZnMG9XCD/vzRlwRrcK4PlKHjKDsRB5SIKOEIXchAkT2LRpEzY2Nvz+++84OjqqXZJ4ShJwhCh4EnSEKMTWrFnD5MmTgeyOyFWrVlW5IvGkktKz2HMpmu3nI1lz7KYEHCEKmAQdIQqpixcv0q9fPwBGjRrFiy++qHJFIjcUReFiZBLbzkWy/XwUh67Gkqn/d3BrZS8HRrcqT6tAdwk4QhQACTpCFELJycl0796dhIQEnnvuOaZOnap2SeIRku+02mw7H8WO85HcjE/LcXkZFxuaVXCnVaAHz/m7SMARogAV26AjEwaKwurusg4nT57Ew8OD3377DQsLC7XLEvdQFIVLkUlsPx/FtvORHLySs9XG0kxL/XIuNKvgRrMK7pR1tVWxWiGKN5kwUCYMFIXM7NmzGTlyJDqdjq1bt9KkSRO1SxJkt9rsvRzD9vPZp6RuxKXmuNzX2YbmFdxoVtGd+mVdsLbQqVSpEMWDTBgoRBFz+fJlFixYwOeffw7AtGnTJOSoSFEULkdlt9psPx/FgdBYMvQG4+UWd1ttyrvRrIIbZV1t5ZSUEIWQBB0hVJSUlMSKFSuYP38+O3fuNG7v06cPb775poqVFU8pGVnsvRTD9gvZrTbXb+dstfFxtqZ5BXeaVXCjQTlXabURogiQoCNEAVMUhd27dzN//nx+++03kpOTAdBoNLRu3ZpBgwbRo0cPaR0oAAaDwtnwBHZdjGbXxSgOht7O2Wqj01KvnDPN7oSbctJqI0SRI0FHiAISFhbGwoULWbBgAZcuXTJu9/f3Z9CgQfTv359SpUqpWGHxEJmQZgw2uy9FE52UkePyUiWsaVbBjeYV3Gng54KNhbxNClGUyStYiHyUlpbGqlWrmD9/Pps2beJu3387Ozt69erFoEGDeO6556SVIB+lZeo5EBrLrotR7LoYzbnwxByX21joaFDOhcYBrjQKcMPPTVpthDAlEnSEyGOKonDo0CHmz5/PkiVLiIuLM17WtGlT46kpW1sZcpwfFEXhfEQiuy5Es/Nidifi9Kx/T0dpNFDV25HGAa40DnCjlm8JLMxk7TAhTJUEHSHySEREBIsWLWL+/PmcPn3auN3X15cBAwYwYMAA/Pz8VKzQdEUlprPnUnaw2XUxmqjE9ByXezpY0TjAlSbl3XjO3xVnW5mXSIjiQoKOEM8gMzOTP//8k/nz5/PXX3+RlZUFgJWVFd26dWPQoEG0aNFCVhvPY2mZeg5fvZ0dbC5Ec+ZWQo7Lrcyzh343DnCjSYAr/u52cjpKiGJKgo4QT+HkyZPMnz+fRYsWERUVZdxer149Bg0aRO/evXFyclKvQBOUlqnnt0NhbDkbyT+hMaRlGnJcXtnLwRhsapcpgaWZDP0WQkjQEeKJhISE0K9fP/bu3Wvc5unpSb9+/Rg4cCCVKlVSsTrTte18JBPXnOZqTIpxm7u9ZXawKe/Kc/6uuNpZqlihEKKwKrZBR9a6Ek9q27Zt9OjRg9jYWMzNzenYsSODBg2ibdu2mJkV25dSvroRl8rHa0+z4XQEkB1uXmtclqbl3SnvIaejhBCPJ2tdyVpXIhfmzJnDqFGj0Ov11K1bl5UrV+Lj46N2WSYrI8vAvN0hzNpyidRMPTqthkENy/BmqwDsrczVLk8IUQjIWldC5IHMzExGjRrFt99+C0Dfvn2ZN28e1tbWKldmuvZciubD1acIicqeMTqojDMfd6lMRU/5R0QI8eQk6AjxENHR0fTo0YMdO3ag0WiYMmUKY8eOldMl+SQ8Po1P/jzDnyduAeBqZ8H77QPpWtNbHnMhxFOToCPEA5w6dYpOnToRGhqKnZ0dixcvpmPHjmqXZZIy9QYW7LnCzM0XSM7Qo9VA/wZleOv58jhay2kqIcSzkaAjxH+sXr2al19+maSkJMqVK8eaNWuoXLmy2mWZpP0hMXy0+hQXIpIAqOnrxCedq1DF21HlyoQQpkKCjhB3KIrClClT+N///oeiKDRv3pzly5fj4uKidmkmJzIxjSl/neOPozcAcLa1YFzbivSoXQqtVk5TCSHyjgQdIYDU1FReffVVlixZAsCwYcOYOXMm5uZy6iQvZekN/LL/Kl9tvEBiehYaDfQN8uXdNhVwspFlGYQQeU+Cjij2bty4QefOnTl8+DBmZmbMmjWL119/Xe2yTM7hq7H8b9Vpzt5ZrqFaKUc+6VyF6j5O6hYmhDBpEnREsfbPP//QpUsXwsPDcXFxYcWKFTRr1kztskxKTFI6n/99juWHrwPgaG3O2LYV6FPXF52cphJC5DMJOqLY+uWXXxg8eDDp6elUqVKF1atXU65cObXLMhl6g8LiA9eYuv4cCWnZi532qlOK99pWxEWWaxBCFBAJOqLY0ev1jB8/nqlTpwLQuXNnfvnlF+zt7VWuzHQcD4vjw9WnOHE9HoBKJR34pEsVapcuoXJlQojiRoKOKFbi4+Pp27cvf/31FwAffPABH3/8MVqtVuXKTMPt5Ay+3HCepQevoShgb2XGO60r8FI9X8x08hgLIQqeBB1RbFy6dIlOnTpx9uxZrKysmD9/Pn369FG7rCJPURQOXb3NsoNh/HniFqmZ2Qvldqvpzfj2gbjZy2kqIYR6JOiIYmHLli307NmT27dv4+3tzapVq6hTp47aZRVpkYlp/H7kBr8dCjOuSwUQWNKBiR0rUa+czD8khFCfBB1h0hRFYfbs2bz11lvo9Xrq1avHH3/8QcmSJdUurUjK0hvYcSGKZQfD2HIuEr1BAcDGQscLVUvSJ8iHWr4lZG0qIUShUWyDTnBwMMHBwej1erVLEfkkIyODESNG8MMPPwDQr18/vv/+e6ysrFSurOi5Ep3Mb4fCWHnkOhEJ6cbtNX2d6F3Hhw7VvbCzLLZvJ0KIQkyjKIqidhFqSkhIwNHRkfj4eBwcHNQuR+SRqKgounfvzq5du9BoNHzxxRe888470tLwBNIy9fx96hbLDoaxPyTWuN3Z1oJuNb3pVdeH8h4yUk0IoY7cfn7Lv2DC5Jw4cYJOnTpx9epVHBwcWLJkCe3bt1e7rCJBURRO3Uhg2aFrrD52k8Q7899oNNAkwI3edX1oFeiBhZmMoBJCFA1PHHTOnj3L0qVL2bVrF1evXiUlJQU3Nzdq1qxJmzZt6N69O5aWMspCqGPZsmW88sorpKSk4Ofnx9q1awkMDFS7rEIvLiWDVUdvsOzQdeMSDQClSljTq44PPWqXwsvJWsUKhRDi6eT61NWRI0cYO3Ysu3fv5rnnniMoKAgvLy+sra2JjY3l1KlT7Nq1i4SEBMaOHcvo0aOLROCRU1emISsri/HjxzNt2jQAWrVqxbJly3B2dla5ssLLYFDYFxLDsoNhrD8dTkaWAQALnZY2VTzpXceHhn4uspq4EKJQyvNTV927d+fdd99lxYoVODk5PXS/ffv28fXXXzN9+nTef//9JypaiKcRHR1Nnz592LJlCwDvvfcekydPRqfTqVxZ4XQzLpUVh6+z/HAYYbGpxu2BJR3oXacUXWp6y0riQgiTkesWnczMTMzNzXN9w0+6v1qkRadoO3LkCN26dePq1avY2try008/0atXL7XLKpSy9AY+/fMsC/dd4c6ocOwtzehUw4s+dX2p4u0gnbWFEEVGnrfoPC60xMXF5WjpKQohRxRtCxcuZOjQoaSlpeHv788ff/xBlSpV1C6rUEpMy2TE4qPsuBAFQL2yzvSu60O7KiWxtpCWLyGE6XqqoRNffPEFy5YtM/7eq1cvXFxc8Pb25vjx43lWnBAPkpmZyahRoxgwYABpaWm0b9+egwcPSsh5iBtxqfT8dh87LkRhba7ju361WTa0Ad1qlZKQI4QweU8VdL799lt8fHwA2LRpE5s2beLvv/+mXbt2vPvuu3laoBD3Cg8Pp2XLlsyaNQuAjz76iLVr1z6y31hxdvJ6PF2C93AuPBE3e0uWDa1Pm8qeapclhBAF5qnm0QkPDzcGnXXr1tGrVy9at25NmTJlqFevXp4WKMRd+/fvp3v37ty8eRN7e3sWLVpEp06d1C6r0Np4Opw3lx4jNVNPBQ97fhpUF28ZIi6EKGaeqkWnRIkShIWFAbB+/XpatWoFZE82JksqiPzwww8/0LRpU27evEnFihU5ePCghJyHUBSFebtCGLroMKmZepqUd2PFGw0k5AghiqWnatHp1q0bffv2JSAggJiYGNq1awfA0aNH8ff3z9MCRfGWnp7OyJEjjetVdevWjQULFmBvL0sPPEiW3sCktWf4Zf9VAPrW8+XjTpUx08lMxkKI4umpgs6MGTMoU6YMYWFhfPnll9jZ2QFw69Ythg0blqcFiuLrxo0bdO/enX/++QeNRsPkyZMZN26cDIF+iKT0LEYsPsL281FoNPB+u0Bea1xWHi8hRLEmi3rKPDqF0q5du+jZsycRERGUKFGCxYsX07ZtW7XLKrRuxqXyyoKDnAtPxMpcy8zeNWlbRTodCyFMV74u6rlw4cJHXt6/f/+nuVkhUBSF4OBg3nrrLbKysqhWrRp//PEH5cqVU7u0QuvUjXheWXCQyMR0XO0s+XFAHar7OKldlhBCFApP1aJTokSJHL9nZmaSkpKChYUFNjY2xMbG5lmB+U1adAqP1NRUXn/9dWOQ7tOnD/PmzcPW1lblygqvzWciGLnkKKmZesp72PHTwLqUKmGjdllCCJHv8rVF5/bt2/dtu3jxIm+88YbMoyOeytWrV+nWrRtHjhxBp9Px5Zdf8tZbb0n/kodQFIX5e67wyZ9nUBRoHOBK8Eu1cLCSGcmFEOJeTxV0HiQgIIDPP/+cl19+mXPnzuXVzYpiYOvWrfTq1YuYmBhcXV357bffaN68udplFVpZegOfrDvDz/uyR1a9GOTLx50rYy4jq4QQ4j55FnQAzMzMuHnzZl7epDBhiqIwffp03nvvPQwGA7Vr1+b333/H19dX7dIKraT0LEYuPsK289lrVr3fviKDG5eTli8hhHiIpwo6a9asyfG7oijcunWL2bNn89xzz+VJYcK0JScn8+qrrxrXTBs4cCBz5szB2lomtXuYW/GpvLLgEGdvJWBppmVm7xq0q1pS7bKEEKJQe6qg06VLlxy/azQa3NzcaNGiBdOnT8+LuoQJCwkJoUuXLpw8eRIzMzO+/vpr3njjDWmVeIRTN+J59eeDRCSk42pnwbwBdakhI6uEEOKxniroGAyGvK7jqcXFxdGqVSuysrLIysrizTffZPDgwWqXJR7ixIkTtG7dmoiICDw9PVm+fDmNGjVSu6xCbcvZ7JFVKRl6AtyzR1b5OMvIKiGEyI087aOjBnt7e3bu3ImNjQ3JyclUqVKFbt264eLionZp4j/27NnDCy+8QHx8PNWrV+fPP//E29tb7bIKtfl7Qvlk3RkMCjTyzx5Z5WgtI6uEECK3cj1M4/PPPyc1NTVX+/7zzz/8+eefT13Uk9DpdNjYZP93m56ejqIoFPPJngul9evX8/zzzxMfH89zzz3H9u3bJeQ8gt6gMHHNaSatzQ45fer6MH9QXQk5QgjxhHIddM6cOYOvry/Dhg3j77//JioqynhZVlYWJ06cYM6cOTRs2JDevXvnetHFnTt30rFjR7y8vNBoNKxateq+fYKDgylTpgxWVlbUq1ePAwcO5Lg8Li6O6tWrU6pUKd59911cXV1ze1iiACxdupSOHTuSmppKu3bt2LhxI05OTmqXVWglp2cxZOEhFuy9AsC4dhWZ0q2qDB8XQoinkOt3zoULF7J582YyMzPp27cvnp6eWFhYYG9vj6WlJTVr1uSnn36if//+nDt3jiZNmuTqdpOTk6levTrBwcEPvHzZsmWMGTOGCRMmcOTIEapXr06bNm2IjIw07uPk5MTx48cJDQ1l8eLFRERE5PawRD779ttv6du3L1lZWbz44ousWrXK2AIn7ncpMpFe3+1jy7lILM20zHmpFq839ZOO2kII8ZSeagkIg8HAiRMnuHr1Kqmpqbi6ulKjRo1nbknRaDT88ccfOUZ11atXj7p16zJ79mzjffv4+DBy5EjGjRt3320MGzaMFi1a0KNHjwfeR3p6Ounp6cbfExIS8PHxkSUg8piiKEyZMoUPPvgAyH5eZs2ahVYrrRIPcjMulRmbLrDyyHUMCrjaWfB9/zrU8i3x+CsLIUQxlK9LQGi1WmrUqEGNGjWetr5cycjI4PDhw4wfPz7Hfbdq1Yp9+/YBEBERgY2NDfb29sTHx7Nz507eeOONh97mlClTmDRpUr7WXdwpisI777zDV199BcD//vc/Pv74Y2mVeIDbyRnM2X6Jn/ddJSMrezRj60oefNihkoysEkKIPFCoR11FR0ej1+vx8PDIsd3Dw8O4zMTVq1cZMmSIsRPyyJEjqVq16kNvc/z48YwZM8b4+90WHZE3srKyGDJkCPPnzwdgxowZjB49Wt2iCqGUjCzm77nCt9svk5ieBUC9ss68166itOIIIUQeKtRBJzeCgoI4duxYrve3tLTE0tIy/woqxtLS0oz9cHQ6HT/++CMDBgxQu6xCJVNvYOnBML7ZcpGoxOxTqIElHRjbtgLNyrtJq5cQQuSxQh10XF1d0el093UuvjvZnCg8EhMT6dKlC1u3bsXS0pJly5bRuXNntcsqNAwGhT9P3mL6xvNciUkBwMfZmndaV6BjNS+0Wgk4QgiRHwp10LGwsKB27dps2bLF2EHZYDCwZcsWRowYoW5xwig6Opp27dpx6NAh7OzsWLNmjaw+fo9dF6P4Yv05Tt1IALI7Go9sEcCLQb5YmEnnbCGEyE/PFHQuXbrE5cuXadKkCdbW1iiK8sRN70lJSVy6dMn4e2hoKMeOHcPZ2RlfX1/GjBnDgAEDqFOnDkFBQcycOZPk5GQGDRr0LKUTHBxMcHAwer3+mW6nuAsLC6N169acO3cOFxcX1q9fT506ddQuq1A4HhbHlxvOsedSDAB2lmYMblyO1xqXxdayUP+PIYQQJuOphpfHxMTQu3dvtm7dikaj4eLFi5QrV45XXnmFEiVKPNHCntu3b3/gf/8DBgxgwYIFAMyePZupU6cSHh5OjRo1+Oabb6hXr96Tlv1AuR2eJu534cIFnn/+ea5du0apUqXYuHEjgYGBapelupCoJKZtPM9fJ8MBsNBpebl+aYY398PFTvqHCSFEXsjt5/dTBZ3+/fsTGRnJvHnzCAwM5Pjx45QrV44NGzYwZswYTp8+/UzFFyQJOk/nyJEjtG3blqioKMqXL8+mTZvw9fVVuyxVRSSkMXPzRX47FIbeoKDRQNea3rzVqrwMFRdCiDyWr/PobNy4kQ0bNlCqVKkc2wMCArh69erT3KQoQu4u25GQkEDNmjVZv3497u7uapelmviUTObuuMyCvaGkZWbPhdMq0J132lSgoqeEZyGEUNNTBZ3k5OQHTuMfGxsrQ7dN3Lp16+jZsydpaWk0adKENWvW4OjoqHZZqkjL1LNg7xXmbr9MfGomAHVKl+C9dhWpW8ZZ5eqEEELAUwadxo0bs3DhQj755BMge+kGg8HAl19+WWRG20hn5Cf366+/MmDAAPR6PR06dOC3337D2tpa7bIKXJbewIrD15m5+SLhCWkAVPCwZ2zbCrSo6C5z4QghxD2yDFmYadUbgPFUfXROnTpFy5YtqVWrFlu3bqVTp06cPn2a2NhY9uzZg5+fX37Umi+kj07uzJo1i1GjRgHw8ssv89NPP2Fubq5yVQXv+u0UXllwkAsRSQB4O1kz5vnydKnpjU7mwhFCCMhKR3/zGP+c/51V4Xs5kRnH2pf3Y25hm6d3k699dKpUqcKFCxeYPXs29vb2JCUl0a1bN4YPH07JkiWfumhR+CiKwscff8zEiRMBGDVqFDNmzCiWi3Nev51Cn+/3c/12Ks62Fgxv7s/L9X2xNNOpXZoQQqgnORrC/oGwf7h6bQ+rk0NYY2tFhNmdiKGDg2dX0LC6OjPlP1WLjimRFp2HMxgMvPXWW3zzzTcATJo0iQ8//LBYnpq5EZdKn+/3ERabShkXG5YOaYCno5XaZQkhRMEyGCD6AoTth7ADcG0/ybdD2Ghrwyp7W45Y/fu+aI+W9g4V6OrflUoVu6Ixz9v3zHxt0YHsdY1OnDhBZGQkBoMhx2WdOnV62psVhURmZiavvPIKixYtArJPXRXX2ajvDTmlXWxYMqS+hBwhRPGQkQw3jvwbbMIOQFocBuCwlSWr7GzZ5OtN6p1Wfi0aGrjVpEvgizT3bY6lTv0BSk8VdNavX0///v2Jjo6+7zKNRlMkOvhKZ+SHS0tLo1evXqxduxadTsfPP//MSy+9pHZZqrgZl8qL3+83hpylQ+pT0rH4dcAWoigyKAYikiMIjT1HXOINbC0csLV0ws7KCVsrJ2wt7LAzt8NCZ6F2qYVH/A3jaSjC/oFbJ0D593PyhpmONc4urHZw5IYmy7i9jEMZOvt3pmO5jnjYeqhR+UM91amrgIAAWrduzUcffYSHR+E6oCclp65yUhSFvn37snTpUqysrFi+fDkdOnRQuyxV3IpPpc/3+7kak4Kvc3bI8XKSkCNEYZOhz+BawjVCbl8kNOIoIdGnCU0M40pmPKkYHnt9c0XBVgFbRYMdGmzRYqvRYacxw1Zjhq3WHFutOXZaS+x0ltiYWWGns8bWzBo7MxtsLWywMrPB3MwaczMrzMys0Zlbgc4CdJagMwczy//8bJH9ZXZnm+7Otift/6goYMgCfSboM7K/G+7+nJX93ZCZ8/Ic+2Rm97G5fqe1Jj7svrtItfdis5c/q80y+SflhnG7rbktbcu0pYt/F6q7VS/wbg35euoqIiKCMWPGFPmQI+738ccfs3TpUszMzFi3bh0tW7ZUuyRVSMgRovBJyEggJC6E0PhQQqNOEhp9htCkMK5nJvCwtnkzRcE3MwtXg4EUDSRptCRrNSRptcbTLZkaDXEaiANAAfR3vjKyb8Rw5+sJ6BQFc0XBXAEz/v3ZXFHu/M6dbQrm9/6OBnO0mGs0mGt0d740uOgVPPUGPDMz8czMwC0zA93dEJOXNDrwrIJSKohjzl6sTr/J+pu7Sc64AhmgQUNQySC6+HehpW9LrM0K//viUwWdHj16sH379iI1jFw83rJly4yjq+bOnVtsQ054fBov3gk5Ps7WLJGQI0SBURSF8ORwQuNDCYkPITT2HKExZwlJvE6MPuWh17MzGCibkUlZvUJZS2fKOZShrHs1SnnVw9yzKtjdmb1dnwFZ6aDPQJ+ZTEpaPMnp8SSlx5OckUhyegJJmYkkZySRnJlMUlYKyVkpJGWlkZyVRrI+jSRDOin6DJKUTJINmSQpWWSS8+SIXqNBr9GQljePyp3vWsASsESnKLjp9ZTMysIzS49nlh4PffbPJQ3gadBQQmOORmd+p8XIHLTmd1qS7m6zAK0ZWNqDV03wqUd4CR/WXd/K6kuruXJhg7GCUnal6OzfmU5+nfCy88qToyooT3XqKiUlhZ49e+Lm5kbVqlXvm0/l7nwrRYGcusp24MABmjZtSlpaGmPGjHmihVlNSXh8Gn2+38eVOyFn6ZAGeEvIESLfKIrCvpv7WHtpFZdjznIl+Qapj2ilcM/KomxmFuUysyhrUYJyjmUo61YNt5K10XhWBqcyT376Jw8YFANZhiwyDZlk6jOzv9/9+u/v927TZ5CZmUpmVhqZ+lSystLv/Hz3ewbpWWlEZcQTnnGb8LTbRKbHolce38RkqbPEw8YDT1vPnF82npS0LYmnrSd2Fnak69PZdm0bqy6tYt+tfRju3La1mTWtS7emi38XannUQqspXNOK5Ouinj/++COvv/46VlZWuLi45Dgvp9FoCAkJebqqVSBBB8LCwggKCiI8PJwOHTqwatUqdLriNzdMeHwaL/6wn9DoZEqVsGbpkPqUKiGLcQqRX/4J2UDwoa84mnozx3YzRcEnM4tymZmUzcykrM6eck5lKeNWDTvP6uBRCVzLZ/dvKYb0Bj3RqdGEp4RzK/kWEckRhCeHG79uJd8iJi0mV7dlZ26HgkJyZrJxW22P2nTx70Lr0q2xMS+874H5GnQ8PT0ZNWoU48aNK7ITx9076urChQvFNugkJSXRuHFjjh07RtWqVdmzZw/29vZql1XgIhLS6PN9dsjxdsoOObLiuBD5IOEWh458R/CVdRzSpANgYVDonphEPb2Osk7l8HGrirlnFXCvBO6BYFU819N7Fhn6DCJS/g1AESkR3Eq6RXjKv4EoISPBuH9J25J08utEZ7/O+Dj4qFh57uVr0HF2dubgwYMm0UenOLfoGAwGunfvzqpVq3B3d+fAgQOULl1a7bIKXERCdp+cEAk5QuSP+Otwdi1Hz/xGcMZ1/rHOnofKXFHoYbDmVb+ueFTpDc7loBhOSKqWlMwUwlPCSctKo6JzxUJ3aupx8nXU1YABA1i2bBnvv//+Uxco1Pf++++zatUqLC0tWbVqVbEMOZEScoTIH7evwtk1cGY1J6KOM8fJkT021mBthZkC3ZwqMbjeWDxL1la70mLLxtyGco7l1C4j3z1V0NHr9Xz55Zds2LCBatWq3dcZ+auvvsqT4kT+WbBgAV988QWQ3eeqQYMGKldU8CIT0ujzg4QcIfJMbAicWZ39dfMopy0sCC7hyC4vTwDM0NK5dBuG1Bld5EbuiKLrqYLOyZMnqVmzJpC9kvm9iuM6SEXNrl27GDJkCAD/+9//iuWsx5GJ2R2PQ6KS8XK0YslgCTlCPJXoS3BmVXa4CT8BwDkLc4I93Nhukz1iUafR0tGvE0OqDcHHvmj0/xCm46mCzrZt2/K6DlFAQkJC6Nq1K5mZmfTo0YNJkyapXVKBi0pM58Xv93P5TshZOqQBvi4ScoTItchz/7bcRJ42br5gYclc77JsJnu+G61GywtlX2Bo9aGUdih+p8ZF4fDUi3qKoic+Pp4OHToQExNDnTp1+Pnnn4vsqLmnFZWYzos/ZIecko5WLBlSX0KOELkReRZO/Z4dbqLP/7tda8blsg2Ya2/FhvjzQAoaNLQr247Xq79OWceyqpUsBDxB0OnWrRsLFizAwcGBbt26PXLf33///ZkLE3krKyuL3r17c/bsWby9vVm9ejU2NsXrAz4qMZ2+P+znUmQSng7Zp6tKu9iqXZYQhZuiwI4vYftn/27TWYBfC0LLNWJuagjrr21Bic8ewNumTBveqP4Gfk5Ff1SuMA25DjqOjo7G/jeOjkV/ToPitnr5mDFj2LBhAzY2NqxZswYvr+LVETA6KTvkXLwTcpYOqU8ZVwk5QjxSVjqsGQUnlmb/HtAGqnTnmlcVvj33K39emGecRbeVbyter/46FZwrqFiwEPd7onl0Pv74Y9555x2TagkoDvPozJ07l2HDhgGwcuXKx7bImZq7IedCRBIeDpYsHdKAshJyhHi0lFhY1g+u7s5e6PGF6YRVaMX3J75n7eW16JXsfxKb+TRjWPVhBLoEqlywKG7yZcJAnU7HrVu3cHd3z5MiCwNTDzqbNm2iXbt26PV6PvvsM8aPH692SQUqJimdvj/8w/mIRAk5QuRWzGVY3AtiLoGFPXFd5/B13FFWXVxFlpIFQJNSTRhWfRiVXSurXKworvJlwsCnmERZqOjcuXP07NkTvV5P//79GTdunNolFah7Q467vSVLBteXkCPE41zbD0tehNRYcPRhb+sP+N/JmUSlRgHwnNdzDKsxjGpu1VQuVIjceeJRVzJPTtEQExNDhw4diI+P57nnnuP7778vVs9dTFI6L837N+QsHVKfcm52apclROF2cgWsegP0GaR51eDrSs1YdCi7E3JZx7JMbDCRWh61VC5SiCfzxEGnfPnyj/3AjI2NfeqCxLPLyMige/fuXL58mTJlyvDHH39gaVl8VvmNTc7gpXn/cC48ETd7S5ZIyBHi0RQFdk2DrZ8CcL58S8bZ6Ll0OXsEbZ8KfRhTZwzWZtZqVinEU3nioDNp0iSTGHVlqhRFYdiwYezYsQN7e3vWrVuHm5ub2mUVmAsRibyx6DCXo5KzQ87g+vhJyBHi4bIyYO2bcHwxBuDnam35JvkCWfFZuFi58PFzH9OkVBO1qxTiqT1x0OnTp49JdUY2NV999RU//vgjWq2WZcuWUbly8egoqCgKyw9d56M1p0jLNODhYMmvr9XH311CjhAPlXo7e2TVlV3cMrPggwp1OJh4BoDmPs2Z2HAizlbOKhcpxLN5oqBTnPp4FEVr167l3XffBbIDT7t27VSuqGAkpWfxwR8nWX3sJgCNA1yZ0bsGrnbF53SdEE8sNhR+7QkxF/nL0ZlP3VxJTLmOtZk144LG0dW/q7znC5NQbEddmdqEgSdOnKBv374oisLQoUMZNWqU2iUViNM34xmx+Cih0cnotBrebl2e15v4odXKG7SpOXN1O1/vmUB0VgpaQIsGrUaT/f2en3Vo0NzdrtHec5kWnUaDBg06jRbN3e139tFptFjoLGhYrh3PVXkZc52F2oecf679A0tfJCEtlk+9fPjbUgP6NKq5VmNK4yn4OviqXaEQeeaJ5tExRaYwj05ERARBQUFcu3aNli1b8vfff2Nubq52WflKURQW7b/KJ3+eJSPLQElHK2a9WJM6ZaSZ3dRk6jOYt+lNvg/fRVYBtTA4KxraOlWiU61hVPJpbFotG6dWwh9vcMAcPvAsSbjGgE6jY2i1oQyuNhgzrSyBKIqGfJkw0BQV9aCTlpZG8+bN2b9/P+XLl2f//v2UKFFC7bLyVXxqJuNWnuDvU+EAtKzozrSe1Slha8L/gRdTIdf/4f2tIzitpAHwvMGSHmXao6CgVwwoih69wYCiGDAo+jvbDOgV/Z3vCgrZvxsUg/Er+zIDipJ9Owayt0en32ZDRiSxOp2xhnJY0NGrMS/UHU1JpzIqPRJ5QFFg13Qytn7C7BJOLHByQAF87X2Z0niKzIsjihwJOrlUlIOOoii8/PLLLF68mBIlSvDPP/8QEBCgdln56lhYHCMWH+H67VTMdRrea1uRVxuVNa3/uAUGg55ft47l6+sbSNdosDcY+MCzBe1bz0Cjy98Wh6zkKPYe+IZ1V9azlRTStVoANIpCXXNnOgZ05fkag7G1KEId3bMyYN1bXDy9jPFuLpy3zP6noHtAd8bWHYuNueks6yOKDwk6uVSUg86nn37Khx9+iJmZGRs3bqR58+Zql5RvFEXhx92hfLH+HJl6BR9na2a9WIsaPk5qlyby2I2IE3y4YQgHlWQAntObM6nVLDx8nyvwWhJvHWPzwa9ZE3mQQ+b/hmkrBVrYl6Nj1Veo7/9C4T7dkxqH4beXWRxzjBnOTmRoNJSwLMHEhhNp4dtC7eqEeGoSdHKpqAad5cuX06tXLwC+//57Bg8erHJF+ed2cgbvLD/OlnORALSr4snn3avhaG3a/ZCKG0VR+GPH//gydDXJWg3WBgPvuDWkZ9tgNGYqn5Y0GLhx7g/+PP4Ta5NDuGL+b7BxRUd7t7p0qj2cCh411KvxQW5fIWJxTz7U3WafdfZkf428G/HJc5/gau2qcnFCPBsJOrlUFIPOjRs3CAgIIDU1lbfeeouvvvpK7ZLyzaErsYxccpRb8WlY6LR82CGQl+uXllNVJiY65gIT/3qFHYZ4AGrpdXzabBo+5VqpXNn9lLQETh3+lrUX/+BvQzxx9/TnCdDa0qlMG9rXGoa7rYeKVQJhB9n4+0tMsjcjQafDSmvB23XfpXeF3vL6ESZBgk4uFcWgM2bMGGbMmEHDhg3ZuXMnunveaE2FwaAwd8dlvtp0Ab1BoayrLbP71qSyl8zK/dQUBf56B078BhVfgFoDwLc+qPyht2HPZ3x6YTFxWg3misKoErXo1+47dBaFf7mBzJiL7P7na9be2MF2c4XMO4+lVoH61iXpWKkvLSr2KvA+MEknljBl94essc1+DAOd/Pm82XTKOZYr0DqEyE8SdHKpqAWdmJgYSpcuTXJyMuvXr6dNmzZql5TnopPSeWvZMXZdjAagcw0vJnetip1lIe4HURRsm8LJfV+xzdaaBqlp1EpLR+cSALX6Q42+YFuwpzLi464w+c+B/J0VA0CgXsvkRp8SUL5jgdaRJwwG4i9vZsPRuayNO8exe/5WbRQNrZwr07H6EOr6NEGnzcd/TBSFI5vH8f7VNdwwN0MDvFZpAG/UehNznZzqFaZFgk4uFbWgM3HiRCZNmkTNmjU5fPiwyTVB770czZtLjxGVmI6VuZaPO1WhZ51SJnecBe7or5z8ezSvlnQn9c4oIje9geeTkmmTnEKNTAPaiu2zW3nKNYc7++SXXftnMvHMPCJ1GnSKwmsOlRj6wk+YWxahkUwPk5HCteMLWXfmV9ZmRnP9nv489mhx0JhjrTXDSmuOtdYca60F1jorrM0ssdZZY21mjbW5zZ0ve6wtbLGxcMDa0h5rC0esrRywtnTC2tIeKzMrrM2sMdeak5mRwtw/evJj6lUMGg1eWms+axVM7ZJ1VXwwhMg/EnRyqSgFnaSkJHx9fbl9+za//fYbPXv2VLukPKM3KHyz5SLfbL2IokCAux3BL9WivIe92qUVfZe3EbKsDwM8XYjT6SjrWJbo1GgSMxKNu7hnZdE6OYU2ySlUt/JEU6sf1HgJHL3ztJSUhJtM/XMAKzKy50Aqo9fwWb0PqVrZdP6W76XEhXHs4CzWXtnAel0Wibr8CZBmCpijkHrnH4JODhUZ98JP2FvI60eYLgk6uVSUgs5XX33F22+/TUBAAGfPnjWZvjkRCWm8ufQo+0NiAehVpxSTOlXB2sI0jk9VEacJX9COl11tiTAzo4pLZea1+RELrQX7bu1jw5UNbL22laTMJONVSt4JPW2TU6ns0xRN7QFQvg0846mPw4e/44Pjs7ihy/4wftmmHG92WICVtWlPcAmAopBxbR9Xr24nNSOR1MwUUrNSSM1MJVWfRmpWGqn6dFIMGaQaMrO/lCxSFX32FwZSUUjVQKpGS6pWQ6pGg+E/LZ0OBgMfVRxAmwZjVTpQIQqOBJ3HuHetqwsXLhT6oJOenk65cuW4efMmP/zwA6+99praJeWJHReiGLPsGDHJGdhY6JjctQpda5ZSuyzTkHCT2z+2YoCdgVALc8o6lOHndgspYZUzWKTr09l7Yy/rr6xne9h2UrJSjJd5Z94JPYoVgZX7oKnVH1z8nqiM9JRoZq/pz89p11A0Gkoa4NNabxNUfWAeHGQxYzCAPh0yU1EyU8lITyAtLZ7UjHhS0hPwLFUfG0cftasUokBI0MmlotKi8+OPP/Laa6/h5eVFSEgIlpZFe2XuTL2BrzZdYO72ywAElnQguG9NyrmZQB+NwiAtgeT57XhNF8UpS0s8rd355YVf8bT1fPTVstLYc2MP66+sZ0fYNlL16cbLfDMzaZOcQhvHipSvNRhNYEcwt3rk7Z05sYgPDn7BpTvdVLpalWJsh5+xs3V/5kMUQhRvEnRyqSgEHb1eT2BgIBcvXmT69OmMGTNG7ZKeya34VEYsPsrhq7cB6Fe/NB+8EIiVuZyqyhP6TDJ+7cnwlFPst7bGycKen9sveuKhxalZqey8vpMNoevZdX0HaYZM42VlMjJpk26gTelWBASNAI9KOa6blRrHvHWD+C75IlkaDS4GmFj1DZrVGZYnhyiEEBJ0cqkoBJ27syA7Oztz9epV7OyKbqtHbHIGPebuJSQ6GXtLM77oUY32VUuqXZbpUBT0q0fw7s31bLK1wVpnyY9t5lPVreoz3WxKZgo7ru9gw8VV7Lq1nwwMxsv8MjJooy1Bm8DelKs9mJDLG/hg30RO3WnFed7Cgw9fWEAJBzklKYTIOxJ0cqmwBx1FUahduzZHjx5lwoQJTJw4Ue2Snlpqhp6X5u3nyLU4vJ2sWTK4Pr4usphgXlJ2fMknJ+aw3MEeM42OOa3m0sCrQZ7eR1JGEtuvbWXDmSXsuX2aTP59C/HPzCJMpyVdq8XeoPBBxQG0r/+OTA8ghMhzEnRyqbAHnQ0bNtC2bVtsbW25evUqLi4uapf0VPQGhTcWHWbjmQgcrMxY+UZDAmToeN46voxZ297l+xKOaIBpTafTukzrfL3LhIwEtl9cw4Yzi9mbHEbWnTzznFkJJrWbj4fzk3VcFkKI3Mrt57dMNVvITZkyBYAhQ4YU2ZCjKAqT1p5m45kILMy0zBtQV0JOXgvdyaKt7/K9c/YSGf+r/2G+hxwABwsHOlV+mU6VXyY+LZ6dJ+Zjb25L0xqvSSuOEKJQkKBTiO3bt48dO3Zgbm5epDsgf7sjhIX7rqLRwMzeNQgq66x2SaYl8ixrVw/iizshZ2SNEfSq0KvAy3C0cqRj0OgCv18hhHiU/J3nXTyTu605/fr1o1SpotmRc9XRG3yx/hwAH75QSToe57XEcHb+1pMPnbIXb3y5wosMrjZE5aKEEKLwkKBTSJ06dYq1a9ei0WgYO7ZoznK651I07644DsDgxmV5pVFZlSsyMelJHF3SjbdtFfQaDR18n+fdeuPklJEQQtxDgk4h9fnnnwPQvXt3KlSooHI1T+7MzQSG/nKYTL1Ch2olGd8uUO2STIs+i/PL+zLcLJ40rZYm7nX4uOkXaDXykhZCiHvJu2IhFBoaytKlSwEYP368ytU8uRtxqQxacICk9CzqlXVmeq/qaLXSypBnFIWwdcN5Pf0SiTotNR39mfb8HMy1z7YWlRBCmCIJOoXQtGnT0Ov1tG7dmlq1aqldzhOJT8lk4E8HiEhIp7yHHd/3r4Olmcx4nJeid0xhaNQOos10BFh7Mrv9z1ibWatdlhBCFEoSdAqZiIgIfvrpJ6DoteakZeoZ/MshLkYm4eFgyYJBQThaSytDXko4uojXL/xMmLk53uYOfNdhMQ4WhW/+JyGEKCwk6BQyM2fOJC0tjfr169O0aVO1y8k1g0Hh7eXHORAai72lGQsGBeHlJK0MeSktZDsjD3zCeUsLXLSW/NBhKW42bmqXJYQQhZoEnUIkPj6eOXPmANmtOUVp9Mxnf53lzxO3MNdp+K5fbQJLSitDXsqKPMe7m17niJUFdmj5tt3P+Dj4qF2WEEIUesU26AQHB1OpUiXq1q2rdilGc+bMISEhgUqVKtGhQwe1y8m1H3eHMm93KADTelanob+ryhWZFkNiOBNW9WS7lTmWCsxqOYeKrpXVLksIIYoEWeuqkKx1lZqaSpkyZYiMjGThwoX069dPtVqexJ8nbjFiyREUBca1q8jrTU17baOjkUdZdnohoKGqZ22quFahonNFLHWW+XJ/SnoS0xe14GezVHQKzGz4Kc3Kd86X+xJCiKJE1roqYn766SciIyMpXbo0ffr0UbucXPknJIa3lh1DUWBAg9IMbVJO7ZLyhaIo7Lm5hx8OfsWR+IvG7X9e2wSAGRoCrD2p4lqFqt4NqeJenXKO5dBpn3G0mUHPT8u78LNZKgAf1xglIUcIIZ6QBJ1CIDMzk6lTpwLw7rvvYm5e+EcqXYxIZPDCQ2ToDbSp7MFHHSsXqT5FuaE36Nl8dSM/HprB2ZRbAJgpCp2SkvHMyuKUpSWnLC2I1ek4m3qLs2G3WB6WHX6s0VLJ2pOqLpWo7NOYql718LL1yv1jpCis/ONlZuojAHjHryedagzOl+MUQghTJkGnEFi6dClXr17F3d2dV155Re1yHisiIY2B8w+SkJZF7dIl+LpPTXQmNCFgpj6TdRdW8tOxYK5kxAFgbTDQIymV/qVa4Nn+bXDwhsgzKLdOcOvWYU7GnOZ0ajgnzTScsbQgRQuHU29y+PpNuL4ZAGd0VLZyp6pzRaqUakSV0i0pYfPgFek3b3qHjxNPgkbDa56NGdDoo4I6fCGEMCnSR0flPjoGg4Fq1apx+vRpPvvss0I/d05iWiY9v93HufBEyrnZsvL1hpSwtVC7rDyRkpnC76d+ZsHp+UTos08XOej19E3Jom/5npRoMBLsPR9+AwYD3A5Ff+s4oTf2cTLqBKeTb3BSk8EFCwuyHtCa463oqGrpQhWn8lTxbkCgX1tOnlzEG+d+IlOjobtDRSZ0+c3kWsuEEOJZ5fbzW4KOykFnzZo1dO7cGQcHB65du4ajo2OB15BbGVkGBi04wJ5LMbjaWfLHsIb4ONuoXdYzi0+PZ+mR2fx6cSW3lUwA3LKy6J9hRs/qQ7GtMwgsbJ/+DlJvk37rGOev7eRk5DFOJ13jpCGFK+b3N6hqFQUtkKXR0MrCnWm9NqDTScOrEEL8l3RGLgIURWHKlCkADBs2rFCHHEVReG/lCfZcisHGQseCQXWLfMiJToli4f7P+S1sM8kYACiVmckgjTOdg97CsnI3eNYOxQDWJbAs15xq5ZpT7e42fRYJESc4E7qZU5FHOJVwlZP6RCJ1GgxAkMaWz3uslZAjhBDPSN5FVbRjxw7279+PpaUlb775ptrlPNLUDef54+gNdFoNc16qRRXvwhvKHud6/FUW7JnIH5GHyLhzRsg/I4PXbPxo0/QDzEo3hPw+VaQzw8GrFvW9alH/ns2R0ee5cvMgNSp2w8K8aAdJIYQoDCToqOhua84rr7yCp+cj+n6o7Jf9V5mz/TIAn3erSrMK7ipX9HQuRR7nx90f83fCBfQaQAPV0jMZ7FqXJk0+QusaoHaJuLtWwN21gtplCCGEyZCgo5LDhw+zceNGdDod7777rtrlPNTG0+FMWH0KgDHPl6dnnaK37MCJK1uZ98/nbEvLHiKOBhqm63nNtw11Gr2Pxk7WixJCCFMlQUcln3/+OQB9+vShbNmyKlfzYIev3mbkkqMYFHgxyIeRLfzVLinXFEXhn7PLmHdkNv/o4wHQKAqtsnS8WrEvleuNAnNZdFQIIUydBB0VnD9/npUrVwIwbtw4lat5sJCoJF77+SDpWQZaVHTnk85ViswQ5z1H5xF84ltOkg5kT/L3Ara8UmMY5ar1A22xXeJNCCGKHQk6Kvjyyy9RFIWOHTtSpUoVtcu5T3RSOgPmH+B2SibVSzkyu29NzHSFPxykpN5m6tp+rEi9CoClQaG7hQcD679HSb/WKlcnhBBCDRJ0Ctj169f55ZdfAArl5IAGg8Lbvx0nLDaV0i42/DiwLjYWhf/P5MS5Pxi/bwLXtNnTQr1k4cXg5l/g4llD3cKEEEKoqvB/gpmYr776iszMTJo2bUqDBg3ULuc+P+4OZceFKCzNtPzQvw6udvmzKndeycpK54e/h/BdzGH0Wg0eegOTq75Bvboj1C5NCCFEISBBpwDFxMTw/fffA4WzNefE9Ti+3HAOgI86VqK8h73KFT3a1bC9vL9lJCc0GaDR0E7ryAedfsGxROHs3C2EEKLgSdApQLNmzSI5OZmaNWvSunXh6jOSlJ7FqCVHydQrtKviSd8gX7VLeijFYGDFtnFMvfYXqVoN9gYDH5TuzAvNJ+f/RH9CCCGKFAk6BSQpKYlvvvkGyB5pVdhGMH206hRXYlLwdrLm827VCl19d8XEXGLiXwPYbkgArYa6BgsmPz+XkqWC1C5NCCFEISRBp4B8//333L59m4CAALp37652OTn8fuQ6vx+9gVYDX/epgaONudolPdCOf77mozM/EKvVYK4ovOlSl37tv0erK5z1CiGEUJ8EnQKQnp7O9OnTARg7diw6XR4sFJlHQqOT+XBV9szHo1uVp04ZZ5Urul9KSgxT177MirTroNXgr9fw+XOTqVChk9qlCSGEKOQk6BSAX375hZs3b+Ll5UW/fv3ULscoI8vAqCVHSc7QU6+sM8ObF76Zj0+eWc74/R9z9U427G9dllGdFmJp5aRqXUIIIYqGwj8L3GOEhYXRrFkzKlWqRLVq1Vi+fLnaJeWg1+v58ssvAXj77bextCw8w7WnbjjHyRvxONmYM7NPDXTawtMvJyszjbmrXqLfgUlc1YGHXmFeleG822uNhBwhhBC5VuRbdMzMzJg5cyY1atQgPDyc2rVr0759e2xtbdUuDYCVK1dy8eJFnJ2dGTJkiNrlGG0/H8kPu0IBmNqjOiUdC8+6T9eu7Wb8lpGc0GZlDxvXOfNB54U4OpVWuzQhhBBFTJEPOiVLlqRkyZIAeHp64urqSmxsbKEIOoqiMGXKFABGjhyJnZ2dyhVli0xM453lxwEY0KA0z1fyULmibIrBwMrNb/PljU13ho0rfFC2Gy80nSTDxoUQQjwV1U9d7dy5k44dO+Ll5YVGo2HVqlX37RMcHEyZMmWwsrKiXr16HDhw4IG3dfjwYfR6PT4+Pvlcde5s3LiRY8eOYWNjw8iRI9UuB/h3iYfopAwqetozvn2g2iUBEBN9nlGLnmPSrc2kajXUVSxZ2XYhLzT7WEKOEEKIp6Z60ElOTqZ69eoEBwc/8PJly5YxZswYJkyYwJEjR6hevTpt2rQhMjIyx36xsbH079/fOPNwYXC3NWfIkCG4uLioXE2273eFsOtiNFbmWmb3rYmVufojwHbsnUq3Nd3ZriRhrii849qAef32U7JkLbVLE0IIUcRpFEVR1C7iLo1Gwx9//EGXLl2M2+rVq0fdunWZPXs2AAaDAR8fH0aOHMm4ceOA7OHbzz//PIMHD37sqKb09HTS09ONvyckJODj40N8fDwODg55diz79u2jYcOGmJubExISQqlSpfLstp/WsbA4eszdS5ZB4fNuVemj8uzHKcmRTFvzMsszbgHgb9DyeePPqeDfTtW6hBBCFH4JCQk4Ojo+9vO7UPfRycjI4PDhwznWhdJqtbRq1Yp9+/YB2f1gBg4cSIsWLXI1dHvKlClMmjQp32q+934A+vXrVyhCTmJaJqOWHCXLoPBCtZL0rqvu6b2Tp5Yw/sBn/w4bt/VnVMefsbTMu7ApRF4yGAxkZGSoXYYQxYa5uXmezDtXqINOdHQ0er0eD4+cnWU9PDw4dy578ck9e/awbNkyqlWrZuzf88svv1C1atUH3ub48eMZM2aM8fe7LTp5KT4+niNHjqDRaBg7dmye3vbTUBSFD/44xbXY7CUePutaVbUlHuJjL/PD5tEsSglFr9PgoYfJNUdTr+arqtQjRG5kZGQQGhqKwWBQuxQhihUnJyc8PT2f6TOrUAed3GjUqNETvflYWlrm+1w2jo6OXL58mZ07d1KhQoV8va/cWHH4OmuO30Sn1fDNizVxtC74JRPSUmJYvHkM82IOkajVZg8bN3Plg64LcXQoHJ3HhXgQRVG4desWOp0OHx8ftFrVuzYKYfIURSElJcXYH/fu6OqnUaiDjqurKzqdjoiIiBzbIyIi8PT0VKmq3LG0tOT5559XuwwuRyUxYc1pAMY8X57apUsU6P3rM9NYu/0DZodtIEKnAa2WAIOOt6oOoXGdYQVaixBPIysri5SUFLy8vLCxsVG7HCGKDWvr7PndIiMjcXd3f+rTWIX6XxMLCwtq167Nli1bjNsMBgNbtmyhQYMGKlZWNKRn6Rm15CgpGXoa+rnwelO/ArtvxWBg596p9PwliA9vbiRCp8HTAJPLdGF5v4MSckSRodfrgez3IyFEwbr7z0VmZuZT34bqLTpJSUlcunTJ+HtoaCjHjh3D2dkZX19fxowZw4ABA6hTpw5BQUHMnDmT5ORkBg0a9Ez3GxwcTHBwsPFNzBR98fd5Tt9MwNnWghm9C26Jh1MnF/PVoWkc1GaCDhwMCoM9nuPFltOwtLQvkBqEyGtq9WsTojjLi9ed6kHn0KFDNG/e3Pj73Y7CAwYMYMGCBfTu3ZuoqCg++ugjwsPDqVGjBuvXr7+vg/KTGj58OMOHDzcOTzM1W89F8NOe7CUepvWshoeDVb7f57XQbXy9639sVBJACxaKwksOgbzaagaODuqPPBNCCFH8qB50mjVrxuOm8hkxYgQjRowooIqKvoiENN5ZfgKAQc+VoUXF/F3iISbqDN9ufosV6TfI0mjQKAodLb0Y0WIaJT2q5et9CyGEEI9SqPvoiCenNyi8tewYsckZVCrpwLh2FfPtvlISbzH39960X9eTpRk3ydJoaKS1Z3mTGUx+caOEHCFUNHDgQDQaDRqNBnNzc8qWLcvYsWNJS0tTuzQhCpTqLTpqMdU+Ot/uuMzeyzFYm+uY1bcmlmZ5v8RDZkYyv295l7m3dhCj04JWSxXFnLdqjSaoWv88vz8hxNNp27Yt8+fPJzMzk8OHDzNgwAA0Gg1ffPGF2qUJUWCKbYvO8OHDOXPmDAcPHlS7lDxz+Optvtp0AYBJnSvj55a3q6Urej2bdk6i26L6fBq5ixidFh+Dhqn+fVnc/5CEHCEKGUtLSzw9PfHx8aFLly60atWKTZs2AdkjWKdMmULZsmWxtramevXqrFixIsf116xZQ0BAAFZWVjRv3pyff/4ZjUZDXFyccZ/du3fTuHFjrK2t8fHxYdSoUSQnJwOwcOFC7OzsuHjxonH/YcOGUbFiRVJSUvL/ARCCYtyiY2riU7OXeNAbFDpV96Jn7bzt/Hv48Pd8dWIOJ7R60IGzQWGodyt6NpuCuYV1nt6XEIWZoiikZqrTEmxtrnvqUSinTp1i7969lC5dGshepmbRokV8++23BAQEsHPnTl5++WXc3Nxo2rQpoaGh9OjRgzfffJPXXnuNo0eP8s477+S4zcuXL9O2bVs+/fRTfvrpJ6Kioox9KufPn0///v1Zt24dL730Env37mXDhg3MmzePffv2yZxEosBI0DEBiqLw/h8nuRGXio+zNZ92rZJnQ2EvX/iTmfs+ZjspoAVrg0J/5xoMbPUVdrbueXIfQhQlqZl6Kn20QZX7PvNxG2wscv+2vW7dOuzs7MjKyiI9PR2tVsvs2bNJT0/ns88+Y/PmzcY5ycqVK8fu3bv57rvvaNq0Kd999x0VKlRg6tSpAFSoUIFTp04xefJk4+1PmTKFl156idGjRwMQEBDAN998Q9OmTZk7dy5WVlZ89913VKtWjVGjRvH7778zceJEateunXcPihCPIUHHBPx2KIw/T9zCTKvhmz41cbB69iUeIm4eZs62d1mVGYlBo0GnKHS3KcMbLb/C1aV8HlQthMhvzZs3Z+7cuSQnJzNjxgzMzMzo3r07p0+fJiUl5b7Z2zMyMqhZsyYA58+fp27dujkuDwoKyvH78ePHOXHiBL/++qtxm6IoGAwGQkNDCQwMpESJEvz444+0adOGhg0bMm7cuHw6WiEeTIJOEXcpMtG4xMPbrStQ0/cJlnhQFDLiw7hyfQ8h4Ue5fPsSISm3CMlK4opOIUujAY2GVjpnRjWdTFmfRvl0FEIUHdbmOs583Ea1+34Stra2+Pv7A/DTTz9RvXp1fvzxR6pUqQLAn3/+ibe3d47rPMlagElJSQwdOpRRo0bdd5mvr6/x5507d6LT6bh16xbJycnY28vEoaLgFNugYwqjrtIy9YxYfJS0TAON/F0Z2qTcg3fMSicl8gxXbuznctQJQuJDuZwWRaghlWs6LYb/nuYyA9BQCyveCnqPGoE98vtQhCgyNBrNE50+Kiy0Wi3vv/8+Y8aM4cKFC1haWnLt2jWaNm36wP0rVKjAX3/9lWPbfwdv1KpVizNnzhjD1IPs3buXL774grVr1/Lee+8xYsQIfv7552c/ICFySaM8brY+E3d3ZuT4+HgcHBzULueJTFh9ip/3XcXF1oK/32yMuy6ZxPBjhNw8QEj0GUKSwricHksImdwwf/gbs70C5TTW+Fm7Uc6xHH5u1fDzaYinayWZ9l4Ue2lpaYSGhlK2bFmsrPJ/hvG8MnDgQOLi4li1apVxW1ZWFmXKlGH06NHExcXx7bffMn36dBo1akR8fDx79uzBwcGBAQMGEBoaSoUKFXjrrbd49dVXOXbsGG+//TbXr18nLi4OR0dHTpw4Qf369XnllVd47bXXsLW15cyZM2zatInZs2eTmJhIjRo16NKlC9OnT+fkyZPUrVuXRYsW0aOH/AMlHu9Rr7/cfn4XvX9LBADbdm8k/uR8ejmHY1siif8tTeKyDiLN/vOUmsPdp9lZ0VDOzB4/m5KULRGAn0dN/Hyew9XOSwKNEMWAmZkZI0aM4MsvvyQ0NBQ3NzemTJlCSEgITk5O1KpVi/fffx+AsmXLsmLFCt5++22+/vprGjRowAcffMAbb7xhPL1VrVo1duzYwQcffEDjxo1RFAU/Pz969+4NwJtvvomtrS2fffYZAFWrVuWzzz5j6NChNGjQ4L7TZkLkB2nRKYItOpfP/Mngfe8S9ZDJAN3RUc68BH72pSjnXBE/ryDKedamhLVzAVcqRNFXVFt08sPkyZP59ttvCQsLU7sUUUxIi04xpGSm8/nu8URZ6rDXQ3VbL/wcS+PnWpVyXkGUc62EvYV09BNCPLs5c+ZQt25dXFxc2LNnD1OnTpV1B0WRI0GniPlr42j2WyqYKwp9Sn3OqNYd1C5JCGGiLl68yKeffkpsbCy+vr68/fbbjB8/Xu2yhHgixTboFMVRV7dvHOKLiJ2g0+ITG0j/Xq3VLkkIYcJmzJjBjBkz1C5DiGcia10VlbWuDAambhzGbZ2WkulaSnu+i5ONhdpVCSGEEIVasQ06Rc3uHRNYq01FoyjE3+zDS/X91C5JCCGEKPQk6BQBKbGX+SRkJQB+caWwtW9AUFkZQSWEEEI8jgSdwk5RmPXnq9w00+GWpeF4xGu8WNdX5r0RQgghckGCTiF34mAwv+qjAdDf6oBOa0O3WjLJlhBCCJEbEnQKscykSCacnIui0VA3y5OrSc/RurInLna5X3RPCCGEKM6KbdAJDg6mUqVK1K1bV+1SHurHP1/lkpmWEgY4ev01APoG+T7mWkIIkTvbt29Ho9EQFxendilC5JtiG3QK+/DykNO/8X1qKAAdnXtyO9UOX2cbGpRzUbkyIURRcDfEPOyrefPmNGzYkFu3buHo6Kh2uULkm2I7YWBhZshIZsL+T8g009DY3IV9N1sCcfSu64NWK52QhRCPdzfE/NeaNWt4/fXXGTZsGBYWFnh6eqpQ3f0yMzMxNzdXuwxhgopti05htuyv1zlmBjYGhX51ZnDkahxmWg0965RSuzQhhKJARrI6X0+wBvPdEHPv1+3bt3nnnXd4//336dmz532nrhYsWICTkxOrVq0iICAAKysr2rRpk2MRz4kTJ1KjRg2+++47fHx8sLGxoVevXsTHx+e4/3nz5hEYGIiVlRUVK1Zkzpw5xsuuXLmCRqNh2bJlNG3aFCsrK3799ddne16EeAhp0SlkwkO3MfP2EdBqebNsZzaez579uGWgO+72xXvlZCEKhcwU+MxLnft+/yZY2D7VVePi4ujcuTPNmjXjk08+eeh+KSkpTJ48mYULF2JhYcGwYcPo06cPe/bsMe5z6dIlfvvtN9auXUtCQgKvvvoqw4YNM4aVX3/9lY8++ojZs2dTs2ZNjh49yuDBg7G1tWXAgAHG2xk3bhzTp0+nZs2axX5leJF/JOgUIkpWJp9se5sUnZbqWlu6NJxIgynbAOgjnZCFEE/JYDDQt29fzMzM+PXXXx85D1dmZiazZ8+mXr16APz8888EBgZy4MABgoKCAEhLS2PhwoV4e2dPdTFr1ixeeOEFpk+fjqenJxMmTGD69Ol069YNgLJly3LmzBm+++67HEFn9OjRxn2EyC8SdAqR9VveZacuE3NFYVKLb9h4OpL41Ey8naxpEuCmdnlCCABzm+yWFbXu+ym8//777Nu3jwMHDmBvb//Ifc3MzHKMRq1YsSJOTk6cPXvWGHR8fX2NIQegQYMGGAwGzp8/j729PZcvX+bVV19l8ODBxn2ysrLu6/Rcp06dpzoeIZ6EBJ1CIi78OJ/f2AQ6LYM9GuHnHcT76/YB0KuODzrphCxE4aDRPPXpIzUsXbqUadOm8eeffxIQEJDv95eUlATADz/8YGwVukun0+X43da26DyOouiSoFMYKApTN7xOrE6Lv2LOa89/Q0hUEv+ExqLVQK+60glZCPHkjh07xquvvsrnn39OmzZtcnWdrKwsDh06ZGy9OX/+PHFxcQQGBhr3uXbtGjdv3sTLK7uv0v79+9FqtVSoUAEPDw+8vLwICQnhpZdeyvuDEuIJSdApBPbs+ow1JKFRFCY2+hRzMwuWHrwMQPMK7pR0tFa5QiFEURMdHU2XLl1o1qwZL7/8MuHh4Tku/2/ryl3m5uaMHDmSb775BjMzM0aMGEH9+vWNwQfAysqKAQMGMG3aNBISEhg1ahS9evUyDlWfNGkSo0aNwtHRkbZt25Kens6hQ4e4ffs2Y8aMyb+DFuIBim3QCQ4OJjg4GL1er2odKXHX+PjSYtBpecmxMtX925OepWfF4euAdEIWQjydP//8k6tXr3L16lVKlix53+WlS5dmwYIF9223sbHhvffeo2/fvty4cYPGjRvz448/5tjH39+fbt260b59e2JjY+nQoUOO4eOvvfYaNjY2TJ06lXfffRdbW1uqVq3K6NGj8/owhXgsjaI8wcQMJighIQFHR0fi4+NxcHAo8Pv/4tdWLMqKwMug4Y8Xd2Fj5ci6EzcZsfgoHg6W7HmvBWY6me5ICLWkpaURGhpK2bJlTX4I9IIFCxg9evQjl4SYOHEiq1at4tixYwVWlyi+HvX6y+3nt3yCqujE4e/5NTO7OfnDWmOwscoekbD0QPbkXL3q+EjIEUIIIZ6BfIqqJDMllgnHvkHRaOhgVYpG1QcCcC0mhd2XotFosoOOEEIIIZ6eBB2V/PTXYC6ZaShhgLHt5xm3Lz14DYDGAW74OD/dnBlCCPE0Bg4c+NiVzCdOnCinrUSRIkFHBSHnVvNd0nkA3qvYjxL22RNvZeoNLL/TCfnFutKaI4QQQjwrCToFzJCZxsS9H5Gp0dDYrATt679rvGzruUiiEtNxtbOgZaCHilUKIYQQpkGCTgH7bf1wjuoM2BgUPmzzXY41Z5YcyD5t1aO2DxZm8tQIIYQQz0o+TQtQ+LU9zIzeD8Cbvu0p6frvTKM34lLZcSEKgD5y2koIIYTIExJ0Coii1/PpljdJ1mqpjjW9m32W4/LfDoahKNCgnAtlXGX9FyGEECIvSNApIBu2jWeHNh0zRWFS8xnodP9OSq03KPx2KHvunBfryUzIQgiRW1euXEGj0Tx2JFizZs1MfmbmgQMH0qVLl3y9j+3bt6PRaB47Oq8wKbZBJzg4mEqVKlG3bt18v6+4qLNMufYnAEPc6uHn+1yOy3dciORWfBolbMxpU1k6IQshnt3AgQPRaDRoNBrMzc0pW7YsY8eOJS0tTe3S8pSPjw+3bt2iSpUqQNH8IM4rX3/99QOX9XhaDwqHDRs25NatWzg6OubZ/eS3YrvW1fDhwxk+fLhxCul8oyhMXT/kzsrkOl5rHXzfLov/yW7N6VarFJZmD15oTwghnlTbtm2ZP38+mZmZHD58mAEDBqDRaPjiiy/ULi3P6HQ642KixV1BhA8LC4si93gX2xadgrJ333TWGOLQKAoTGkzC3DznWh0RCWlsOx/J/9u787gq6vUP4J/DzmEHZZVNIQFlUVFDroJJgWlhmqaiYZI7iaKG5c8lzH1PvS5ZYsU110hR8xIJKHgJEFBTQQ3U9CBubIdNOM/vDy9zPYIIyuEkPu/X67xezsx3vvPMI3ge5/udGQAY1YsnITPGWo6mpibMzc1hbW2NIUOGwM/PD3FxccJ2mUyGZcuWwd7eHtra2nB3d8f+/fvl+jh06BAcHR2hpaWF/v37Y9euXfWumJw6dQp9+/aFtrY2rK2tMX36dEilUgDAd999B11dXVy+fFloP3XqVDg5OaG8vLxezMXFxVBVVUV6eroQo7GxMV5//XWhzQ8//ABr60f/Xj4+dJWfn4/+/fsDAIyMjCASiTBu3Di58/30009hbGwMc3NzLFq0qNH81Q0FLV26FGZmZjA0NERkZCRqamowZ84cGBsbo0OHDti5c6fcfjdu3MCIESNgaGgIY2NjBAYGIj8//4X7PXfuHN544w1oa2vDxMQEEydORFlZWb1+H8/Lkx9fX18AwL179zBq1ChYWVlBLBbD1dUVu3fvlusrMTERGzZsEPbNz89v8IrZgQMH0KVLF2hqasLOzg5r1qyRi9vOzg5Lly7F+PHjoaenBxsbG2zfvr3R3LckLnQUqLz0FiIvPfpBHa3XGR6dA+u12Zd+A7UyQk87IziY6rV2iIyxZiIiSKVSpXxe5B3M58+fR0pKCjQ0NIR1y5Ytw3fffYetW7fijz/+wMyZMzFmzBgkJiYCAPLy8vD+++9jyJAhyM7OxqRJkzBv3jy5fq9evYqAgAAMGzYMZ8+exZ49e3Dq1CmEhoYCAD788EO8/fbbCAoKQk1NDY4cOYIdO3YgOjoaYnH9p78bGBjAw8MDCQkJAB59uYtEImRmZgpf6omJifDx8am3r7W1NQ4cOAAAyMnJgUQiwYYNG4Ttu3btgo6ODlJTU7Fy5UpERkbKFX4N+e2333Dr1i0kJSVh7dq1WLhwIQYPHgwjIyOkpqZi8uTJmDRpEv7669HDXh8+fAh/f3/o6enh5MmTSE5Ohq6uLgICAlBdXf3c/UqlUvj7+8PIyAhpaWnYt28ffv31VyHPDeVCIpEIn8zMTJiYmKBfv34AHr0ss0ePHjhy5AjOnz+PiRMnYuzYsfj9998BPBoG8/LywoQJE4Q+6orLx2VkZGDEiBEYOXIkzp07h0WLFmH+/Pn1htDWrFkDT09PZGZmYurUqZgyZQpycnIazX2LoVdccXExAaDi4uIW73vFbn/qGtWV3vy2K0nL79fbXlsrI+/l8WQbEUsHMm60+PEZYy+uoqKCLly4QBUVFUREVFZWRgCU8ikrK2ty3MHBwaSqqko6OjqkqalJAEhFRYX2799PRESVlZUkFospJSVFbr+QkBAaNWoUERFFRERQ165d5bbPmzePANCDBw+E9hMnTpRrc/LkSVJRURFydv/+ferQoQNNmTKFzMzMaMmSJY3GHh4eToMGDSIiovXr19MHH3xA7u7udOzYMSIicnBwoO3btxMRUV5eHgGgzMxMIiI6ceKEXHx1fHx86B//+Ifcup49e1JERMRT4wgODiZbW1uqra0V1nXu3Jn69u0rLNfU1JCOjg7t3r2biIi+//576ty5M8lkMqFNVVUVaWtr0/Hjx5+73+3bt5ORkZHcz8CRI0dIRUWFCgoKhH4DAwPrnUdFRQX17t2bBg8eLHfMJw0aNIhmzZolLPv4+FBYWJhcmyfzO3r0aHrzzTfl2syZM4dcXFyEZVtbWxozZoywLJPJyNTUlLZs2fLUWB6P/fHfv8c19fv7lZ2jo2jnsnchuvIvQCTCfPdPINY2qtfm1JW7+OtBBfS11PC2q4USomSMtWX9+/fHli1bIJVKsW7dOqipqWHYsGEAgCtXrqC8vBxvvvmm3D7V1dXo1q0bgEdXRZ68YaNXr15yy9nZ2Th79iyio6OFdUQEmUyGvLw8ODs7w8jICN988w38/f3Rp08fzJ07t9G4fXx88M0336C2thaJiYl46623YG5ujoSEBLi5ueHKlSvCEExzuLm5yS1bWFigsLCw0X26dOkCFZX/DX6YmZkJE5+BR3OETExMhH6ys7Nx5coV6OnJX6GvrKzE1atXn7vfixcvwt3dHTo6/3v8iLe3N2QyGXJycmBm9vQbWcaPH4/S0lLExcUJx6ytrcXSpUuxd+9e3Lx5E9XV1aiqqmrwKltjLl68iMBA+dEKb29vrF+/HrW1tVBVfTTv9PHci0QimJubPzP3LYULHQV4WFmChRmrIVMVYZCGOfp2n9hgu7oXeL7XzQpa6jwJmbGXgVgslpsX0drHbg4dHR04ODgAAL799lu4u7vjm2++QUhIiHAOR44cgZWVldx+mpqaTT5GWVkZJk2ahOnTp9fbZmPzv8dlJCUlQVVVFRKJBFKptF4h8Lh+/fqhtLQUZ86cQVJSEpYuXQpzc3MsX74c7u7usLS0hKOjY5NjrKOuri63LBKJIJPJmr1PY/2UlZWhR48ecoVfnfbt2z93v8/ryy+/xPHjx/H777/L5XzVqlXYsGED1q9fD1dXV+jo6GDGjBlyw2stSRHn1lRc6ChAVW0lHMWWuFtxExGPvZn8cXdKq/DvP24D4GfnMPYyEYlEcv+rflmoqKjg888/R3h4OEaPHg0XFxdoamri+vXrDc53AYDOnTvj6NGjcuvS0tLklrt3744LFy4IBVVDUlJSsGLFChw+fBgREREIDQ3Frl27ntre0NAQbm5u2LRpE9TV1eHk5ARTU1N88MEHiI2NfWq8AIQ5SLW1tU9to0jdu3fHnj17YGpqCn19/Rbr19nZGVFRUZBKpcLPX3JyMlRUVNC5c+cG9zlw4AAiIyNx7NgxdOrUSW5bcnIyAgMDMWbMGACPJmrn5ubCxcVFaKOhofHMPDo7OyM5Oble36+99ppwNUfZeDKyAujqmGLFyOM4OPQojAxsG2xz4MxfqJERPKwN4WTecr8MjDH2NMOHD4eqqio2b94MPT09zJ49GzNnzsSuXbtw9epVnDlzBhs3bhSKkEmTJuHSpUuIiIhAbm4u9u7dK0wyrXtPX0REBFJSUhAaGoqsrCxcvnwZP//8szBJtrS0FGPHjsX06dMxcOBAREdHY8+ePfXu7nqSr68voqOjhaLG2NgYzs7O2LNnT6OFjq2tLUQiEWJjY3Hnzp1Wv/oWFBSEdu3aITAwECdPnkReXh4SEhIwffp0YWLx8/arpaWF4OBgnD9/HidOnMAnn3yCsWPHNjhsdf78eXz44YeIiIhAly5dUFBQgIKCAty/fx8A4OjoiLi4OKSkpODixYuYNGkSbt++LdeHnZ0dUlNTkZ+fj7t37zZ4BWbWrFmIj4/H4sWLkZubi127dmHTpk2YPXv2c59rS+NCR4HaGTR8pYaI8ON/X+DJt5QzxlqLmpoaQkNDsXLlSkilUixevBjz58/HsmXL4OzsjICAABw5cgT29vYAAHt7e+zfvx8HDx6Em5sbtmzZItx1VTe85ebmhsTEROTm5qJv377o1q0bFixYAEtLSwBAWFgYdHR0sHTpo9feuLq6YunSpZg0aRJu3rz51Fh9fHxQW1srNxfH19e33ronWVlZ4YsvvsDcuXNhZmb21LuSFEUsFiMpKQk2NjYYOnQonJ2dERISgsrKyhe6wiMWi3H8+HHcv38fPXv2xPvvv48BAwZg06ZNDbZPT09HeXk5vvzyS1hYWAifoUOHAgD+7//+D927d4e/vz98fX1hbm5e76nKs2fPhqqqKlxcXNC+fXtcv3693nG6d++OvXv34scff0TXrl2xYMECREZGyt3Wr2wiohe4X7ENqHtgYHFxcYteZmxMytW7GP11KnQ11ZD6+QDoaPIIImN/V5WVlcjLy4O9vT20tLSevUMbt2TJEmzduhU3btxQdijsFdDY719Tv7/5G1YJfvz90T8Q73pYcpHDGPtb++c//4mePXvCxMQEycnJWLVqVatfJWHsRfC3bCu7L63GL+cLAACjevIkZMbY39vly5fx5Zdf4v79+7CxscGsWbPw2WefKTssxpqMC51WdvDMX6iulaGrlT5cO7w8L0VjjL2a1q1bh3Xr1ik7DMaeG09GbkVEhB/THg1bjeSrOYwxxpjCcaHTitKvPcCVwjJoq6si0MNS2eEwxhhjbR4XOq1o939vKX/H3QJ6WurPaM0YY4yxF/XKFjqbN2+Gi4tLvfe4KEpx+UMcOSsBAIzsxcNWjDHGWGt4ZQudadOm4cKFC/UeZ64oMVk3UVUjg5O5HrpZG7bKMRljjLFX3Stb6LQmIhKGrUb2tBYenc4YY4wxxeJCpxVk/1WMSwWl0FRTwXvdOig7HMYYAwAkJCRAJBKhqKhI2aG0iHHjxtV7jcGT2to5NyQ/Px8ikQhZWVkKPY6vry9mzJih0GO0BC50WsHu1EdXcwa5WsBAzJOQGWOKVfdl/rRP//79AQB9+vSBRCKBgUHbeKbXhg0bhJeOAi/PF3FLs7a2hkQiQdeuXVukv6cVhwcPHsTixYtb5BiKxA8MVLCyqhocPnsLAE9CZoy1jroC5kmHDh3C5MmTMXXqVACAhoYGzM3NWzu8Bj18+BDq6i/2H8G2UrC9KFVV1Vb5ezU2Nlb4MVoCX9FRsENZt1BeXYtO7XXQ085I2eEwxl4BdQXM458HDx5g9uzZ+PzzzzF8+HAA9f+nHhUVBUNDQ8TExMDR0RFaWlrw9/eXe4HnokWL4OHhgW3btsHa2hpisRgjRoxAcXGxXAw7duyAs7MztLS04OTkhH/+85/CtrqhlT179sDHxwdaWlqIjo6udx6zZ8/G4MGDheX169dDJBLhl19+EdY5ODhgx44dAOSHrsaNG4fExERs2LBBuJKVn58v7JeRkQFPT0+IxWL06dMHOTk5T81nXbx79+5F3759oa2tjZ49eyI3NxdpaWnw9PSErq4uBg4ciDt37jQ7D83tVyaTITIyEh06dICmpiY8PDzkcvLk0NW4ceMavLKXkJAAAPj+++/h6ekJPT09mJubY/To0SgsLBT6qrsCaGRkBJFIJLyZ/MkrZg8ePMCHH34IIyMjiMViDBw4EJcvXxa21/18HT9+HM7OztDV1UVAQECDRXmLoldccXExAaDi4mKF9D/4q5NkGxFLXyddVUj/jDHFqqiooAsXLlBFRQUREclkMpJWS5Xykclkz3UODx48IEdHR3rnnXfk+jhx4gQBoAcPHhAR0c6dO0ldXZ08PT0pJSWF0tPTqVevXtSnTx9hn4ULF5KOjg698cYblJmZSYmJieTg4ECjR48W2vzwww9kYWFBBw4coD///JMOHDhAxsbGFBUVRUREeXl5BIDs7OyENrdu3aoX96FDh8jAwIBqamqIiGjIkCHUrl07ioiIICKiv/76iwDQ5cuXiYgoODiYAgMDiYioqKiIvLy8aMKECSSRSEgikVBNTY1wzr1796aEhAT6448/qG/fvnLn+KS6eJ2cnOiXX36hCxcu0Ouvv049evQgX19fOnXqFJ05c4YcHBxo8uTJzc5Dc/tdu3Yt6evr0+7du+nSpUv06aefkrq6OuXm5sr1m5mZKeSiLgcSiYTCwsLI1NSUJBIJERF98803dPToUbp69SqdPn2avLy8aODAgUREVFNTQwcOHCAAlJOTQxKJhIqKioiIyMfHh8LCwoS43n33XXJ2dqakpCTKysoif39/cnBwoOrqarmfLz8/P0pLS6OMjAxydnaW+9l50pO/f49r6vc3D10p0PmbxTh3sxgaqioY2p0nITPWFlTUVKD3v3or5dipo1MhVhc3ax+ZTIbRo0dDTU0N0dHRz7zr8+HDh9i0aRN69350jrt27YKzszN+//139OrVCwBQWVmJ7777DlZWVgCAjRs3YtCgQVizZg3Mzc2xcOFCrFmzBkOHDgUA2Nvb48KFC9i2bRuCg4OFY82YMUNo05C+ffuitLQUmZmZ6NGjB5KSkjBnzhzExMQAeHRFysrKCg4ODvX2NTAwgIaGBsRicYPDOEuWLIGPjw8AYO7cuRg0aBAqKyuhpaX11Hhmz54Nf39/AEBYWBhGjRqF+Ph4eHt7AwBCQkLk5gg1NQ/N7Xf16tWIiIjAyJEjAQArVqzAiRMnsH79emzevLnBXNQN6x08eBDbtm3Dr7/+KuRl/PjxQtuOHTviq6++Qs+ePVFWVgZdXV1hiMrU1BSGhoYN5uby5cs4dOgQkpOT0adPHwBAdHQ0rK2tERMTI1xFfPjwIbZu3YpOnToBAEJDQxEZGfnUnLcEHrpSoLpbyt/qYgZjHQ0lR8MYexV9/vnnOH36NH7++Wfo6ek9s72amprcg1SdnJxgaGiIixcvCutsbGyEIgcAvLy8IJPJkJOTA6lUiqtXryIkJAS6urrC58svv8TVq1fljuXp6dloLIaGhnB3d0dCQgLOnTsHDQ0NTJw4EZmZmSgrK0NiYqJQrDSXm5ub8GcLCwsAEIZrmrKPmZkZAMDV1VVuXV0fzclDc/otKSnBrVu3hCKojre3t9zfUUMyMzMxduxYbNq0SW7/jIwMvPPOO7CxsYGenp6Q0+vXrzfa3+MuXrwINTU1oUAGABMTE3Tu3FkuLrFYLBQ5wKPcPyvvL4qv6ChIeXUNfs56NAl5NE9CZqzN0FbTRuroVKUduzl+/PFHrF69GkeOHIGjo6OCopJXVlYGAPj666/lvvSAR5NkH6ejo/PM/nx9fZGQkABNTU34+PjA2NgYzs7OOHXqFBITEzFr1qznivPxic91V7lkMlmz93lyXV0fzclDc/p9XgUFBXj33Xfx8ccfIyQkRFgvlUrh7+8Pf39/REdHo3379rh+/Tr8/f1RXV39QsdsyJMTzkUiEYioxY/zOC50FCT2rARlVTWwNRHj9Y4myg6HMdZCRCJRs4ePlCErKwshISFYvny5MCzSFDU1NUhPTxeGqXJyclBUVARnZ2ehzfXr13Hr1i1YWj56OfF//vMfqKiooHPnzjAzM4OlpSX+/PNPBAUFvfB5+Pj44Ntvv4WamhoCAgIAPCp+du/ejdzcXPj6+j51Xw0NDdTW1r5wDM+jpfNQR19fH5aWlkhOTpa7mpWcnCz8nT2psrISgYGBcHJywtq1a+W2Xbp0Cffu3cPy5cthbW0NAEhPT5dro6HxaESisVw6OzujpqYGqampwtDVvXv3kJOTAxcXl+afaAviQkdB6oatPuhpDRUVfhIyY6z13L17F0OGDIGvry/GjBmDgoICue2qqqpo3759g/uqq6vjk08+wVdffQU1NTWEhobi9ddfl/sS1dLSQnBwMFavXo2SkhJMnz4dI0aMEOZ8fPHFF5g+fToMDAwQEBCAqqoqpKen48GDBwgPD2/WufTr1w+lpaWIjY3F8uXLATwqdN5//31YWFjgtddee+q+dnZ2SE1NRX5+vtxck9bSknl43Jw5c7Bw4UJ06tQJHh4e2LlzJ7Kyshq8cw0AJk2ahBs3biA+Pl7u7i1jY2PY2NhAQ0MDGzduxOTJk3H+/Pl6z8axtbWFSCRCbGws3n77bWhra0NXV1eujaOjIwIDAzFhwgRs27YNenp6mDt3LqysrBAYGPjc59oSeI6OAkiraiACoKYiwvs9eBIyY6x1HTlyBNeuXcPRo0dhYWFR79PYy4zFYjEiIiIwevRoeHt7Q1dXF3v27JFr4+DggKFDh+Ltt9/GW2+9BTc3N7nbpj/++GPs2LEDO3fuhKurK3x8fBAVFQV7e/tmn4uRkRFcXV3Rvn17ODk5AXhU/MhksmfOz5k9ezZUVVXh4uIiDMm0ppbMw+OmT5+O8PBwzJo1C66urvjll19w6NChpw5PJiYmQiKRwMXFRe7nICUlBe3bt0dUVBT27dsHFxcXLF++HKtXr5bb38rKCl988QXmzp0LMzMzhIaGNnicnTt3okePHhg8eDC8vLxARDh69OgLPx/pRYlI0YNjf3MlJSUwMDBAcXEx9PX1W7TvW0UVsDRs3pg6Y+zvpbKyEnl5ebC3t2/0jpy2ICoqCjNmzGj09QiLFi1CTEyMwl8vwBjQ+O9fU7+/+YqOAnGRwxhjjCkXFzqMMcYYa7N46EqBQ1eMsZffqzR0xdjfDQ9dMcYYY4w1ggsdxhhjjLVZXOgwxlgTvOKj/IwpRUv83nGhwxhjjah7XL8iHofPGGtceXk5gPqvjmiONvFk5Pfeew8JCQkYMGAA9u/fr+xwGGNtiJqaGsRiMe7cuQN1dXWoqPD/DxlTNCJCeXk5CgsLYWhoWO/9YM3RJgqdsLAwjB8/Hrt27VJ2KIyxNkYkEsHCwgJ5eXm4du2assNh7JViaGgovFrkebWJQqfu7baMMaYIGhoacHR05OErxlqRurr6C13JqaP0QicpKQmrVq1CRkYGJBIJfvrpJwwZMkSuzebNm7Fq1SoUFBTA3d0dGzdufOpbWhljTBFUVFT4OTqMvYSUPtgslUrh7u6OzZs3N7h9z549CA8Px8KFC3HmzBm4u7vD398fhYWFrRwpY4wxxl42Sr+iM3DgQAwcOPCp29euXYsJEybgo48+AgBs3boVR44cwbfffou5c+c2+3hVVVWoqqoSlktKSpofNGOMMcZeCkq/otOY6upqZGRkwM/PT1inoqICPz8/nD59+rn6XLZsGQwMDISPtbV1S4XLGGOMsb8ZpV/Raczdu3dRW1sLMzMzufVmZma4dOmSsOzn54fs7GxIpVJ06NAB+/btg5eXV4N9fvbZZwgPDxeWi4uLYWNjw1d2GGOMsZdI3ff2sx4q+LcudJrq119/bXJbTU1NaGpqCst1ieIrO4wxxtjLp7S0FAYGBk/d/rcudNq1awdVVVXcvn1bbv3t27df+L76OpaWlrhx4wb09PQgEolapE/WuJKSElhbW+PGjRv8xvhWxrlXHs698nDulUeRuScilJaWwtLSstF2f+tCR0NDAz169EB8fLxwy7lMJkN8fDxCQ0Nb5BgqKiro0KFDi/TFmkdfX5//0VESzr3ycO6Vh3OvPIrKfWNXcuoovdApKyvDlStXhOW8vDxkZWXB2NgYNjY2CA8PR3BwMDw9PdGrVy+sX78eUqlUuAuLMcYYY+xplF7opKeno3///sJy3UTh4OBgREVF4YMPPsCdO3ewYMECFBQUwMPDA7/88ku9CcqMMcYYY09SeqHj6+v7zBnToaGhLTZUxZRPU1MTCxculJsUzloH5155OPfKw7lXnr9D7kX0rCqDMcYYY+wl9bd+YCBjjDHG2IvgQocxxhhjbRYXOowxxhhrs7jQYYwxxlibxYUOU5hly5ahZ8+e0NPTg6mpKYYMGYKcnBy5NpWVlZg2bRpMTEygq6uLYcOG1XsSNnsxy5cvh0gkwowZM4R1nHfFuXnzJsaMGQMTExNoa2vD1dUV6enpwnYiwoIFC2BhYQFtbW34+fnh8uXLSoy4baitrcX8+fNhb28PbW1tdOrUCYsXL5a7q5dz3zKSkpLwzjvvwNLSEiKRCDExMXLbm5Ln+/fvIygoCPr6+jA0NERISAjKysoUEi8XOkxhEhMTMW3aNPznP/9BXFwcHj58iLfeegtSqVRoM3PmTBw+fBj79u1DYmIibt26haFDhyox6rYlLS0N27Ztg5ubm9x6zrtiPHjwAN7e3lBXV8exY8dw4cIFrFmzBkZGRkKblStX4quvvsLWrVuRmpoKHR0d+Pv7o7KyUomRv/xWrFiBLVu2YNOmTbh48SJWrFiBlStXYuPGjUIbzn3LkEqlcHd3x+bNmxvc3pQ8BwUF4Y8//kBcXBxiY2ORlJSEiRMnKiZgYqyVFBYWEgBKTEwkIqKioiJSV1enffv2CW0uXrxIAOj06dPKCrPNKC0tJUdHR4qLiyMfHx8KCwsjIs67IkVERNA//vGPp26XyWRkbm5Oq1atEtYVFRWRpqYm7d69uzVCbLMGDRpE48ePl1s3dOhQCgoKIiLOvaIAoJ9++klYbkqeL1y4QAAoLS1NaHPs2DESiUR08+bNFo+Rr+iwVlNcXAwAMDY2BgBkZGTg4cOH8PPzE9o4OTnBxsYGp0+fVkqMbcm0adMwaNAgufwCnHdFOnToEDw9PTF8+HCYmpqiW7du+Prrr4XteXl5KCgokMu9gYEBevfuzbl/QX369EF8fDxyc3MBANnZ2Th16hQGDhwIgHPfWpqS59OnT8PQ0BCenp5CGz8/P6ioqCA1NbXFY1L6k5HZq0Emk2HGjBnw9vZG165dAQAFBQXQ0NCAoaGhXFszMzMUFBQoIcq248cff8SZM2eQlpZWbxvnXXH+/PNPbNmyBeHh4fj888+RlpaG6dOnQ0NDA8HBwUJ+n3yFDef+xc2dOxclJSVwcnKCqqoqamtrsWTJEgQFBQEA576VNCXPBQUFMDU1lduupqYGY2NjhfxdcKHDWsW0adNw/vx5nDp1StmhtHk3btxAWFgY4uLioKWlpexwXikymQyenp5YunQpAKBbt244f/48tm7diuDgYCVH17bt3bsX0dHR+Ne//oUuXbogKysLM2bMgKWlJef+FcdDV0zhQkNDERsbixMnTqBDhw7CenNzc1RXV6OoqEiu/e3bt2Fubt7KUbYdGRkZKCwsRPfu3aGmpgY1NTUkJibiq6++gpqaGszMzDjvCmJhYQEXFxe5dc7Ozrh+/ToACPl98g43zv2LmzNnDubOnYuRI0fC1dUVY8eOxcyZM7Fs2TIAnPvW0pQ8m5ubo7CwUG57TU0N7t+/r5C/Cy50mMIQEUJDQ/HTTz/ht99+g729vdz2Hj16QF1dHfHx8cK6nJwcXL9+HV5eXq0dbpsxYMAAnDt3DllZWcLH09MTQUFBwp8574rh7e1d7xEKubm5sLW1BQDY29vD3NxcLvclJSVITU3l3L+g8vJyqKjIf6WpqqpCJpMB4Ny3lqbk2cvLC0VFRcjIyBDa/Pbbb5DJZOjdu3fLB9Xi05sZ+68pU6aQgYEBJSQkkEQiET7l5eVCm8mTJ5ONjQ399ttvlJ6eTl5eXuTl5aXEqNumx++6IuK8K8rvv/9OampqtGTJErp8+TJFR0eTWCymH374QWizfPlyMjQ0pJ9//pnOnj1LgYGBZG9vTxUVFUqM/OUXHBxMVlZWFBsbS3l5eXTw4EFq164dffrpp0Ibzn3LKC0tpczMTMrMzCQAtHbtWsrMzKRr164RUdPyHBAQQN26daPU1FQ6deoUOTo60qhRoxQSLxc6TGEANPjZuXOn0KaiooKmTp1KRkZGJBaL6b333iOJRKK8oNuoJwsdzrviHD58mLp27Uqamprk5ORE27dvl9suk8lo/vz5ZGZmRpqamjRgwADKyclRUrRtR0lJCYWFhZGNjQ1paWlRx44dad68eVRVVSW04dy3jBMnTjT4b3twcDARNS3P9+7do1GjRpGuri7p6+vTRx99RKWlpQqJV0T02GMjGWOMMcbaEJ6jwxhjjLE2iwsdxhhjjLVZXOgwxhhjrM3iQocxxhhjbRYXOowxxhhrs7jQYYwxxlibxYUOY4wxxtosLnQYY0rl6+uLGTNmKPw4dnZ2WL9+vcKP0xRRUVH13h7PGFMMLnQYY81y584dTJkyBTY2NtDU1IS5uTn8/f2RnJwstBGJRIiJiWlSfwcPHsTixYsVFK3y/Z0KLMZeRWrKDoAx9nIZNmwYqqursWvXLnTs2BG3b99GfHw87t2716x+qquroaGhAWNjYwVFyhhjfEWHMdYMRUVFOHnyJFasWIH+/fvD1tYWvXr1wmeffYZ3330XwKMrGADw3nvvQSQSCcuLFi2Ch4cHduzYAXt7e2hpaQGoP3RlZ2eHpUuXYvz48dDT04ONjQ22b98uF0dKSgo8PDygpaUFT09PxMTEQCQSISsrq1nn8vHHH6N9+/bQ19fHG2+8gezsbGF7Xbzff/897OzsYGBggJEjR6K0tFRoU1paiqCgIOjo6MDCwgLr1q2TOx9fX19cu3YNM2fOhEgkgkgkkovh+PHjcHZ2hq6uLgICAiCRSJocP2OsabjQYYw1ma6uLnR1dRETE4OqqqoG26SlpQEAdu7cCYlEIiwDwJUrV3DgwAEcPHiw0aJkzZo18PT0RGZmJqZOnYopU6YgJycHAFBSUoJ33nkHrq6uOHPmDBYvXoyIiIhmn8vw4cNRWFiIY8eOISMjA927d8eAAQNw//59oc3Vq1cRExOD2NhYxMbGIjExEcuXLxe2h4eHIzk5GYcOHUJcXBxOnjyJM2fOCNsPHjyIDh06IDIyEhKJRK6QKS8vx+rVq/H9998jKSkJ169fx+zZs5t9HoyxZ1DIq0IZY23W/v37ycjIiLS0tKhPnz702WefUXZ2tlwbAPTTTz/JrVu4cCGpq6tTYWGh3Pon36xua2tLY8aMEZZlMhmZmprSli1biIhoy5YtZGJiQhUVFUKbr7/+mgBQZmbmU+O2tbWldevWERHRyZMnSV9fnyorK+XadOrUibZt2ybEKxaLqaSkRNg+Z84c6t27NxE9elu2uro67du3T9heVFREYrG43vnUHbfOzp07CQBduXJFWLd582YyMzN7avyMsefDV3QYY80ybNgw3Lp1C4cOHUJAQAASEhLQvXt3REVFPXNfW1tbtG/f/pnt3NzchD+LRCKYm5ujsLAQAJCTkwM3Nzdh6AsAevXq1axzyM7ORllZGUxMTISrVLq6usjLy8PVq1eFdnZ2dtDT0xOWLSwshDj+/PNPPHz4UO7YBgYG6Ny5c5NiEIvF6NSpU4N9M8ZaDk9GZow1m5aWFt588028+eabmD9/Pj7++GMsXLgQ48aNa3Q/HR2dJvWvrq4utywSiSCTyZ433HrKyspgYWGBhISEetsev+1bkXE01DcRtUjfjLH/4Ss6jLEX5uLiAqlUKiyrq6ujtrZWIcfq3Lkzzp07JzdH6PF5QE3RvXt3FBQUQE1NDQ4ODnKfdu3aNamPjh07Ql1dXe7YxcXFyM3NlWunoaGhsFwwxp6NCx3GWJPdu3cPb7zxBn744QecPXsWeXl52LdvH1auXInAwEChnZ2dHeLj41FQUIAHDx60aAyjR4+GTCbDxIkTcfHiRRw/fhyrV68GgHp3NT2Nn58fvLy8MGTIEPz73/9Gfn4+UlJSMG/ePKSnpzepDz09PQQHB2POnDk4ceIE/vjjD4SEhEBFRUUuDjs7OyQlJeHmzZu4e/du80+YMfZCuNBhjDWZrq4uevfujXXr1qFfv37o2rUr5s+fjwkTJmDTpk1CuzVr1iAuLg7W1tbo1q1bi8agr6+Pw4cPIysrCx4eHpg3bx4WLFgAAHLzdhojEolw9OhR9OvXDx999BFee+01jBw5EteuXYOZmVmTY1m7di28vLwwePBg+Pn5wdvbG87OznJxREZGIj8/H506dWrS/CTGWMsSEQ8KM8ZectHR0fjoo49QXFwMbW1tpcUhlUphZWWFNWvWICQkRGlxMMb+hycjM8ZeOt999x06duwIKysrZGdnIyIiAiNGjGj1IiczMxOXLl1Cr169UFxcjMjISACQG8ZjjCkXFzqMsZdOQUEBFixYgIKCAlhYWGD48OFYsmSJUmJZvXo1cnJyoKGhgR49euDkyZNNntDMGFM8HrpijDHGWJvFk5EZY4wx1mZxocMYY4yxNosLHcYYY4y1WVzoMMYYY6zN4kKHMcYYY20WFzqMMcYYa7O40GGMMcZYm8WFDmOMMcbaLC50GGOMMdZm/T/80G3sF7sL0gAAAABJRU5ErkJggg==", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "plot_data_comparison4(\n", + " plot_title = \"Matching email regex against accepting strings\", \n", + " data1 = email_regex,\n", + " data1_label = \"Regex\",\n", + " data2 = email_zipper,\n", + " data2_label = \"Zipper\",\n", + " data3 = email_regex_mem,\n", + " data3_label = \"Regex with memoization\",\n", + " data4 = email_zipper_mem,\n", + " data4_label = \"Zipper with memoization\",\n", + " x_label = \"String length\",\n", + " y_label = \"Time (us)\")" + ] + }, + { + "cell_type": "code", + "execution_count": 72, + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAoYAAAHHCAYAAAAiZpktAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjkuMiwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8hTgPZAAAACXBIWXMAAA9hAAAPYQGoP6dpAACJ30lEQVR4nOzdd3gU1dvG8e+mF9IoIQRCR3qTJl0FCQgo0ouCCIhKxwLoi4KFKmIFrNhABQsqCEgvEpDeu3RIQksCIX3P+0fM/lhCSSTJBnJ/rmuvZGfOzjxzdnfm2TNzzliMMQYRERERyfOcHB2AiIiIiOQOSgxFREREBFBiKCIiIiL/UmIoIiIiIoASQxERERH5lxJDEREREQGUGIqIiIjIv5QYioiIiAigxFBERERE/qXE8F9ffvklFouFTZs23bLs/fffz/3335/9Qd0BVq5cicViYeXKlbZpTz75JCVLlnRYTHmR6jznpO0rjh496uhQsszduE03kpn99/3330+VKlWyN6C7TG78LFksFsaMGePoMO4YOZYYpn1YLBYLa9euTTffGENISAgWi4U2bdr8p3WMGzeOefPm3WakInKtadOm8eWXXzo6jBx1p+5P8uJ7dTtOnz7NmDFj2LZtm6NDEfnPdu/eTadOnShdujReXl4ULFiQJk2a8Pvvv2d6WTneYujh4cHs2bPTTV+1ahUnT57E3d39Py87p3bkf/75J3/++We2r+dO0KRJE+Li4mjSpImjQ8nTPv30U/bv359ty8+LycaN9idPPPEEcXFxlChRIueDyoD/8l7l9m3KStfuv0+fPs3YsWOVGGaRvPRZyk2OHTvGpUuX6NWrF++99x6jR48G4JFHHuGTTz7J1LJcsiPAm3n44YeZO3cu77//Pi4u/1v97NmzqVWrFufOncvpkDLNzc3N0SHkGk5OTnh4eGTJsowxxMfH4+npmSXLy0tcXV0dHUKe4ezsjLOzs6PDyBKxsbF4e3vfVdt0K47cf1utVhITE7Nsn3krV65cwcvLK0fWlSYvfZZuJSfr/+GHH+bhhx+2mzZw4EBq1arFO++8w9NPP53hZeV4i2G3bt04f/48S5YssU1LTEzkxx9/pHv37td9zdtvv02DBg0oUKAAnp6e1KpVix9//NGujMViITY2lq+++sp2yvrJJ5+0zT916hR9+vQhODgYd3d3SpUqxbPPPktiYqLdchISEhg+fDiFChXC29ubxx57jLNnz9qVufYalbTr7ObMmcNbb71FsWLF8PDwoFmzZhw6dCjd9nz00UeULl0aT09P6taty5o1azJ13cu3335LrVq18PT0JH/+/HTt2pUTJ06ki7FKlSrs2LGDpk2b4uXlRdmyZW31tmrVKurVq4enpyfly5dn6dKldq8/duwYzz33HOXLl8fT05MCBQrQqVOndNeNXO8aw4wqWbIkbdq0YfHixdSuXRtPT08+/vhjAKKiohg6dCghISG4u7tTtmxZJk6ciNVqtVvG+fPneeKJJ/D19cXf359evXqxfft2LBZLulaTffv20bFjR/Lnz4+Hhwe1a9fmt99+s82PjIykUKFC3H///RhjbNMPHTqEt7c3Xbp0uen2ZLTOANv74unpSbFixXjzzTeZOXNmumtzfv31V1q3bm373JYpU4Y33niDlJQUu+Vde43h0aNHsVgsvP3223zyySeUKVMGd3d36tSpw8aNG+1eGx4eTu/evSlWrBju7u4UKVKERx991BZHyZIl2b17N6tWrbJ9t271Wc3IdzbNt99+S926dfHy8iIgIIAmTZqka5FfuHAhTZs2xcfHB19fX+rUqZPuzMOGDRto2bIlfn5+eHl50bRpU/766y+7MmPGjMFisbBv3z46d+6Mr68vBQoUYMiQIcTHx9vK3Wx/cr1rqNI+y2vXrqVu3bp4eHhQunRpvv7663Tbm9H3/npu571Ki3vVqlU899xzBAYGUqxYsRzdpk2bNhEaGkrBggXx9PSkVKlSPPXUUzfd5uvZsWMHFovF7vu7efNmLBYL9957r13ZVq1aUa9ePdvzq/e1K1eupE6dOgD07t3bVmfX7jv27NnDAw88gJeXF0WLFmXSpEkZitNisTBw4EBmzZpF5cqVcXd3Z9GiRUDqMempp56icOHCuLu7U7lyZb744ot0yzh27BiPPPII3t7eBAYGMmzYMBYvXpxuv5u2z9+8eTNNmjTBy8uLl19+GUg9rr322muULVsWd3d3QkJCeOmll0hISLBb15IlS2jUqBH+/v7ky5eP8uXL25aR5oMPPqBy5cq272vt2rXtvos3usZw2rRptjoIDg5mwIABREVF2ZVJ24b/Wt8JCQkMGzaMQoUK4ePjwyOPPMLJkyevWzY31j9k7PieUc7OzoSEhKSr51vJ8RbDkiVLUr9+fb777jtatWoFpO70o6Oj6dq1K++//36617z33ns88sgj9OjRg8TERL7//ns6derE/Pnzad26NQDffPMNffv2pW7durbMuEyZMkDqqYK6desSFRXF008/TYUKFTh16hQ//vgjV65csfsFOWjQIAICAnjttdc4evQo7777LgMHDuSHH3645bZNmDABJycnXnjhBaKjo5k0aRI9evRgw4YNtjLTp09n4MCBNG7cmGHDhnH06FHatWtHQECAbSd9M2+99RajR4+mc+fO9O3bl7Nnz/LBBx/QpEkTtm7dir+/v63sxYsXadOmDV27dqVTp05Mnz6drl27MmvWLIYOHcozzzxD9+7dmTx5Mh07duTEiRP4+PgAsHHjRtatW0fXrl0pVqwYR48eZfr06dx///3s2bMny34F7d+/n27dutG/f3/69etH+fLluXLlCk2bNuXUqVP079+f4sWLs27dOkaNGsWZM2d49913gdRf323btuXvv//m2WefpUKFCvz666/06tUr3Xp2795Nw4YNKVq0KCNHjsTb25s5c+bQrl07fvrpJx577DECAwOZPn06nTp14oMPPmDw4MFYrVaefPJJfHx8mDZt2k23JaN1durUKR544AEsFgujRo3C29ubzz777LqXUXz55Zfky5eP4cOHky9fPpYvX86rr75KTEwMkydPvmX9zp49m0uXLtG/f38sFguTJk2iffv2/PPPP7ZWxg4dOrB7924GDRpEyZIliYyMZMmSJRw/fpySJUvy7rvvMmjQIPLly8crr7wCQOHChW+63ox8ZwHGjh3LmDFjaNCgAa+//jpubm5s2LCB5cuX06JFC1sdPPXUU1SuXJlRo0bh7+/P1q1bWbRoke3H5PLly2nVqhW1atXitddew8nJiZkzZ/Lggw+yZs0a6tataxdf586dKVmyJOPHj2f9+vW8//77XLx40Zb03Gx/ciOHDh2iY8eO9OnTh169evHFF1/w5JNPUqtWLSpXrgxk7r2/nqx4r5577jkKFSrEq6++SmxsbI5tU2RkJC1atKBQoUKMHDkSf39/jh49ys8//5yhbb9alSpV8Pf3Z/Xq1TzyyCMArFmzBicnJ7Zv305MTAy+vr5YrVbWrVt3w9aSihUr8vrrr/Pqq6/y9NNP07hxYwAaNGhgK3Px4kVatmxJ+/bt6dy5Mz/++CMj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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "image/png": "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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "image/png": "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JxsFvv/+DgUHB2NmZvZI88j891yDer5v/716mPf+v4rqvbrb79y9FOVratOmDU8//TRTpkxh+vTptG3blh49ejBw4MBy25Fego6JGzhwIMOHDycmJoYuXboY9S253datW+nevTutW7fmq6++okKFClhaWjJv3ry7DlN/VPf6n87tH1rF8b0Fodfr0el0/PXXX3d9LgcHhwLVU5A6x4wZw7x58xg3bhzNmjXD2dkZnU5H//797/jf8aO4/X/+N+n1eh577DFef/31u35P1apVH+o5btb76quv3tF6dNN//yivWbMGUAPBpUuX7vnzebuiPmdJSUm0adMGJycn3n33XYKDg7GxseHAgQO88cYbBXrMgrzX48aNo1u3bqxcuZI1a9bw9ttvM3XqVDZs2ECDBg0euu6S/p29eR4+/fRTo1aF2/33d+O/imK0UXH//kPRvFd3+527l6J8TTqdjmXLlrFr1y5+//131qxZw3PPPce0adPYtWvXA98jUyRBx8T17NmTkSNHsmvXrvt29P3111+xsbFhzZo1Rql/3rx5dxx7tz9Wnp6eODk5cfTo0aIp/BHcnKMiKiqKdu3aGbbn5eVx7tw56tate9/vDw4ORlEUgoKCHvrD/mEtW7aM8PBwpk2bZtiWlZVFUlJSsT4vqK8zLS2Njh073ve4SpUqsXHjRjIyMoxadf470q1y5cqA2uL1oMcEtSn+22+/5fXXX+fnn38mPDyc3bt3G11uvJuCnrNKlSrddTTef7dt2rSJhIQEli9fbjRPUXR09ANfw8MKDg7mlVde4ZVXXiEyMpL69eszbdo0FixYADxcECjo72xwcDB6vZ7jx4/fM6Dc7AB89OjRe7Z63DzGycmpQO8vqK1At7dsREVFodfrjVpWi2OodUHf+/spyveqKDzsa2ratClNmzblgw8+YOHChQwaNIjFixczbNiw4i611JE+OibOwcGB2bNn884779CtW7d7Hmdubo5OpzMaTnvu3Lm7TuRlb29/x4eKmZkZPXr04Pfff7/r8g5F+b+tB2ncuDHu7u588803RvMI/fzzzwW6NNarVy/Mzc2ZMmXKHXUrikJCQkKR1Wpubn7Hc3z55Zd3DGsuDn379mXnzp2GVpXbJSUlGc5dp06dyM3N5ZtvvjHs1+v1REREGH2Pl5cXbdu2Ze7cuVy9evWOx7x9+oCkpCTD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", "text/plain": [ "
" ] @@ -279,7 +499,7 @@ }, { "data": { - "image/png": "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", + "image/png": "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", "text/plain": [ "
" ] @@ -289,19 +509,37 @@ } ], "source": [ - "plot_data(\n", - " plot_title = \"Matching regex (a|b)* against accepting strings\", \n", - " data = regex_data,\n", + "plot_data_with_regression(\n", + " plot_title = \"Matching email regex against accepting strings\", \n", + " data = email_regex,\n", " data_label = \"Regex\",\n", " x_label = \"String length\",\n", + " y_label = \"Time (us)\",\n", + " degree = 3)\n", + "\n", + "plot_data_with_regression(\n", + " plot_title = \"Matching email regex mem against accepting strings\", \n", + " data = email_regex_mem,\n", + " data_label = \"Regex\",\n", + " x_label = \"String length\",\n", + " y_label = \"Time (us)\",\n", + " degree = 3)\n", + "plot_data_comparison2(\n", + " plot_title = \"Matching email regex against accepting strings\", \n", + " data1 = email_regex,\n", + " data1_label = \"Regex\",\n", + " data2 = email_regex_mem,\n", + " data2_label = \"Regex Mem\",\n", + " x_label = \"String length\",\n", " y_label = \"Time (us)\")\n", "\n", - "plot_data(\n", - " plot_title = \"Matching regex (a|b)* against accepting strings using Zipper\", \n", - " data = zipper_data,\n", + "plot_data_with_regression(\n", + " plot_title = \"Matching email zipper against accepting strings\", \n", + " data = email_zipper,\n", " data_label = \"Zipper\",\n", " x_label = \"String length\",\n", - " y_label = \"Time (us)\")" + " y_label = \"Time (us)\",\n", + " degree = 1)" ] }, { From b1d1116926d5e6bcd1cd4b6c3231d2353b8e80cd Mon Sep 17 00:00:00 2001 From: Samuel Chassot Date: Mon, 9 Dec 2024 16:49:14 +0100 Subject: [PATCH 77/78] typo --- .../src/main/scala/ch/epfl/map/OptimisedChecks.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data-structures/maps/mutablemaps/src/main/scala/ch/epfl/map/OptimisedChecks.scala b/data-structures/maps/mutablemaps/src/main/scala/ch/epfl/map/OptimisedChecks.scala index dffc6769..50762463 100644 --- a/data-structures/maps/mutablemaps/src/main/scala/ch/epfl/map/OptimisedChecks.scala +++ b/data-structures/maps/mutablemaps/src/main/scala/ch/epfl/map/OptimisedChecks.scala @@ -4,7 +4,7 @@ package ch.epfl.map object OptimisedChecks { - extension [T](inline value: T) inline def.ensuring(condition: T => Boolean): T = value + extension [T](inline value: T) inline def ensuring(condition: T => Boolean): T = value inline def require(inline condition: Boolean): Unit = () inline def assert(inline condition: Boolean): Unit = () } From cd9f735e1a0ae8a8b650c383a53fd7adb7ca01b7 Mon Sep 17 00:00:00 2001 From: Samuel Chassot Date: Thu, 12 Dec 2024 11:28:06 +0100 Subject: [PATCH 78/78] fix the ci --- .github/workflows/bolts-CI.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/bolts-CI.yml b/.github/workflows/bolts-CI.yml index d354b1c9..2a1dda3b 100644 --- a/.github/workflows/bolts-CI.yml +++ b/.github/workflows/bolts-CI.yml @@ -9,7 +9,6 @@ jobs: if: github.event.pull_request.draft == false runs-on: [self-hosted, linux] env: - JAVA_OPTS_TMP_DIR: ./tmp_java # define Java options for both official sbt and sbt-extras JAVA_OPTS: -Dsbt.io.implicit.relative.glob.conversion=allow -Xss512M -Xms1024M -Xmx12G -XX:MaxMetaspaceSize=2G -XX:+UseCodeCacheFlushing -XX:ReservedCodeCacheSize=768M JVM_OPTS: -Dsbt.io.implicit.relative.glob.conversion=allow -Xss512M -Xms1024M -Xmx12G -XX:MaxMetaspaceSize=2G -XX:+UseCodeCacheFlushing -XX:ReservedCodeCacheSize=768M