diff --git a/.github/workflows/bolts-CI.yml b/.github/workflows/bolts-CI.yml index 2431477e..d7638c34 100644 --- a/.github/workflows/bolts-CI.yml +++ b/.github/workflows/bolts-CI.yml @@ -24,6 +24,8 @@ jobs: temp-dir: "/tmp/tmp_${{ github.run_id }}_${{ github.run_attempt }}" - name: Bolts admit VCs 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] 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 34515fe6..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.* @@ -406,7 +404,7 @@ object TupleListOpsGenK { case Nil() => () } }.ensuring(_ => getKeysList(l).content - key == getKeysList(removePresrvNoDuplicatedKeys(l, key)).content) - + @opaque @inlineOnce def lemmaEqMapSameKeysSet[K, B](lm1: ListMap[K, B], lm2: ListMap[K, B]): Unit = { @@ -459,28 +457,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/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 = () } 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..5ee7ff6b --- /dev/null +++ b/lexers/regex/verifiedlexer/benchmark_results/Regex_benchmark_results.ipynb @@ -0,0 +1,574 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# First results" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Textual data" + ] + }, + { + "cell_type": "code", + "execution_count": 60, + "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": 61, + "metadata": {}, + "outputs": [], + "source": [ + "ab_star_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", + "ab_star_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", + "ab_star_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)]\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": 62, + "metadata": {}, + "outputs": [], + "source": [ + "import matplotlib.pyplot as plt\n", + "import numpy as np" + ] + }, + { + "cell_type": "code", + "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", + " 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_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", + " 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": 64, + "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 = ab_star_regex_data,\n", + " data1_label = \"Regex\",\n", + " data2 = ab_star_zipper_data,\n", + " data2_label = \"Zipper\",\n", + " data3 = ab_star_regex_mem_data,\n", + " data3_label = \"Regex with memoization\",\n", + " x_label = \"String length\",\n", + " y_label = \"Time (us)\")" + ] + }, + { + "cell_type": "code", + "execution_count": 65, + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "plot_data_comparison2(\n", + " plot_title = \"Matching regex (a|b)* against accepting strings\", \n", + " data1 = ab_star_regex_data,\n", + " data1_label = \"Regex\",\n", + " data2 = ab_star_zipper_data,\n", + " data2_label = \"Zipper\",\n", + " x_label = \"String length\",\n", + " y_label = \"Time (us)\")" + ] + }, + { + "cell_type": "code", + "execution_count": 66, + "metadata": {}, + "outputs": [ + { + "data": { + "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": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "image/png": "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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "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_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)\",\n", + " degree = 1)" + ] + }, + { + "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/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 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/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/benchmark/RegexBenchmark.scala b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/benchmark/RegexBenchmark.scala new file mode 100644 index 00000000..df7d339c --- /dev/null +++ b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/benchmark/RegexBenchmark.scala @@ -0,0 +1,191 @@ +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.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) +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 abStarAccepting_Regex(): Unit = { + val r = RegexBenchmarkUtil.abStar + val s = RegexBenchmarkUtil.abStar_Accepting_strings(size.toInt) + val res = matchR(r, s) + assert(res) + } + + @Benchmark + @BenchmarkMode(Array(Mode.AverageTime)) + @OutputTimeUnit(TimeUnit.MICROSECONDS) + def abStarAccepting_Zipper(): Unit = { + val r = RegexBenchmarkUtil.abStar + val s = RegexBenchmarkUtil.abStar_Accepting_strings(size.toInt) + val res = matchZipper(r, s) + assert(res) + } + + @Benchmark + @BenchmarkMode(Array(Mode.AverageTime)) + @OutputTimeUnit(TimeUnit.MICROSECONDS) + def abStarAccepting_RegexMem(): Unit = { + val r = RegexBenchmarkUtil.abStar + val s = RegexBenchmarkUtil.abStar_Accepting_strings(size.toInt) + val res = matchRMem(r, s)(RegexBenchmarkUtil.regexCache) + assert(res) + } + + @Benchmark + @BenchmarkMode(Array(Mode.AverageTime)) + @OutputTimeUnit(TimeUnit.MICROSECONDS) + 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) + assert(res) + } + + // Email accepting regex ----------------------------------------------------------------------------------------------------------------------- + + @Benchmark + @BenchmarkMode(Array(Mode.AverageTime)) + @OutputTimeUnit(TimeUnit.MICROSECONDS) + def emailAccepting_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 emailAccepting_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 emailAccepting_ZipperMem(): 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 emailAccepting_RegexMem(): Unit = { + val r = RegexBenchmarkUtil.emailRegex + val s = RegexBenchmarkUtil.email_Accepting_strings(size.toInt) + val res = matchRMem(r, s)(RegexBenchmarkUtil.regexCache) + assert(res) + } + + +} + +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 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) + + 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) + 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).toStainless)).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 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/benchmark/Utils.scala b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/benchmark/Utils.scala index 14e84d4d..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 @@ -11,6 +11,7 @@ import stainless.collection._ import ch.epfl.map.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.exprs.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/ListUtils.scala b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/ListUtils.scala deleted file mode 100644 index 674dbcd5..00000000 --- a/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/ListUtils.scala +++ /dev/null @@ -1,564 +0,0 @@ -/** Author: Samuel Chassot - */ -package ch.epfl.lexer - -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 = { - decreases(prefix) - (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) - decreases(l) - 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(lTot) - (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 = { - require(l.contains(e)) - require(l.head != e) - decreases(l) - - 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 = { - decreases(l1.size) - 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 = { - require(isPrefix(p, l)) - decreases(p) - 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)) - decreases(l1) - - 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/Main.scala b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/Main.scala index ba78a05f..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 @@ -3,7 +3,8 @@ 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._ import stainless.lang._ @@ -14,16 +15,94 @@ import scala.collection.View.Empty object Main { def main(args: Array[String]): Unit = { - RegexBenchmark.benchmark01() - RegexBenchmark.benchmark02() - RegexBenchmark.benchmark03() - RegexBenchmark.benchmark03Simp() + testZippers1() + testZippers2() + testZippers3() + println("Running zipper match test...") + testZipperMatch() + // 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 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 = ZipperRegex.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 = ZipperRegex.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 = ZipperRegex.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).* @@ -104,19 +183,27 @@ 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) + // 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) +<<<<<<< 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()))))))) 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/Utils.scala b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/Utils.scala new file mode 100644 index 00000000..66e79192 --- /dev/null +++ b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/Utils.scala @@ -0,0 +1,1424 @@ +/** Author: Samuel Chassot + */ +package ch.epfl.lexer + +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.StaticChecks._ +import ch.epfl.lexer.ListUtils.lemmaSubseqRefl + +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 + 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 + 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)) + lemmaContainsThenExists(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)) + lemmaContainsThenExists(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)) { + lemmaContainsThenExists(s1, witness, a => f(a).contains(b)) + assert(s1.exists(a => f(a).contains(b))) + check(false) + } else { + lemmaContainsThenExists(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)) + + @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 + @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)) + + @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(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)) + 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 = { + require(s.exists(p)) + ListUtils.getWitness(s.toList, p) + }.ensuring(res => p(res) && s.contains(res) && s.exists(p)) + + @opaque + @ghost + @inlineOnce + def lemmaContainsThenExists[A](s: Set[A], e: A, p: A => Boolean): Unit = { + require(s.contains(e)) + require(p(e)) + ListUtils.lemmaContainsThenExists(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)) + lemmaContainsThenExists(s1 ++ s2, witness, p) + } + if (s2.exists(p)){ + val witness = getWitness(s2, p) + assert(s2.contains(witness)) + assert((s1 ++ s2).contains(witness)) + lemmaContainsThenExists(s1 ++ s2, witness, p) + } + }.ensuring(_ => !s1.exists(p) && !s2.exists(p)) + + @ghost + @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 + @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)) + + @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 { + def isPrefix[B](prefix: List[B], l: List[B]): Boolean = { + decreases(prefix) + (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) + decreases(l) + 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(lTot) + (l1, lTot) match { + case (Nil(), _) => true + case (Cons(hd1, tl1), Cons(hdTot, tlTot)) if hd1 == hdTot => consecutiveSubseqAtHead(tl1, tlTot) + case _ => false + } + } + + @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) && l.exists(p)) + + @ghost + @opaque + @inlineOnce + 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) => lemmaContainsThenExists(tl, e, p) + case Nil() => check(false) + } + }.ensuring(_ => l.exists(p)) + + @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 + @ghost + 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 + @ghost + def lemmaContainsAndNotHdThenTlContains[B](l: List[B], e: B): Unit = { + require(l.contains(e)) + require(l.head != e) + + }.ensuring (_ => l.tail.contains(e)) + + @inlineOnce + @opaque + @ghost + 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 + @ghost + 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 + @ghost + 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 + @ghost + def lemmaNotHeadSoGetIndexTailIsMinusOne[B](l: List[B], e: B): Unit = { + require(l.contains(e)) + require(l.head != e) + decreases(l) + + if (l.tail.head != e) { + lemmaNotHeadSoGetIndexTailIsMinusOne(l.tail, e) + } + }.ensuring (_ => getIndex(l, e) == getIndex(l.tail, e) + 1) + + @inlineOnce + @opaque + @ghost + 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 + @ghost + def lemmaConcatTwoListThenFirstIsPrefix[B](l1: List[B], l2: List[B]): Unit = { + decreases(l1.size) + l1 match { + case Cons(hd, tl) => lemmaConcatTwoListThenFirstIsPrefix(tl, l2) + case Nil() => () + } + }.ensuring (_ => isPrefix(l1, l1 ++ l2)) + + @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) + 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 + @ghost + 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 + @ghost + 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 + @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 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 + 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 + 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 + @ghost + 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 + @ghost + def lemmaPrefixStaysPrefixWhenAddingToSuffix[B](p: List[B], l: List[B], suffix: List[B]): Unit = { + require(isPrefix(p, l)) + decreases(p) + p match { + case Cons(hd, tl) => lemmaPrefixStaysPrefixWhenAddingToSuffix(tl, l.tail, suffix) + case Nil() => () + } + }.ensuring (_ => isPrefix(p, l ++ suffix)) + + @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)) + 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 + @ghost + 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 + @ghost + 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 + @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)) + 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 + @ghost + 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 + @ghost + 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 + @ghost + 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 + @ghost + 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 + @ghost + 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) => { + 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 + @ghost + 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 + @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) + (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 + @ghost + 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 (_ => ListSpecs.subseq(l1, l2 ++ l3)) + + @inlineOnce + @opaque + @ghost + 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 + 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 + @ghost + 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 + @ghost + 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 + @ghost + 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 + @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 + @ghost + 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 + @ghost + 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 + @ghost + 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) + case Cons(hd, tl) => lemmaConcatTwoListsWhichNotContainThenTotNotContain(tl, l2, b) + case Nil() => () + } + }.ensuring (_ => !(l1 ++ l2).contains(b)) + + @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) + require(l2 == l2Bis) + + }.ensuring (_ => l1Bis.forall(b => l2Bis.contains(b))) + + @inlineOnce + @opaque + @ghost + 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 + @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 + @ghost + 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) + + @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/VerifiedLexer.scala b/lexers/regex/verifiedlexer/src/main/scala/ch/epfl/lexer/VerifiedLexer.scala index 90eca502..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()) @@ -247,7 +248,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 4fcb5b5e..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 @@ -18,9 +18,10 @@ 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 { +object MemoisationRegex { import VerifiedRegex._ import VerifiedRegexMatcher._ @@ -34,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)) @@ -88,157 +91,2375 @@ object Memoisation { } })) - val _ = cache.update((r, c), res) - () + val _ = cache.update((r, c), res) + () + + }.ensuring (_ => validCacheMap(this.cache)) + + } +} + +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) + } + ) + } + + 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)) + + @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] + 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 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() + } + }.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)) + l match { + case Cons(hd, tl) if tl.isEmpty => hd + case Cons(hd, tl) => Concat(hd, generalisedConcat(tl)) + case Nil() => EmptyExpr() + } + }.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 { + case ElementMatch(c) => true + 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 + case EmptyLang() => true + } + + @ghost + 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) + }) + ) + + @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]() + 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) + } + } + + // @ghost + def isEmptyExpr[C](r: Regex[C]): Boolean = { + r match { + case EmptyExpr() => true + case _ => false + } + } + // @ghost + def isEmptyLang[C](r: Regex[C]): Boolean = { + r match { + case EmptyLang() => true + case _ => false + } + } + // @ghost + def isElementMatch[C](r: Regex[C]): Boolean = { + r match { + case ElementMatch(_) => true + case _ => false + } + } + @ghost + def elementMatchIsChar[C](r: Regex[C], c: C): Boolean = { + require(isElementMatch(r)) + r match { + case ElementMatch(cc) => c == cc + } + } + // @ghost + def isStar[C](r: Regex[C]): Boolean = { + r match { + case Star(_) => true + case _ => false + } + } + // @ghost + def isUnion[C](r: Regex[C]): Boolean = { + r match { + case Union(_, _) => true + case _ => false + } + } + @ghost + 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 + } + } + + // @ghost + def isConcat[C](r: Regex[C]): Boolean = { + r match { + case Concat(_, _) => true + case _ => false + } + } +} + +object ZipperRegex { + import VerifiedRegex.* + import VerifiedRegexMatcher.* + import stainless.lang.Set + import MemoisationZipper.* + + /** + * Context[C] represent sequences of expressions + * Zipper[C] are sets of Context[C], and they represent disjunctions of expressions + */ + 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 + inline def head: Regex[C] = { + require(!isEmpty) + exprs.head + } + inline def tail: Context[C] = { + 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]] + + @ghost + 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(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.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 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(1) + } + }.ensuring(res => res >= 0) + + @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))) + + @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) + 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() + } + } + + + def derivationStepZipperDown[C](expr: Regex[C], context: Context[C], a: C): Zipper[C] = { + require(validRegex(expr)) + 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, 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() + } + } + + // @inlineOnce + def derivationStepZipper[C](z: Zipper[C], a: C): Zipper[C] = { + z.flatMap(c => derivationStepZipperUp(c, a)) + } + + def nullableContext[C](c: Context[C]): Boolean = { + c.forall(r => nullable(r)) + } + def nullableZipper[C](z: Zipper[C]): Boolean = { + z.exists(c => nullableContext(c)) + } + + def matchZipper[C](z: Zipper[C], input: List[C]): Boolean = { + decreases(input.size) + 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)) + } + + // 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)) + + 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 + 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)) + + 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 ----------------------------------------------------------------------------------------------------- + + @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)) + + /** + * Main theorem to prove the equivalence between a regex and its corresponding zipper + */ + @ghost + @opaque + @inlineOnce // type Zipper[C] = Set[Context[C]] + 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(zipperDepthTotal(zl), s.size) + + mainMatchTheorem(r, s) + zl match { + case Cons(hd, tl) if tl.isEmpty => { + assert(r == generalisedConcat(hd.exprs)) + hd.exprs match { + case Cons(hExp, tlExp) => { + assert(r == generalisedUnion(unfocusZipperList(zl))) + 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)) + theoremZipperRegexEquiv(zR1, List(Context(List(r1))), r1, s) + theoremZipperRegexEquiv(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) => { + 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) + + 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(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))) + theoremZipperRegexEquiv(zR1, List(Context(List(r1, r2))), Concat(r1, r2), s) + theoremZipperRegexEquiv(zR2, List(Context(List(r2))), r2, s) + + + } 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 + 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 + 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))) + + } else { + assert(zDerivUp == zDerivDown) + assert(zDeriv == zDerivDown) + assert(matchZipper(zDerivDown, stl) == matchZipper(z, s)) + } + + r1 match { + case ElementMatch(c) if c == shd => { + assert(zDerivDown == Set(Context(tlExp))) + val zVirt = Set(Context(tlExp)) + theoremZipperRegexEquiv(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 + 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) + + 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)) + 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 + 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) + + 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)) + + check(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 + 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) + + 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)) + + check(matchR(r, s) == matchZipper(z, s)) + } + case Star(rInner) => { + assert(zDerivDown == derivationStepZipperDown(rInner, Context(Cons(Star(rInner), tlExp)), shd)) + + 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(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)))) + + theoremZipperRegexEquiv(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)) + + 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) + + 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) + + 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)) + 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)))) + + theoremZipperRegexEquiv(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)) + + } + + 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)) + theoremZipperRegexEquiv(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 _ => { + lemmaEmptyZipperMatchesNothing(zDerivDown, stl) + assert(zDerivDown == Set()) + mainMatchTheorem(r, s) + assert(r == Concat(r1, r2)) + mainMatchTheorem(r1, s) + mainMatchTheorem(r2, s) + + check(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)))) + + theoremZipperRegexEquiv(zTail, List(Context(tlExp)), generalisedConcat(tlExp), s) + assert(matchR(generalisedConcat(tlExp), s) == matchZipper(zTail, 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 { + 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)) + + // 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)) + + 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) + 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) + 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) + check(matchR(rR1, s)) + check(false) + } + check(!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) => { + 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))) + 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) + 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))) + 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)) + check(false) + } + check(!matchZipper(z, s)) + check(matchR(r, s) == matchZipper(z, s)) + } + } + } + } + case Nil() => { + assert(isEmptyLang(r)) + lemmaEmptyZipperMatchesNothing(z, s) + } + } + + + }.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 + * + * @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 + 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))) + + @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](z1: Zipper[C], ct2: Context[C], s: List[C]): Unit = { + require(matchZipper(appendTo(z1, ct2), s)) + decreases(s, zipperDepthTotal(z1.toList)) + 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 + assert(z1.contains(ct1)) + 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)) + 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 { + // ct1Hd is nullable but derivDown matches + 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) + + 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 + assert(matchZipper(derivationStepZipperDown(ct1Hd, ct1Tl, shd), s1)) + assert(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) + 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) + } + } else { + // ct1Hd is NOT nullable + val derivDown = derivationStepZipperDown(ct1Hd, ct1Tl.concat(ct2), shd) + 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 + assert(matchZipper(derivationStepZipperDown(ct1Hd, ct1Tl, shd), s1)) + assert(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)) + 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) + } + } + 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(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) + } + } + + + } + 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 + // 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 ----------------------------------------------------------------------------------------------------- + + @ghost + @opaque + @inlineOnce + def lemmaUnfocusPreservesNullability[C](r: Regex[C], z: Zipper[C]): Unit = { + require(validRegex(r)) + require(r == unfocusZipper(z.toList)) + decreases(regexDepth(r)) + + val reg = 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 => generalisedConcat(c.exprs) == witnessNullableReg)) + val witnessContext = SetUtils.getWitness(z, (c: Context[C]) => generalisedConcat(c.exprs) == witnessNullableReg) + assert(z.contains(witnessContext)) + assert(generalisedConcat(witnessContext.exprs) == witnessNullableReg) + assert(nullable(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(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) + } + + assert(!z.exists(c => nullableContext(c))) + assert(!nullableZipper(z)) + } + + + + }.ensuring(_ => nullable(r) == nullableZipper(z)) + + @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 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)){ + lemmaTotalDepthZipperLargerThanOfAnyContext(tl, c) + } + } + case Nil() => check(false) + } + + + }.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 + 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 + @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) + 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 + 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 + 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)) + - }.ensuring (_ => validCacheMap(this.cache)) + @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 => { + 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, 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)) + check(false) + } + assert(deriv.isEmpty) + lemmaEmptyZipperMatchesNothing(deriv, tl) + } + case Nil() => + check(!nullableContext(c)) + } + }.ensuring(_ => matchZipper(z, s) == (s == List(a))) -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) && validRegex(r) // && !isEmptyLang(r) - case Union(rOne, rTwo) => validRegex(rOne) && validRegex(rTwo) - case Concat(rOne, rTwo) => validRegex(rOne) && validRegex(rTwo) - case EmptyExpr() => true - case EmptyLang() => true - } + @ghost + @opaque + @inlineOnce + def lemmaZipperOfEmptyContextMatchesEmptyString[C](z: Zipper[C], s: List[C]): Unit = { + require(z == Set(Context(List()))) + decreases(s.size) + s match { + 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, 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(Context(List()))) + check(false) + } + check((deriv.isEmpty)) - // @ghost - 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) + lemmaEmptyZipperMatchesNothing(derivUp, tl) + case Nil() => + check(nullableContext(Context[C](List()))) } - }.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] + }.ensuring(_ => matchZipper(z, s) == s.isEmpty) - /** 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) + @ghost + @opaque + @inlineOnce + 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)) - 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) + @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(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)) - 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) - } - } - // @ghost - def isEmptyExpr[C](r: Regex[C]): Boolean = { - r match { - case EmptyExpr() => true - case _ => false - } - } - // @ghost - def isEmptyLang[C](r: Regex[C]): Boolean = { - r match { - case EmptyLang() => true - case _ => false - } - } - // @ghost - def isElementMatch[C](r: Regex[C]): Boolean = { - r match { - case ElementMatch(_) => true - case _ => false - } - } @ghost - def elementMatchIsChar[C](r: Regex[C], c: C): Boolean = { - require(isElementMatch(r)) - r match { - case ElementMatch(cc) => c == cc - } - } - // @ghost - def isStar[C](r: Regex[C]): Boolean = { - r match { - case Star(_) => true - case _ => false - } - } - // @ghost - def isUnion[C](r: Regex[C]): Boolean = { - r match { - case Union(_, _) => true - case _ => false + @opaque + @inlineOnce + def lemmaZipperOfEmptyExprMatchesOnlyEmptyString[C](z: Zipper[C], s: List[C]): Unit = { + require(unfocusZipper(z.toList) == 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) + } } - } - @ghost - 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 + 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) + } } - } + }.ensuring(_ => matchZipper(z, s) == s.isEmpty) - // @ghost - def isConcat[C](r: Regex[C]): Boolean = { - r match { - case Concat(_, _) => true - case _ => false - } - } } object VerifiedRegexMatcher { import VerifiedRegex._ import ListUtils._ - import Memoisation._ + import MemoisationRegex._ def derivativeStep[C](r: Regex[C], a: C): Regex[C] = { require(validRegex(r)) @@ -343,6 +2564,7 @@ object VerifiedRegexMatcher { } ) + def matchRMem[C](r: Regex[C], input: List[C])(implicit cache: Cache[C]): Boolean = { require(validRegex(r)) require(cache.valid) @@ -350,6 +2572,20 @@ 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)) + + 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)) + 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)) @@ -370,6 +2606,150 @@ 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) => + 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 => 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 => 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)) + mainMatchTheorem(r, s) + r match { + case Concat(hd, concatTl) => + assert(matchRSpec(r,s) == findConcatSeparation(hd, concatTl, Nil(), s, s).isDefined) + 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 + case Nil() => matchR(r, s) == s.isEmpty + }) + @ghost def matchRSpec[C](r: Regex[C], s: List[C]): Boolean = { require(validRegex(r)) @@ -496,16 +2876,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) { @@ -538,16 +2918,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) @@ -1003,6 +3383,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)) @@ -1380,6 +3825,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 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 diff --git a/lexers/regex/verifiedlexer/src/main/scala/test.worksheet.sc b/lexers/regex/verifiedlexer/src/main/scala/test.worksheet.sc index 4b41a5eb..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 0839d7e4..74b6c9d4 100644 --- a/lexers/regex/verifiedlexer/stainless.conf +++ b/lexers/regex/verifiedlexer/stainless.conf @@ -3,14 +3,13 @@ vc-cache = true # debug = ["verification", "smt"] -timeout = 180 +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" -compact = true diff --git a/lexers/regex/verifiedlexer/verify.sh b/lexers/regex/verifiedlexer/verify.sh index b281ce09..6323b4ec 100755 --- a/lexers/regex/verifiedlexer/verify.sh +++ b/lexers/regex/verifiedlexer/verify.sh @@ -1,8 +1,8 @@ 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._\ + -D-parallel=12 --functions=Memoisation._,VerifiedRegex_,ZipperRegex._,VerifiedRegexMatcher._,VerifiedLexer._,ListUtils._,SetUtils._\ $1 diff --git a/lexers/regex/verifiedlexer/verify_dev.sh b/lexers/regex/verifiedlexer/verify_dev.sh index 04aabbed..85427209 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/Utils.scala\ + src/main/scala/ch/epfl/map/*\ + --config-file=stainless.conf\ + -D-parallel=16\ + $1 diff --git a/run-one-test.sh b/run-one-test.sh index 12001f6a..7f30ba4b 100755 --- a/run-one-test.sh +++ b/run-one-test.sh @@ -51,6 +51,7 @@ function run_tests { if [ $ADMIT_VCS = true ]; then if [ $status -eq 0 ] || [ $status -eq 1 ]; then + cat stainless-stack-trace.txt echo "Stainless accepted project: $project." exit 0 else 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