From c8af5276d31adf4c03edd2c5cc3c461f71f863fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maximilian=20K=C3=B6hl?= Date: Fri, 2 Aug 2024 16:13:38 +0200 Subject: [PATCH] chore: add VTS synthesis tool --- Cargo.lock | 531 +- Cargo.toml | 1 + engine/crates/vts-synth/.gitignore | 1 + engine/crates/vts-synth/Cargo.toml | 28 + .../crates/vts-synth/models/coffee-machine.fm | 1 + .../vts-synth/models/coffee-machine.fts.xml | 22 + .../vts-synth/models/coffee-machine2.fts.xml | 23 + engine/crates/vts-synth/models/email.fm | 1 + engine/crates/vts-synth/models/email.fts.xml | 17 + engine/crates/vts-synth/models/simple.fts.xml | 29 + .../crates/vts-synth/models/vibes/README.md | 9 + .../crates/vts-synth/models/vibes/aerouc5.fm | 1 + .../vts-synth/models/vibes/aerouc5.fts.xml | 101 + .../crates/vts-synth/models/vibes/aerouc5.out | 57 + .../crates/vts-synth/models/vibes/aerouc5.tvl | 98 + .../models/vibes/claroline-pow.fts.xml | 11621 ++++++++++++++++ .../models/vibes/claroline-reduced.fm | 1 + .../vts-synth/models/vibes/claroline.fm | 1 + .../vts-synth/models/vibes/claroline.out | 596 + .../vts-synth/models/vibes/claroline.tvl | 190 + .../vts-synth/models/vibes/cpterminal.fm | 1 + .../vts-synth/models/vibes/cpterminal.fts.xml | 45 + .../vts-synth/models/vibes/cpterminal.out | 57 + .../vts-synth/models/vibes/cpterminal.tvl | 74 + .../crates/vts-synth/models/vibes/minepump.fm | 1 + .../vts-synth/models/vibes/minepump.fts.xml | 97 + .../vts-synth/models/vibes/minepump.out | 57 + .../vts-synth/models/vibes/minepump.tvl | 25 + engine/crates/vts-synth/models/vibes/svm.fm | 1 + .../crates/vts-synth/models/vibes/svm.fts.xml | 37 + engine/crates/vts-synth/models/vibes/svm.out | 57 + engine/crates/vts-synth/models/vibes/svm.tvl | 25 + engine/crates/vts-synth/src/algorithms.rs | 614 + .../vts-synth/src/algorithms/minimize.rs | 662 + engine/crates/vts-synth/src/cudd.rs | 345 + engine/crates/vts-synth/src/domains.rs | 523 + engine/crates/vts-synth/src/frontends/mod.rs | 4 + .../crates/vts-synth/src/frontends/vibes.rs | 132 + engine/crates/vts-synth/src/frontends/yaml.rs | 60 + engine/crates/vts-synth/src/lattice.rs | 299 + engine/crates/vts-synth/src/logic.rs | 3 + .../vts-synth/src/logic/propositional.rs | 170 + .../src/logic/propositional/parser.rs | 320 + engine/crates/vts-synth/src/main.rs | 71 + .../crates/vts-synth/src/synthesis/confmon.rs | 856 ++ engine/crates/vts-synth/src/synthesis/mod.rs | 3 + engine/crates/vts-synth/src/ts.rs | 819 ++ engine/crates/vts-synth/src/ts/algorithms.rs | 39 + .../crates/vts-synth/src/ts/algorithms/bfs.rs | 97 + .../crates/vts-synth/src/ts/algorithms/dfs.rs | 150 + engine/crates/vts-synth/src/ts/output.rs | 4 + engine/crates/vts-synth/src/ts/output/dot.rs | 111 + .../crates/vts-synth/src/ts/output/pseuco.rs | 156 + engine/crates/vts-synth/src/ts/traits.rs | 315 + engine/crates/vts-synth/src/ts/transposed.rs | 95 + engine/crates/vts-synth/src/ts/types.rs | 41 + engine/crates/vts-synth/src/ts_traits.rs | 165 + engine/rust-toolchain | 2 +- 58 files changed, 19757 insertions(+), 105 deletions(-) create mode 100644 engine/crates/vts-synth/.gitignore create mode 100644 engine/crates/vts-synth/Cargo.toml create mode 100644 engine/crates/vts-synth/models/coffee-machine.fm create mode 100644 engine/crates/vts-synth/models/coffee-machine.fts.xml create mode 100644 engine/crates/vts-synth/models/coffee-machine2.fts.xml create mode 100644 engine/crates/vts-synth/models/email.fm create mode 100644 engine/crates/vts-synth/models/email.fts.xml create mode 100644 engine/crates/vts-synth/models/simple.fts.xml create mode 100644 engine/crates/vts-synth/models/vibes/README.md create mode 100644 engine/crates/vts-synth/models/vibes/aerouc5.fm create mode 100644 engine/crates/vts-synth/models/vibes/aerouc5.fts.xml create mode 100644 engine/crates/vts-synth/models/vibes/aerouc5.out create mode 100644 engine/crates/vts-synth/models/vibes/aerouc5.tvl create mode 100644 engine/crates/vts-synth/models/vibes/claroline-pow.fts.xml create mode 100644 engine/crates/vts-synth/models/vibes/claroline-reduced.fm create mode 100644 engine/crates/vts-synth/models/vibes/claroline.fm create mode 100644 engine/crates/vts-synth/models/vibes/claroline.out create mode 100644 engine/crates/vts-synth/models/vibes/claroline.tvl create mode 100644 engine/crates/vts-synth/models/vibes/cpterminal.fm create mode 100644 engine/crates/vts-synth/models/vibes/cpterminal.fts.xml create mode 100644 engine/crates/vts-synth/models/vibes/cpterminal.out create mode 100644 engine/crates/vts-synth/models/vibes/cpterminal.tvl create mode 100644 engine/crates/vts-synth/models/vibes/minepump.fm create mode 100644 engine/crates/vts-synth/models/vibes/minepump.fts.xml create mode 100644 engine/crates/vts-synth/models/vibes/minepump.out create mode 100644 engine/crates/vts-synth/models/vibes/minepump.tvl create mode 100644 engine/crates/vts-synth/models/vibes/svm.fm create mode 100644 engine/crates/vts-synth/models/vibes/svm.fts.xml create mode 100644 engine/crates/vts-synth/models/vibes/svm.out create mode 100644 engine/crates/vts-synth/models/vibes/svm.tvl create mode 100644 engine/crates/vts-synth/src/algorithms.rs create mode 100644 engine/crates/vts-synth/src/algorithms/minimize.rs create mode 100644 engine/crates/vts-synth/src/cudd.rs create mode 100644 engine/crates/vts-synth/src/domains.rs create mode 100644 engine/crates/vts-synth/src/frontends/mod.rs create mode 100644 engine/crates/vts-synth/src/frontends/vibes.rs create mode 100644 engine/crates/vts-synth/src/frontends/yaml.rs create mode 100644 engine/crates/vts-synth/src/lattice.rs create mode 100644 engine/crates/vts-synth/src/logic.rs create mode 100644 engine/crates/vts-synth/src/logic/propositional.rs create mode 100644 engine/crates/vts-synth/src/logic/propositional/parser.rs create mode 100644 engine/crates/vts-synth/src/main.rs create mode 100644 engine/crates/vts-synth/src/synthesis/confmon.rs create mode 100644 engine/crates/vts-synth/src/synthesis/mod.rs create mode 100644 engine/crates/vts-synth/src/ts.rs create mode 100644 engine/crates/vts-synth/src/ts/algorithms.rs create mode 100644 engine/crates/vts-synth/src/ts/algorithms/bfs.rs create mode 100644 engine/crates/vts-synth/src/ts/algorithms/dfs.rs create mode 100644 engine/crates/vts-synth/src/ts/output.rs create mode 100644 engine/crates/vts-synth/src/ts/output/dot.rs create mode 100644 engine/crates/vts-synth/src/ts/output/pseuco.rs create mode 100644 engine/crates/vts-synth/src/ts/traits.rs create mode 100644 engine/crates/vts-synth/src/ts/transposed.rs create mode 100644 engine/crates/vts-synth/src/ts/types.rs create mode 100644 engine/crates/vts-synth/src/ts_traits.rs diff --git a/Cargo.lock b/Cargo.lock index 31fb4d2f..507b04e8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -32,28 +32,35 @@ dependencies = [ [[package]] name = "ahash" -version = "0.8.3" +version = "0.8.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c99f64d1e06488f620f932677e24bc6e2897582980441ae90a671415bd7ec2f" +checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" dependencies = [ "cfg-if", "getrandom", "once_cell", "version_check", + "zerocopy", ] +[[package]] +name = "allocator-api2" +version = "0.2.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f" + [[package]] name = "anstream" -version = "0.3.2" +version = "0.6.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ca84f3628370c59db74ee214b3263d58f9aadd9b4fe7e711fd87dc452b7f163" +checksum = "64e15c1ab1f89faffbf04a634d5e1962e9074f2741eef6d97f3c4e322426d526" dependencies = [ "anstyle", "anstyle-parse", "anstyle-query", "anstyle-wincon", "colorchoice", - "is-terminal", + "is_terminal_polyfill", "utf8parse", ] @@ -83,12 +90,12 @@ dependencies = [ [[package]] name = "anstyle-wincon" -version = "1.0.1" +version = "3.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "180abfa45703aebe0093f79badacc01b8fd4ea2e35118747e5811127f926e188" +checksum = "5bf74e1b6e971609db8ca7a9ce79fd5768ab6ae46441c572e46cf596f59e57f8" dependencies = [ "anstyle", - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] @@ -97,6 +104,16 @@ version = "1.0.71" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c7d0618f0e0b7e8ff11427422b64564d5fb0be1940354bfe2e0529b18a9d9b8" +[[package]] +name = "ariadne" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72fe02fc62033df9ba41cba57ee19acf5e742511a140c7dbc3a873e19a19a1bd" +dependencies = [ + "unicode-width", + "yansi", +] + [[package]] name = "atty" version = "0.2.14" @@ -114,6 +131,15 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" +[[package]] +name = "autotools" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef941527c41b0fc0dd48511a8154cd5fc7e29200a0ff8b7203c5d777dbc795cf" +dependencies = [ + "cc", +] + [[package]] name = "base64" version = "0.21.2" @@ -127,16 +153,25 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" [[package]] -name = "bitflags" -version = "1.3.2" +name = "bit-set" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" +checksum = "0700ddab506f33b20a03b13996eccd309a48e5ff77d0d95926aa0210fb4e95f1" +dependencies = [ + "bit-vec", +] + +[[package]] +name = "bit-vec" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb" [[package]] name = "bitflags" -version = "2.3.3" +version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "630be753d4e58660abd17930c71b647fe46c27ea6b63cc59e1e3851406972e42" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitmaps" @@ -204,6 +239,16 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "chumsky" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8eebd66744a15ded14960ab4ccdbfb51ad3b81f51f3f04a80adac98c985396c9" +dependencies = [ + "hashbrown 0.14.5", + "stacker", +] + [[package]] name = "cipher" version = "0.4.4" @@ -221,45 +266,46 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4ea181bf566f71cb9a5d17a59e1871af638180a18fb0035c92ae62b705207123" dependencies = [ "atty", - "bitflags 1.3.2", + "bitflags", "clap_lex 0.2.4", - "indexmap", - "strsim", + "indexmap 1.9.3", + "strsim 0.10.0", "termcolor", "textwrap", ] [[package]] name = "clap" -version = "4.3.11" +version = "4.5.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1640e5cc7fb47dbb8338fd471b105e7ed6c3cb2aeb00c2e067127ffd3764a05d" +checksum = "0fbb260a053428790f3de475e304ff84cdbc4face759ea7a3e64c1edd938a7fc" dependencies = [ "clap_builder", + "clap_derive", ] [[package]] name = "clap_builder" -version = "4.3.11" +version = "4.5.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "98c59138d527eeaf9b53f35a77fcc1fad9d883116070c63d5de1c7dc7b00c72b" +checksum = "64b17d7ea74e9f833c7dbf2cbe4fb12ff26783eda4782a8975b72f895c9b4d99" dependencies = [ "anstream", "anstyle", - "clap_lex 0.5.0", - "strsim", + "clap_lex 0.7.2", + "strsim 0.11.1", ] [[package]] name = "clap_derive" -version = "4.3.2" +version = "4.5.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8cd2b2a819ad6eec39e8f1d6b53001af1e5469f8c177579cdaeb313115b825f" +checksum = "501d359d5f3dcaf6ecdeee48833ae73ec6e42723a1e52419c79abf9507eec0a0" dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.16", + "syn 2.0.72", ] [[package]] @@ -273,9 +319,9 @@ dependencies = [ [[package]] name = "clap_lex" -version = "0.5.0" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2da6da31387c7e4ef160ffab6d5e7f00c42626fe39aea70a7b0f1773f7dd6c1b" +checksum = "1462739cb27611015575c0c11df5df7601141071f07518d56fcc1be504cbec97" [[package]] name = "clock-zones" @@ -402,6 +448,16 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "cudd-sys" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da04b991245abeb377212488dd81428ec1f8e99522c16e859ff42b2999f209ea" +dependencies = [ + "autotools", + "libc", +] + [[package]] name = "derive_more" version = "0.99.17" @@ -445,25 +501,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f" [[package]] -name = "errno" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4bcfec3a70f97c962c307b2d2c56e358cf1d00b558d74262b5f929ee8cc7e73a" -dependencies = [ - "errno-dragonfly", - "libc", - "windows-sys 0.48.0", -] - -[[package]] -name = "errno-dragonfly" -version = "0.1.2" +name = "equivalent" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" -dependencies = [ - "cc", - "libc", -] +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" [[package]] name = "flate2" @@ -513,7 +554,7 @@ checksum = "e77ac7b51b8e6313251737fcef4b1c01a2ea102bde68415b62c0ee9268fec357" dependencies = [ "proc-macro2", "quote", - "syn 2.0.16", + "syn 2.0.72", ] [[package]] @@ -546,12 +587,25 @@ name = "hashbrown" version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" +dependencies = [ + "ahash 0.7.6", +] + +[[package]] +name = "hashbrown" +version = "0.14.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" +dependencies = [ + "ahash 0.8.11", + "allocator-api2", +] [[package]] name = "heck" -version = "0.4.1" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" [[package]] name = "hermit-abi" @@ -571,12 +625,6 @@ dependencies = [ "libc", ] -[[package]] -name = "hermit-abi" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "443144c8cdadd93ebf52ddb4056d257f5b52c04d3c804e657d19eb73fc33668b" - [[package]] name = "hmac" version = "0.12.1" @@ -621,6 +669,17 @@ dependencies = [ "serde", ] +[[package]] +name = "indexmap" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de3fc2e30ba82dd1b3911c8de1ffc143c74a914a14e99514d7637e3099df5ea0" +dependencies = [ + "equivalent", + "hashbrown 0.14.5", + "serde", +] + [[package]] name = "indicatif" version = "0.17.5" @@ -704,15 +763,10 @@ dependencies = [ ] [[package]] -name = "is-terminal" -version = "0.4.9" +name = "is_terminal_polyfill" +version = "1.70.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb0889898416213fab133e1d33a0e5858a48177452750691bde3666d0fdbaf8b" -dependencies = [ - "hermit-abi 0.3.2", - "rustix", - "windows-sys 0.48.0", -] +checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" [[package]] name = "itertools" @@ -765,12 +819,6 @@ version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f7012b1bbb0719e1097c47611d3898568c546d597c2e74d66f6087edd5233ff4" -[[package]] -name = "linux-raw-sys" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09fc20d2ca12cb9f044c93e3bd6d32d523e6e2ec3db4f7b2939cd99026ecd3f0" - [[package]] name = "lock_api" version = "0.4.9" @@ -797,6 +845,12 @@ dependencies = [ "rawpointer", ] +[[package]] +name = "memchr" +version = "2.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" + [[package]] name = "memoffset" version = "0.6.5" @@ -833,7 +887,7 @@ name = "momba-explore" version = "0.1.1" dependencies = [ "clock-zones", - "indexmap", + "indexmap 1.9.3", "itertools", "num-traits", "ordered-float", @@ -847,7 +901,7 @@ dependencies = [ name = "momba-sidekick" version = "0.1.0" dependencies = [ - "clap 4.3.11", + "clap 4.5.13", "clap_derive", "clock-zones", "hashbrown 0.11.2", @@ -859,8 +913,8 @@ dependencies = [ name = "momba-simulator" version = "0.1.0" dependencies = [ - "ahash 0.8.3", - "clap 4.3.11", + "ahash 0.8.11", + "clap 4.5.13", "clap_derive", "clock-zones", "hashbrown 0.11.2", @@ -879,7 +933,7 @@ version = "0.0.0" dependencies = [ "clock-zones", "downcast-rs", - "hashbrown 0.3.1", + "hashbrown 0.12.3", "momba-explore", "ordered-float", "pyo3 0.17.3", @@ -900,6 +954,16 @@ dependencies = [ "rawpointer", ] +[[package]] +name = "nu-ansi-term" +version = "0.46.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" +dependencies = [ + "overload", + "winapi", +] + [[package]] name = "num-complex" version = "0.4.3" @@ -947,9 +1011,9 @@ checksum = "830b246a0e5f20af87141b25c173cd1b609bd7779a4617d6ec582abaf90870f3" [[package]] name = "once_cell" -version = "1.17.1" +version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" [[package]] name = "ordered-float" @@ -968,6 +1032,12 @@ version = "6.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ceedf44fb00f2d1984b0bc98102627ce622e083e49a5bacdb3e514fa4238e267" +[[package]] +name = "overload" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" + [[package]] name = "parking_lot" version = "0.11.2" @@ -1064,6 +1134,12 @@ version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94" +[[package]] +name = "pin-project-lite" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" + [[package]] name = "pkg-config" version = "0.3.27" @@ -1090,13 +1166,22 @@ checksum = "dc375e1527247fe1a97d8b7156678dfe7c1af2fc075c9a4db3690ecd2a148068" [[package]] name = "proc-macro2" -version = "1.0.58" +version = "1.0.86" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa1fb82fc0c281dd9671101b66b771ebbe1eaf967b96ac8740dcba4b70005ca8" +checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" dependencies = [ "unicode-ident", ] +[[package]] +name = "psm" +version = "0.1.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5787f7cda34e3033a72192c018bc5883100330f362ef279a8cbccfce8bb4e874" +dependencies = [ + "cc", +] + [[package]] name = "pyo3" version = "0.13.2" @@ -1213,11 +1298,21 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "quick-xml" +version = "0.34.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f24d770aeca0eacb81ac29dfbc55ebcc09312fdd1f8bbecdc7e4a84e000e3b4" +dependencies = [ + "memchr", + "serde", +] + [[package]] name = "quote" -version = "1.0.27" +version = "1.0.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f4f29d145265ec1c483c7c654450edde0bfe043d3938d6972630663356d9500" +checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" dependencies = [ "proc-macro2", ] @@ -1307,7 +1402,7 @@ version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" dependencies = [ - "bitflags 1.3.2", + "bitflags", ] [[package]] @@ -1335,7 +1430,7 @@ dependencies = [ "derive_more", "hashbrown 0.3.1", "im", - "indexmap", + "indexmap 1.9.3", "momba-explore", "ordered-float", "rand", @@ -1354,19 +1449,6 @@ dependencies = [ "semver", ] -[[package]] -name = "rustix" -version = "0.38.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac5ffa1efe7548069688cd7028f32591853cd7b5b756d41bcffd2353e4fc75b4" -dependencies = [ - "bitflags 2.3.3", - "errno", - "libc", - "linux-raw-sys", - "windows-sys 0.48.0", -] - [[package]] name = "rustls" version = "0.21.3" @@ -1429,22 +1511,22 @@ checksum = "bebd363326d05ec3e2f532ab7660680f3b02130d780c299bca73469d521bc0ed" [[package]] name = "serde" -version = "1.0.163" +version = "1.0.204" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2113ab51b87a539ae008b5c6c02dc020ffa39afd2d83cffcb3f4eb2722cebec2" +checksum = "bc76f558e0cbb2a839d37354c575f1dc3fdc6546b5be373ba43d95f231bf7c12" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.163" +version = "1.0.204" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c805777e3930c8883389c602315a24224bcc738b63905ef87cd1420353ea93e" +checksum = "e0cd7e117be63d3c3678776753929474f3b04a43a080c744d6b0ae2a8c28e222" dependencies = [ "proc-macro2", "quote", - "syn 2.0.16", + "syn 2.0.72", ] [[package]] @@ -1458,6 +1540,19 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_yaml" +version = "0.9.34+deprecated" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a8b1a1a2ebf674015cc02edccce75287f1a0130d394307b36743c2f5d504b47" +dependencies = [ + "indexmap 2.3.0", + "itoa", + "ryu", + "serde", + "unsafe-libyaml", +] + [[package]] name = "sha1" version = "0.10.5" @@ -1480,6 +1575,15 @@ dependencies = [ "digest", ] +[[package]] +name = "sharded-slab" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" +dependencies = [ + "lazy_static", +] + [[package]] name = "sized-chunks" version = "0.6.5" @@ -1502,12 +1606,31 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" +[[package]] +name = "stacker" +version = "0.1.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c886bd4480155fd3ef527d45e9ac8dd7118a898a46530b7b94c3e21866259fce" +dependencies = [ + "cc", + "cfg-if", + "libc", + "psm", + "winapi", +] + [[package]] name = "strsim" version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + [[package]] name = "subtle" version = "2.5.0" @@ -1527,9 +1650,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.16" +version = "2.0.72" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6f671d4b5ffdb8eadec19c0ae67fe2639df8684bd7bc4b83d986b8db549cf01" +checksum = "dc4b9b9bf2add8093d3f2c0204471e951b2285580335de42f9d2534f3ae7a8af" dependencies = [ "proc-macro2", "quote", @@ -1590,7 +1713,17 @@ checksum = "f9456a42c5b0d803c8cd86e73dd7cc9edd429499f37a3550d286d5e86720569f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.16", + "syn 2.0.72", +] + +[[package]] +name = "thread_local" +version = "1.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c" +dependencies = [ + "cfg-if", + "once_cell", ] [[package]] @@ -1646,6 +1779,63 @@ dependencies = [ "zip", ] +[[package]] +name = "tracing" +version = "0.1.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" +dependencies = [ + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.72", +] + +[[package]] +name = "tracing-core" +version = "0.1.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" +dependencies = [ + "once_cell", + "valuable", +] + +[[package]] +name = "tracing-log" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" +dependencies = [ + "log", + "once_cell", + "tracing-core", +] + +[[package]] +name = "tracing-subscriber" +version = "0.3.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b" +dependencies = [ + "nu-ansi-term", + "sharded-slab", + "smallvec", + "thread_local", + "tracing-core", + "tracing-log", +] + [[package]] name = "typenum" version = "1.16.0" @@ -1685,6 +1875,12 @@ version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e1766d682d402817b5ac4490b3c3002d91dfa0d22812f341609f97b08757359c" +[[package]] +name = "unsafe-libyaml" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "673aac59facbab8a9007c7f6108d11f63b603f7cabff99fabf650fea5c32b861" + [[package]] name = "untrusted" version = "0.7.1" @@ -1724,12 +1920,40 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" +[[package]] +name = "valuable" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" + [[package]] name = "version_check" version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" +[[package]] +name = "vts-synth" +version = "0.1.0" +dependencies = [ + "ariadne", + "bit-set", + "chumsky", + "clap 4.5.13", + "cudd-sys", + "hashbrown 0.14.5", + "im", + "indexmap 2.3.0", + "quick-xml", + "rand", + "serde", + "serde_json", + "serde_yaml", + "thiserror", + "tracing", + "tracing-subscriber", +] + [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" @@ -1757,7 +1981,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.16", + "syn 2.0.72", "wasm-bindgen-shared", ] @@ -1779,7 +2003,7 @@ checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.16", + "syn 2.0.72", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -1858,6 +2082,15 @@ dependencies = [ "windows-targets 0.48.1", ] +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets 0.52.6", +] + [[package]] name = "windows-targets" version = "0.42.2" @@ -1888,6 +2121,22 @@ dependencies = [ "windows_x86_64_msvc 0.48.0", ] +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", + "windows_i686_gnullvm", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", +] + [[package]] name = "windows_aarch64_gnullvm" version = "0.42.2" @@ -1900,6 +2149,12 @@ version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + [[package]] name = "windows_aarch64_msvc" version = "0.42.2" @@ -1912,6 +2167,12 @@ version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + [[package]] name = "windows_i686_gnu" version = "0.42.2" @@ -1924,6 +2185,18 @@ version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + [[package]] name = "windows_i686_msvc" version = "0.42.2" @@ -1936,6 +2209,12 @@ version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + [[package]] name = "windows_x86_64_gnu" version = "0.42.2" @@ -1948,6 +2227,12 @@ version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + [[package]] name = "windows_x86_64_gnullvm" version = "0.42.2" @@ -1960,6 +2245,12 @@ version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + [[package]] name = "windows_x86_64_msvc" version = "0.42.2" @@ -1972,6 +2263,38 @@ version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "yansi" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09041cd90cf85f7f8b2df60c646f853b7f535ce68f85244eb6731cf89fa498ec" + +[[package]] +name = "zerocopy" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.72", +] + [[package]] name = "zip" version = "0.6.6" diff --git a/Cargo.toml b/Cargo.toml index bbed3202..3ffbdf65 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,6 +10,7 @@ members = [ "engine/crates/pyo3-generic-wrap/example", "engine/crates/robust-diagnosis", "engine/crates/momba-simulator", + "engine/crates/vts-synth", ] exclude = ["private"] diff --git a/engine/crates/vts-synth/.gitignore b/engine/crates/vts-synth/.gitignore new file mode 100644 index 00000000..c585e193 --- /dev/null +++ b/engine/crates/vts-synth/.gitignore @@ -0,0 +1 @@ +out \ No newline at end of file diff --git a/engine/crates/vts-synth/Cargo.toml b/engine/crates/vts-synth/Cargo.toml new file mode 100644 index 00000000..9fb38357 --- /dev/null +++ b/engine/crates/vts-synth/Cargo.toml @@ -0,0 +1,28 @@ +[package] +name = "vts-synth" +description = "VTS synthesis tool." +version = "0.1.0" +edition = "2021" +publish = false + +[features] +disable-determinize = [] +disable-projection = [] + +[dependencies] +ariadne = "0.3" +bit-set = "0.5" +chumsky = "0.9" +clap = { version = "4.4", features = ["derive"] } +cudd-sys = "1.0" +hashbrown = { version = "0.14", features = ["raw"] } +im = "15.1" +indexmap = { version = "2.0", features = ["serde"] } +quick-xml = { version = "0.34", features = ["serialize"] } +rand = "0.8" +serde = { version = "1.0", features = ["derive", "rc"] } +serde_json = "1.0" +serde_yaml = "0.9" +thiserror = "1.0" +tracing = { version = "0.1.37", features = ["release_max_level_info"] } +tracing-subscriber = "0.3.17" diff --git a/engine/crates/vts-synth/models/coffee-machine.fm b/engine/crates/vts-synth/models/coffee-machine.fm new file mode 100644 index 00000000..f32a5804 --- /dev/null +++ b/engine/crates/vts-synth/models/coffee-machine.fm @@ -0,0 +1 @@ +true \ No newline at end of file diff --git a/engine/crates/vts-synth/models/coffee-machine.fts.xml b/engine/crates/vts-synth/models/coffee-machine.fts.xml new file mode 100644 index 00000000..c421d793 --- /dev/null +++ b/engine/crates/vts-synth/models/coffee-machine.fts.xml @@ -0,0 +1,22 @@ + + + idle + + + + + + + + + + + + + + + + + + + diff --git a/engine/crates/vts-synth/models/coffee-machine2.fts.xml b/engine/crates/vts-synth/models/coffee-machine2.fts.xml new file mode 100644 index 00000000..6ce1ec9b --- /dev/null +++ b/engine/crates/vts-synth/models/coffee-machine2.fts.xml @@ -0,0 +1,23 @@ + + + idle + + + + + + + + + + + + + + + + + + + + diff --git a/engine/crates/vts-synth/models/email.fm b/engine/crates/vts-synth/models/email.fm new file mode 100644 index 00000000..cdf0b2e9 --- /dev/null +++ b/engine/crates/vts-synth/models/email.fm @@ -0,0 +1 @@ +(s && !e) || (e && !s) || (s && e) \ No newline at end of file diff --git a/engine/crates/vts-synth/models/email.fts.xml b/engine/crates/vts-synth/models/email.fts.xml new file mode 100644 index 00000000..3031efd8 --- /dev/null +++ b/engine/crates/vts-synth/models/email.fts.xml @@ -0,0 +1,17 @@ + + + idle + + + + + + + + + + + + + + diff --git a/engine/crates/vts-synth/models/simple.fts.xml b/engine/crates/vts-synth/models/simple.fts.xml new file mode 100644 index 00000000..225edb7c --- /dev/null +++ b/engine/crates/vts-synth/models/simple.fts.xml @@ -0,0 +1,29 @@ + + + s1 + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/engine/crates/vts-synth/models/vibes/README.md b/engine/crates/vts-synth/models/vibes/README.md new file mode 100644 index 00000000..bfde8411 --- /dev/null +++ b/engine/crates/vts-synth/models/vibes/README.md @@ -0,0 +1,9 @@ +# VIBeS FTS Models + +The models in this directory are from the [VIBeS project](https://projects.info.unamur.be/vibes/index.html) of [UNamur](https://www.unamur.be/en/). + +- [Aero UC5](https://projects.info.unamur.be/vibes/case-study-aerouc5.html) ([`aerouc5.fts.xml`](aerouc5.fts.xml)) +- [Soda Vending Machine](https://projects.info.unamur.be/vibes/case-study-svm.html) ([`svm.fts.xml`](svm.fts.xml)) +- [Claroline](https://projects.info.unamur.be/vibes/case-study-claroline.html) ([`claroline-pow.fts.xml`](claroline-pow.fts.xml)) +- [Card Payment Terminal](https://projects.info.unamur.be/vibes/case-study-cpterminal.html) ([`cpterminal.fts.xml`](cpterminal.fts.xml)) +- [Minepump](https://projects.info.unamur.be/vibes/case-study-minepump.html) ([`minepump.fts.xml`](minepump.fts.xml)) diff --git a/engine/crates/vts-synth/models/vibes/aerouc5.fm b/engine/crates/vts-synth/models/vibes/aerouc5.fm new file mode 100644 index 00000000..cd409f88 --- /dev/null +++ b/engine/crates/vts-synth/models/vibes/aerouc5.fm @@ -0,0 +1 @@ +(!Check_for_obstacles || OWS) && (!Mark_landing_position || HMS_D) && (!Mark_landing_position || HOCAS) && (!Display_real_reference_objects || OWS) && (!SI_sensor_based || OWS) && (!SI_from_DB || Database) && (!Provide_slope_indication_for_LP || Mark_landing_position) && (!AeroUc5 || HOCAS) && (!AeroUc5 || Mark_landing_position) && (!AeroUc5 || Database) && (!AeroUc5 || HMS_D) && (!AeroUc5 || OWS) && (!AeroUc5 || Display_reference_objects_in_landing_zone) && (!AeroUc5 || Provide_slope_indication_for_LP) && (HOCAS_GE_Aviation_Systems || x29 || !HOCAS) && (HOCAS_Honeywell || !x29 || !HOCAS) && (!HOCAS_GE_Aviation_Systems || x30 || !HOCAS) && (!HOCAS_Honeywell || !x30 || !HOCAS) && (!HOCAS_GE_Aviation_Systems || HOCAS) && (!HOCAS_Honeywell || HOCAS) && (!HOCAS || AeroUc5) && (!Mark_landing_position || Mark_LP) && (!Mark_landing_position || ArtificialParentCheck_for_obstacles) && (!Mark_landing_position || Check_for_no_ground) && (Mark_LP_by_handling_pilot_only || x31 || !Mark_LP) && (Mark_LP_by_both_pilots || !x31 || !Mark_LP) && (!Mark_LP_by_handling_pilot_only || x32 || !Mark_LP) && (!Mark_LP_by_both_pilots || !x32 || !Mark_LP) && (!Mark_LP_by_handling_pilot_only || Mark_LP) && (!Mark_LP_by_both_pilots || Mark_LP) && (!Mark_LP || Mark_landing_position) && (!Check_for_obstacles || ArtificialParentCheck_for_obstacles) && (!ArtificialParentCheck_for_obstacles || Mark_landing_position) && (!Check_for_no_ground || Mark_landing_position) && (!Mark_landing_position || AeroUc5) && (DB_provided_by_customer || x33 || !Database) && (DB_provided_by_Cassidian || !x33 || !Database) && (!DB_provided_by_customer || x34 || !Database) && (!DB_provided_by_Cassidian || !x34 || !Database) && (!DB_provided_by_customer || Database) && (!DB_provided_by_Cassidian || Database) && (!Database || AeroUc5) && (HMS_D_Elbit || x35 || !HMS_D) && (HMS_D_Thales || !x35 || !HMS_D) && (!HMS_D_Elbit || x36 || !HMS_D) && (!HMS_D_Thales || !x36 || !HMS_D) && (!HMS_D_Elbit || HMS_D) && (!HMS_D_Thales || HMS_D) && (!HMS_D || AeroUc5) && (HELLAS || x37 || !OWS) && (ELOP || !x37 || !OWS) && (!HELLAS || x38 || !OWS) && (!ELOP || !x38 || !OWS) && (!HELLAS || OWS) && (!ELOP || OWS) && (!OWS || AeroUc5) && (!Display_reference_objects_in_landing_zone || ArtificialParentDisplay_real_reference_objects) && (!Display_reference_objects_in_landing_zone || Display_visual_3D_cues) && (!Display_real_reference_objects || ArtificialParentDisplay_real_reference_objects) && (!ArtificialParentDisplay_real_reference_objects || Display_reference_objects_in_landing_zone) && (!Display_visual_3D_cues || Display_reference_objects_in_landing_zone) && (!Display_reference_objects_in_landing_zone || AeroUc5) && (SI_sensor_based || x39 || !Provide_slope_indication_for_LP) && (SI_from_DB || !x39 || !Provide_slope_indication_for_LP) && (!SI_sensor_based || x40 || !Provide_slope_indication_for_LP) && (!SI_from_DB || !x40 || !Provide_slope_indication_for_LP) && (!SI_sensor_based || Provide_slope_indication_for_LP) && (!SI_from_DB || Provide_slope_indication_for_LP) && (!Provide_slope_indication_for_LP || AeroUc5) && (AeroUc5) \ No newline at end of file diff --git a/engine/crates/vts-synth/models/vibes/aerouc5.fts.xml b/engine/crates/vts-synth/models/vibes/aerouc5.fts.xml new file mode 100644 index 00000000..dfb88236 --- /dev/null +++ b/engine/crates/vts-synth/models/vibes/aerouc5.fts.xml @@ -0,0 +1,101 @@ + + s0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/engine/crates/vts-synth/models/vibes/aerouc5.out b/engine/crates/vts-synth/models/vibes/aerouc5.out new file mode 100644 index 00000000..7f96b1d8 --- /dev/null +++ b/engine/crates/vts-synth/models/vibes/aerouc5.out @@ -0,0 +1,57 @@ +Features: {"Display_visual_3D_cues", "Display_real_reference_objects", "Check_for_no_ground", "Check_for_obstacles", "OWS", "Mark_landing_position", "HMS_D", "HOCAS", "SI_sensor_based", "SI_from_DB", "Database", "Provide_slope_indication_for_LP", "AeroUc5", "Display_reference_objects_in_landing_zone", "HOCAS_GE_Aviation_Systems", "x29", "HOCAS_Honeywell", "x30", "Mark_LP", "ArtificialParentCheck_for_obstacles", "Mark_LP_by_handling_pilot_only", "x31", "Mark_LP_by_both_pilots", "x32", "DB_provided_by_customer", "x33", "DB_provided_by_Cassidian", "x34", "HMS_D_Elbit", "x35", "HMS_D_Thales", "x36", "HELLAS", "x37", "ELOP", "x38", "ArtificialParentDisplay_real_reference_objects", "x39", "x40"} +Observables: {"activate", "Landing_and_touchdown_for_more_than_5_sec", "Depart_from_landing_position", "deactivate", "Virtual_3D_cues_displayed", "Real_objects_displayed", "Approach_to_landing_position", "Trigger_mark_landing_position", "Provide_landing_position_not_on_ground", "Provide_landing_position_with_obstacle", "Provide_valid_landing_position"} + +States: 25 (initial: 1) +Transitions: 46 +SCCs: 1 + +== Initial VTS == +States: 108 (initial: 1) +Transitions: 208 +SCCs: 14 +Deterministic: false +Monotonic: true + +== Refined VTS == +States: 106 (initial: 1) +Transitions: 204 +SCCs: 12 +Deterministic: false +Monotonic: true + +== Observability Projection == +States: 75 (initial: 1) +Transitions: 280 +SCCs: 9 +Deterministic: false +Monotonic: true + +== Determinize == +States: 85 (initial: 1) +Transitions: 241 +SCCs: 12 + +== Flatten Beliefs == +States: 85 (initial: 1) +Transitions: 241 +SCCs: 12 +Deterministic: true +Monotonic: true + +== Compress SCCs == +States: 48 (initial: 1) +Transitions: 152 +SCCs: 12 +Deterministic: true +Monotonic: true + +== Compress Transitions == +States: 45 (initial: 1) +Transitions: 152 +SCCs: 11 +Deterministic: true +Monotonic: true + +real 0m0.126s +user 0m0.090s +sys 0m0.041s diff --git a/engine/crates/vts-synth/models/vibes/aerouc5.tvl b/engine/crates/vts-synth/models/vibes/aerouc5.tvl new file mode 100644 index 00000000..294009ef --- /dev/null +++ b/engine/crates/vts-synth/models/vibes/aerouc5.tvl @@ -0,0 +1,98 @@ +// Feature model corresponding to the SferiAssist.vedit model + +root AeroUc5 { + +// Check_for_obstacles && Check_for_no_ground && Display_visual_3D_cues && Display_real_reference_objects; + +// Display_visual_3D_cues && Display_real_reference_objects; + + group allof { + Display_reference_objects_in_landing_zone, + Mark_landing_position, + HOCAS, + HMS_D, + Database, + OWS, + Provide_slope_indication_for_LP + } +} + +root Display_reference_objects_in_landing_zone{ + group allof{ + Display_visual_3D_cues, + opt Display_real_reference_objects + } +} + +root Display_real_reference_objects{ + Display_real_reference_objects -> OWS; +} + +root Mark_landing_position{ + group allof{ + Check_for_no_ground, + opt Check_for_obstacles, + Mark_LP + } + Mark_landing_position -> HOCAS; + Mark_landing_position -> HMS_D; +} + + +root Check_for_obstacles{ + Check_for_obstacles -> OWS; +} + + +root Mark_LP{ + group oneof{ + Mark_LP_by_handling_pilot_only, + Mark_LP_by_both_pilots + } +} + + + +root HOCAS{ + group oneof{ + HOCAS_GE_Aviation_Systems, + HOCAS_Honeywell + } +} + +root HMS_D{ + group oneof{ + HMS_D_Thales, + HMS_D_Elbit + } +} + +root Database{ + group oneof{ + DB_provided_by_customer, + DB_provided_by_Cassidian + } +} + +root OWS{ + group oneof{ + ELOP, + HELLAS + } +} + +root Provide_slope_indication_for_LP{ + group oneof{ + SI_sensor_based, + SI_from_DB + } + Provide_slope_indication_for_LP -> Mark_landing_position; +} + +root SI_from_DB{ + SI_from_DB -> Database; +} + +root SI_sensor_based{ + SI_sensor_based -> OWS; +} \ No newline at end of file diff --git a/engine/crates/vts-synth/models/vibes/claroline-pow.fts.xml b/engine/crates/vts-synth/models/vibes/claroline-pow.fts.xml new file mode 100644 index 00000000..59c512b9 --- /dev/null +++ b/engine/crates/vts-synth/models/vibes/claroline-pow.fts.xml @@ -0,0 +1,11621 @@ + + + 0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/engine/crates/vts-synth/models/vibes/claroline-reduced.fm b/engine/crates/vts-synth/models/vibes/claroline-reduced.fm new file mode 100644 index 00000000..8ed72070 --- /dev/null +++ b/engine/crates/vts-synth/models/vibes/claroline-reduced.fm @@ -0,0 +1 @@ +!Admin && !Teacher && !RegisteredUser && !CourseLearnPath && !CourseForum && !CourseWork && (!Visibility || Course) && (!Course || Visibility) && (!Access || Course) && (!Course || Access) && (!Course || Tools) && (!Tools || Course) && (Admin || !x65 || !RegisteredUser) && (Admin || !x64 || x67 || !RegisteredUser) && (!x65 || !RegisteredUser) && (!x65 || x67 || !RegisteredUser) && (!Student || !x69 || !RegisteredUser) && (!Student || RegisteredUser) && (Student || !x67 || !RegisteredUser) && (Teacher || x64 || !RegisteredUser) && (!x64 || x66 || !RegisteredUser) && (Admin || x66 || !RegisteredUser) && (!Admin || !x68 || !RegisteredUser) && (!Teacher || x68 || !RegisteredUser) && (!x68 || x69 || !RegisteredUser) && (!Admin || x69 || !RegisteredUser) && (!CourseUser || ReactivableByOwner || Admin || RegisteredUser || RegisteredUser || PublicVisibility) && (!CourseUser || ReactivableByOwner || Admin || RegisteredUser || PublicVisibility || PublicVisibility) && (!CourseIndex || ReactivableByOwner || Admin || RegisteredUser || PublicVisibility) && (!CourseIndex || ReactivableByOwner || Admin || Available || AvailableFromTo || PublicVisibility) && (!CourseIndex || ReactivableByOwner || Admin || RegisteredUser || Available || AvailableFromTo) && (!CourseIndex || Teacher || Admin || RegisteredUser || Available || AvailableFromTo) && (!CourseUser || Teacher || Admin || Available || AvailableFromTo) && (!CourseUser || ReactivableByOwner || Admin || Available || AvailableFromTo) && (!CourseIndex || ReactivableByOwner || Admin || MembersVisibility || Available || AvailableFromTo) && (!CourseIndex || ReactivableByOwner || Admin || Available || AvailableFromTo || Available || AvailableFromTo) && (!CourseIndex || ReactivableByOwner || Admin || MembersVisibility || PublicVisibility) && (!CourseIndex || Teacher || Admin || MembersVisibility || PublicVisibility) && (!CourseIndex || Teacher || Admin || Available || AvailableFromTo || PublicVisibility) && (!CourseIndex || Teacher || Admin || MembersVisibility || Available || AvailableFromTo) && (!CourseIndex || Teacher || Admin || Available || AvailableFromTo || Available || AvailableFromTo) && (!CourseUser || Teacher || Admin || RegisteredUser || PublicVisibility || PublicVisibility) && (!CourseIndex || Teacher || Admin || RegisteredUser || PublicVisibility) && (!CourseUser || ArtificialParentCourseUser) && (!CourseUser || Teacher || Admin || RegisteredUser || RegisteredUser || PublicVisibility) && (!ArtificialParentCourseUser || Tools) && (!Tools || ArtificialParentCourseUser) && (!CourseIndex || Tools) && (!Tools || CourseIndex) && (!CourseDescription || Teacher || Admin || Available || AvailableFromTo) && (!CourseDescription || ReactivableByOwner || Admin || Available || AvailableFromTo) && (!CourseForum || ReactivableByOwner || Admin || Available || AvailableFromTo) && (!CourseForum || Teacher || Admin || Available || AvailableFromTo) && (!CourseAnnoucements || Teacher || Admin || Available || AvailableFromTo) && (!CourseAnnoucements || ReactivableByOwner || Admin || Available || AvailableFromTo) && (!CourseAnnoucements || ReactivableByOwner || Admin || RegisteredUser || RegisteredUser || PublicVisibility) && (!CourseAnnoucements || ReactivableByOwner || Admin || RegisteredUser || PublicVisibility || PublicVisibility) && (!CourseAnnoucements || Teacher || Admin || RegisteredUser || PublicVisibility || PublicVisibility) && (!CourseAnnoucements || Teacher || Admin || RegisteredUser || RegisteredUser || PublicVisibility) && (!CourseDescription || ReactivableByOwner || Admin || RegisteredUser || RegisteredUser || PublicVisibility) && (!CourseDescription || ReactivableByOwner || Admin || RegisteredUser || PublicVisibility || PublicVisibility) && (!CourseDescription || Teacher || Admin || RegisteredUser || RegisteredUser || PublicVisibility) && (!CourseDescription || Teacher || Admin || RegisteredUser || PublicVisibility || PublicVisibility) && (!CourseForum || ReactivableByOwner || Admin || RegisteredUser || PublicVisibility || PublicVisibility) && (!CourseForum || ReactivableByOwner || Admin || RegisteredUser || RegisteredUser || PublicVisibility) && (!CourseForum || Teacher || Admin || RegisteredUser || RegisteredUser || PublicVisibility) && (!CourseForum || Teacher || Admin || RegisteredUser || PublicVisibility || PublicVisibility) && (PublicVisibility || !x90 || !Visibility) && (MembersVisibility || x90 || !Visibility) && (!PublicVisibility || Visibility) && (!PublicVisibility || !x91 || !Visibility) && (!MembersVisibility || x91 || !Visibility) && (!MembersVisibility || Visibility) && (!ArtificialParentCourseDescription || Tools) && (!Tools || ArtificialParentCourseDescription) && (!CourseDescription || ArtificialParentCourseDescription) && (!ArtificialParentCourseAnnoucements || Tools) && (!Tools || ArtificialParentCourseAnnoucements) && (!CourseAnnoucements || ArtificialParentCourseAnnoucements) && (!Tools || ArtificialParentCourseForum) && (!ArtificialParentCourseForum || Tools) && (!CourseForum || ArtificialParentCourseForum) && (!CourseDescription || Teacher || Admin || MembersAccess || PublicVisibility || PublicVisibility) && (!CourseDescription || Teacher || Admin || RegisteredUser || IdentifiedAccess || PublicVisibility) && (!CourseDescription || Teacher || Admin || MembersAccess || IdentifiedAccess || PublicVisibility) && (!CourseDescription || Teacher || Admin || RegisteredUser || IdentifiedAccess || FreeAccess) && (!CourseDescription || Teacher || Admin || MembersAccess || IdentifiedAccess || FreeAccess) && (!CourseDescription || Teacher || Admin || MembersAccess || RegisteredUser || FreeAccess) && (!CourseDescription || Teacher || Admin || MembersAccess || PublicVisibility || FreeAccess) && (!CourseDescription || Teacher || Admin || MembersAccess || RegisteredUser || PublicVisibility) && (!CourseDescription || Teacher || Admin || RegisteredUser || PublicVisibility || FreeAccess) && (!CourseDescription || Teacher || Admin || RegisteredUser || RegisteredUser || FreeAccess) && (!CourseUser || Teacher || Admin || MembersAccess || IdentifiedAccess || FreeAccess) && (!CourseUser || Teacher || Admin || RegisteredUser || IdentifiedAccess || FreeAccess) && (!CourseUser || Teacher || Admin || MembersAccess || IdentifiedAccess || PublicVisibility) && (!CourseUser || Teacher || Admin || RegisteredUser || IdentifiedAccess || PublicVisibility) && (!CourseUser || Teacher || Admin || RegisteredUser || RegisteredUser || FreeAccess) && (!CourseUser || Teacher || Admin || MembersAccess || PublicVisibility || PublicVisibility) && (!CourseUser || Teacher || Admin || MembersAccess || RegisteredUser || PublicVisibility) && (!CourseUser || Teacher || Admin || MembersAccess || RegisteredUser || FreeAccess) && (!CourseUser || Teacher || Admin || RegisteredUser || PublicVisibility || FreeAccess) && (!CourseUser || Teacher || Admin || MembersAccess || PublicVisibility || FreeAccess) && (!CourseDescription || ReactivableByOwner || Admin || MembersAccess || PublicVisibility || PublicVisibility) && (!CourseDescription || ReactivableByOwner || Admin || MembersAccess || IdentifiedAccess || PublicVisibility) && (!CourseDescription || ReactivableByOwner || Admin || MembersAccess || IdentifiedAccess || FreeAccess) && (!CourseDescription || ReactivableByOwner || Admin || RegisteredUser || IdentifiedAccess || PublicVisibility) && (!CourseDescription || ReactivableByOwner || Admin || RegisteredUser || IdentifiedAccess || FreeAccess) && (!CourseDescription || ReactivableByOwner || Admin || MembersAccess || PublicVisibility || FreeAccess) && (!CourseDescription || ReactivableByOwner || Admin || RegisteredUser || PublicVisibility || FreeAccess) && (!CourseDescription || ReactivableByOwner || Admin || MembersAccess || RegisteredUser || FreeAccess) && (!CourseDescription || ReactivableByOwner || Admin || MembersAccess || RegisteredUser || PublicVisibility) && (!CourseDescription || ReactivableByOwner || Admin || RegisteredUser || RegisteredUser || FreeAccess) && (!CourseUser || ReactivableByOwner || Admin || RegisteredUser || IdentifiedAccess || PublicVisibility) && (!CourseUser || ReactivableByOwner || Admin || RegisteredUser || IdentifiedAccess || FreeAccess) && (!CourseUser || ReactivableByOwner || Admin || RegisteredUser || PublicVisibility || FreeAccess) && (!CourseUser || ReactivableByOwner || Admin || MembersAccess || IdentifiedAccess || PublicVisibility) && (!CourseUser || ReactivableByOwner || Admin || MembersAccess || IdentifiedAccess || FreeAccess) && (!CourseUser || ReactivableByOwner || Admin || MembersAccess || PublicVisibility || PublicVisibility) && (!CourseUser || ReactivableByOwner || Admin || MembersAccess || RegisteredUser || PublicVisibility) && (!CourseUser || ReactivableByOwner || Admin || MembersAccess || PublicVisibility || FreeAccess) && (!CourseUser || ReactivableByOwner || Admin || MembersAccess || RegisteredUser || FreeAccess) && (!CourseUser || ReactivableByOwner || Admin || RegisteredUser || RegisteredUser || FreeAccess) && (MembersAccess || !x84 || x87 || !Access) && (IdentifiedAccess || x84 || !Access) && (!x84 || x86 || !Access) && (MembersAccess || x86 || !Access) && (!x85 || x87 || !Access) && (FreeAccess || !x87 || !Access) && (MembersAccess || !x85 || !Access) && (!x85 || !Access) && (!FreeAccess || !x89 || !Access) && (!FreeAccess || Access) && (!x88 || x89 || !Access) && (!MembersAccess || x89 || !Access) && (!MembersAccess || Access) && (!MembersAccess || !x88 || !Access) && (!IdentifiedAccess || Access) && (!IdentifiedAccess || x88 || !Access) && (!CourseForum || Teacher || Admin || MembersAccess || PublicVisibility || PublicVisibility) && (!CourseForum || Teacher || Admin || MembersAccess || IdentifiedAccess || FreeAccess) && (!CourseForum || Teacher || Admin || RegisteredUser || IdentifiedAccess || FreeAccess) && (!CourseForum || Teacher || Admin || MembersAccess || IdentifiedAccess || PublicVisibility) && (!CourseForum || Teacher || Admin || RegisteredUser || IdentifiedAccess || PublicVisibility) && (!CourseForum || Teacher || Admin || MembersAccess || RegisteredUser || FreeAccess) && (!CourseForum || Teacher || Admin || MembersAccess || PublicVisibility || FreeAccess) && (!CourseForum || Teacher || Admin || MembersAccess || RegisteredUser || PublicVisibility) && (!CourseForum || Teacher || Admin || RegisteredUser || PublicVisibility || FreeAccess) && (!CourseForum || Teacher || Admin || RegisteredUser || RegisteredUser || FreeAccess) && (!CourseAnnoucements || Teacher || Admin || RegisteredUser || IdentifiedAccess || PublicVisibility) && (!CourseAnnoucements || Teacher || Admin || RegisteredUser || IdentifiedAccess || FreeAccess) && (!CourseAnnoucements || Teacher || Admin || MembersAccess || PublicVisibility || FreeAccess) && (!CourseAnnoucements || Teacher || Admin || MembersAccess || IdentifiedAccess || PublicVisibility) && (!CourseAnnoucements || Teacher || Admin || MembersAccess || IdentifiedAccess || FreeAccess) && (!CourseAnnoucements || Teacher || Admin || RegisteredUser || PublicVisibility || FreeAccess) && (!CourseAnnoucements || Teacher || Admin || MembersAccess || RegisteredUser || FreeAccess) && (!CourseAnnoucements || Teacher || Admin || MembersAccess || RegisteredUser || PublicVisibility) && (!CourseAnnoucements || Teacher || Admin || MembersAccess || PublicVisibility || PublicVisibility) && (!CourseAnnoucements || Teacher || Admin || RegisteredUser || RegisteredUser || FreeAccess) && (!CourseForum || ReactivableByOwner || Admin || RegisteredUser || PublicVisibility || FreeAccess) && (!CourseForum || ReactivableByOwner || Admin || MembersAccess || RegisteredUser || PublicVisibility) && (!CourseForum || ReactivableByOwner || Admin || MembersAccess || PublicVisibility || FreeAccess) && (!CourseForum || ReactivableByOwner || Admin || MembersAccess || RegisteredUser || FreeAccess) && (!CourseForum || ReactivableByOwner || Admin || RegisteredUser || RegisteredUser || FreeAccess) && (!CourseForum || ReactivableByOwner || Admin || MembersAccess || IdentifiedAccess || PublicVisibility) && (!CourseForum || ReactivableByOwner || Admin || MembersAccess || IdentifiedAccess || FreeAccess) && (!CourseForum || ReactivableByOwner || Admin || RegisteredUser || IdentifiedAccess || PublicVisibility) && (!CourseForum || ReactivableByOwner || Admin || RegisteredUser || IdentifiedAccess || FreeAccess) && (!CourseForum || ReactivableByOwner || Admin || MembersAccess || PublicVisibility || PublicVisibility) && (!CourseAnnoucements || ReactivableByOwner || Admin || RegisteredUser || IdentifiedAccess || PublicVisibility) && (!CourseAnnoucements || ReactivableByOwner || Admin || RegisteredUser || IdentifiedAccess || FreeAccess) && (!CourseAnnoucements || ReactivableByOwner || Admin || RegisteredUser || PublicVisibility || FreeAccess) && (!CourseAnnoucements || ReactivableByOwner || Admin || MembersAccess || IdentifiedAccess || PublicVisibility) && (!CourseAnnoucements || ReactivableByOwner || Admin || MembersAccess || IdentifiedAccess || FreeAccess) && (!CourseAnnoucements || ReactivableByOwner || Admin || MembersAccess || PublicVisibility || PublicVisibility) && (!CourseAnnoucements || ReactivableByOwner || Admin || MembersAccess || RegisteredUser || FreeAccess) && (!CourseAnnoucements || ReactivableByOwner || Admin || RegisteredUser || RegisteredUser || FreeAccess) && (!CourseAnnoucements || ReactivableByOwner || Admin || MembersAccess || RegisteredUser || PublicVisibility) && (!CourseAnnoucements || ReactivableByOwner || Admin || MembersAccess || PublicVisibility || FreeAccess) && (!User || Claroline) && (!Claroline || User) && (!Claroline || Course) && (!Course || Claroline) && (!RegisteredUser || User) && (RegisteredUser || !x62 || !User) && (UnregisteredUser || x62 || !User) && (!RegisteredUser || !x63 || !User) && (!UnregisteredUser || x63 || !User) && (!UnregisteredUser || User) && (!ArtificialParentLostPassword || Subscription) && (!Subscription || ArtificialParentLostPassword) && (!LostPassword || ArtificialParentLostPassword) && (!Subscription || Claroline) && (!Claroline || Subscription) && (!ArtificialParentOpenSubscription || Subscription) && (!Subscription || ArtificialParentOpenSubscription) && (!OpenSubscription || ArtificialParentOpenSubscription) && (!RegistrationDenied || !x83 || !Registration) && (!AllowedRegistration || x83 || !Registration) && (!RegistrationDenied || Registration) && (RegistrationDenied || !x82 || !Registration) && (AllowedRegistration || x82 || !Registration) && (!AllowedRegistration || Registration) && (!Course || Status) && (!Status || Course) && (!Course || Registration) && (!Registration || Course) && (!ArtificialParentWithValidationRegistration || AllowedRegistration) && (!AllowedRegistration || ArtificialParentWithValidationRegistration) && (!WithValidationRegistration || ArtificialParentWithValidationRegistration) && (!ArtificialParentWithKeyRegistration || AllowedRegistration) && (!AllowedRegistration || ArtificialParentWithKeyRegistration) && (!WithKeyRegistration || ArtificialParentWithKeyRegistration) && (!ReactivableByOwner || !x80 || !Unavailable) && (!x80 || x81 || !Unavailable) && (!ReactivableByOwner || x81 || !Unavailable) && (!ReactivableByOwner || Unavailable) && (!ReactivableByAdmin || x80 || !Unavailable) && (!Deleted || Unavailable) && (!Deleted || !x81 || !Unavailable) && (Deleted || !x79 || !Unavailable) && (!x77 || x79 || !Unavailable) && (!x77 || !Unavailable) && (ReactivableByOwner || !x76 || x79 || !Unavailable) && (ReactivableByOwner || !x77 || !Unavailable) && (!x76 || x78 || !Unavailable) && (ReactivableByOwner || x78 || !Unavailable) && (!ReactivableByAdmin || Unavailable) && (ReactivableByAdmin || x76 || !Unavailable) && (!Available || Status) && (Available || !x70 || x73 || !Status) && (!x71 || x73 || !Status) && (!x71 || !Status) && (Available || !x71 || !Status) && (!Unavailable || Status) && (!Unavailable || !x75 || !Status) && (Unavailable || !x73 || !Status) && (!AvailableFromTo || x74 || !Status) && (!AvailableFromTo || Status) && (!x70 || x72 || !Status) && (AvailableFromTo || x70 || !Status) && (!Available || !x74 || !Status) && (Available || x72 || !Status) && (!x74 || x75 || !Status) && (!Available || x75 || !Status) && (!Tools || ArtificialParentCourseWork) && (!ArtificialParentCourseWork || Tools) && (!CourseWork || ArtificialParentCourseWork) && (!Tools || ArtificialParentCourseAgenda) && (!ArtificialParentCourseAgenda || Tools) && (!CourseAgenda || ArtificialParentCourseAgenda) && (!Tools || ArtificialParentCourseLearnPath) && (!ArtificialParentCourseLearnPath || Tools) && (!CourseLearnPath || ArtificialParentCourseLearnPath) && (!Tools || ArtificialParentCourseDiscussion) && (!ArtificialParentCourseDiscussion || Tools) && (!CourseDiscussion || ArtificialParentCourseDiscussion) && (!Admin || RegisteredUser) && (!CourseWork || Teacher || Admin || RegisteredUser || RegisteredUser || PublicVisibility) && (!CourseWork || Teacher || Admin || RegisteredUser || PublicVisibility || PublicVisibility) && (!CourseAgenda || ReactivableByOwner || Admin || RegisteredUser || RegisteredUser || PublicVisibility) && (!CourseWork || ReactivableByOwner || Admin || RegisteredUser || PublicVisibility || PublicVisibility) && (!CourseAgenda || Teacher || Admin || RegisteredUser || PublicVisibility || PublicVisibility) && (!CourseAgenda || ReactivableByOwner || Admin || RegisteredUser || PublicVisibility || PublicVisibility) && (!CourseAgenda || ReactivableByOwner || Admin || Available || AvailableFromTo) && (!CourseAgenda || Teacher || Admin || Available || AvailableFromTo) && (!CourseWork || ReactivableByOwner || Admin || Available || AvailableFromTo) && (!CourseWork || Teacher || Admin || Available || AvailableFromTo) && (!CourseDiscussion || ReactivableByOwner || Admin || RegisteredUser || RegisteredUser || PublicVisibility) && (!CourseDiscussion || ReactivableByOwner || Admin || RegisteredUser || PublicVisibility || PublicVisibility) && (!CourseLearnPath || ReactivableByOwner || Admin || RegisteredUser || PublicVisibility || PublicVisibility) && (!CourseLearnPath || ReactivableByOwner || Admin || RegisteredUser || RegisteredUser || PublicVisibility) && (!CourseDiscussion || Teacher || Admin || RegisteredUser || PublicVisibility || PublicVisibility) && (!CourseDiscussion || Teacher || Admin || RegisteredUser || RegisteredUser || PublicVisibility) && (!CourseLearnPath || Teacher || Admin || RegisteredUser || RegisteredUser || PublicVisibility) && (!CourseLearnPath || Teacher || Admin || RegisteredUser || PublicVisibility || PublicVisibility) && (!CourseLearnPath || ReactivableByOwner || Admin || Available || AvailableFromTo) && (!CourseLearnPath || Teacher || Admin || Available || AvailableFromTo) && (!CourseDiscussion || Teacher || Admin || Available || AvailableFromTo) && (!CourseDiscussion || ReactivableByOwner || Admin || Available || AvailableFromTo) && (!CourseWork || ReactivableByOwner || Admin || MembersAccess || PublicVisibility || PublicVisibility) && (!CourseWork || ReactivableByOwner || Admin || MembersAccess || RegisteredUser || PublicVisibility) && (!CourseWork || ReactivableByOwner || Admin || RegisteredUser || RegisteredUser || PublicVisibility) && (!CourseWork || ReactivableByOwner || Admin || RegisteredUser || IdentifiedAccess || FreeAccess) && (!CourseWork || ReactivableByOwner || Admin || RegisteredUser || RegisteredUser || FreeAccess) && (!CourseWork || ReactivableByOwner || Admin || RegisteredUser || PublicVisibility || FreeAccess) && (!CourseWork || ReactivableByOwner || Admin || MembersAccess || PublicVisibility || FreeAccess) && (!CourseWork || ReactivableByOwner || Admin || RegisteredUser || IdentifiedAccess || PublicVisibility) && (!CourseWork || ReactivableByOwner || Admin || MembersAccess || IdentifiedAccess || PublicVisibility) && (!CourseDiscussion || ReactivableByOwner || Admin || MembersAccess || PublicVisibility || PublicVisibility) && (!CourseDiscussion || ReactivableByOwner || Admin || MembersAccess || IdentifiedAccess || PublicVisibility) && (!CourseDiscussion || ReactivableByOwner || Admin || MembersAccess || IdentifiedAccess || FreeAccess) && (!CourseDiscussion || ReactivableByOwner || Admin || RegisteredUser || IdentifiedAccess || FreeAccess) && (!CourseDiscussion || ReactivableByOwner || Admin || RegisteredUser || IdentifiedAccess || PublicVisibility) && (!CourseDiscussion || ReactivableByOwner || Admin || MembersAccess || RegisteredUser || FreeAccess) && (!CourseDiscussion || ReactivableByOwner || Admin || MembersAccess || PublicVisibility || FreeAccess) && (!CourseDiscussion || ReactivableByOwner || Admin || MembersAccess || RegisteredUser || PublicVisibility) && (!CourseDiscussion || ReactivableByOwner || Admin || RegisteredUser || PublicVisibility || FreeAccess) && (!CourseDiscussion || ReactivableByOwner || Admin || RegisteredUser || RegisteredUser || FreeAccess) && (!CourseWork || Teacher || Admin || RegisteredUser || RegisteredUser || FreeAccess) && (!CourseWork || Teacher || Admin || RegisteredUser || PublicVisibility || FreeAccess) && (!CourseWork || Teacher || Admin || MembersAccess || RegisteredUser || PublicVisibility) && (!CourseWork || Teacher || Admin || MembersAccess || IdentifiedAccess || PublicVisibility) && (!CourseWork || Teacher || Admin || RegisteredUser || IdentifiedAccess || PublicVisibility) && (!CourseWork || Teacher || Admin || RegisteredUser || IdentifiedAccess || FreeAccess) && (!CourseWork || Teacher || Admin || MembersAccess || RegisteredUser || FreeAccess) && (!CourseWork || Teacher || Admin || MembersAccess || PublicVisibility || PublicVisibility) && (!CourseWork || Teacher || Admin || MembersAccess || PublicVisibility || FreeAccess) && (!CourseDiscussion || Teacher || Admin || MembersAccess || PublicVisibility || PublicVisibility) && (!CourseDiscussion || Teacher || Admin || MembersAccess || IdentifiedAccess || PublicVisibility) && (!CourseDiscussion || Teacher || Admin || MembersAccess || IdentifiedAccess || FreeAccess) && (!CourseDiscussion || Teacher || Admin || RegisteredUser || IdentifiedAccess || PublicVisibility) && (!CourseDiscussion || Teacher || Admin || RegisteredUser || IdentifiedAccess || FreeAccess) && (!CourseDiscussion || Teacher || Admin || MembersAccess || RegisteredUser || PublicVisibility) && (!CourseDiscussion || Teacher || Admin || MembersAccess || RegisteredUser || FreeAccess) && (!CourseDiscussion || Teacher || Admin || RegisteredUser || PublicVisibility || FreeAccess) && (!CourseDiscussion || Teacher || Admin || MembersAccess || PublicVisibility || FreeAccess) && (!CourseDiscussion || Teacher || Admin || RegisteredUser || RegisteredUser || FreeAccess) && (!CourseLearnPath || ReactivableByOwner || Admin || RegisteredUser || RegisteredUser || FreeAccess) && (!CourseLearnPath || ReactivableByOwner || Admin || MembersAccess || RegisteredUser || FreeAccess) && (!CourseLearnPath || ReactivableByOwner || Admin || RegisteredUser || PublicVisibility || FreeAccess) && (!CourseLearnPath || ReactivableByOwner || Admin || MembersAccess || RegisteredUser || PublicVisibility) && (!CourseLearnPath || ReactivableByOwner || Admin || MembersAccess || PublicVisibility || FreeAccess) && (!CourseLearnPath || ReactivableByOwner || Admin || MembersAccess || PublicVisibility || PublicVisibility) && (!CourseLearnPath || Teacher || Admin || MembersAccess || RegisteredUser || FreeAccess) && (!CourseLearnPath || Teacher || Admin || RegisteredUser || RegisteredUser || FreeAccess) && (!CourseLearnPath || Teacher || Admin || MembersAccess || PublicVisibility || PublicVisibility) && (!CourseLearnPath || Teacher || Admin || MembersAccess || PublicVisibility || FreeAccess) && (!CourseLearnPath || Teacher || Admin || MembersAccess || RegisteredUser || PublicVisibility) && (!CourseLearnPath || Teacher || Admin || RegisteredUser || PublicVisibility || FreeAccess) && (!CourseLearnPath || ReactivableByOwner || Admin || MembersAccess || IdentifiedAccess || PublicVisibility) && (!CourseLearnPath || Teacher || Admin || MembersAccess || IdentifiedAccess || PublicVisibility) && (!CourseLearnPath || ReactivableByOwner || Admin || RegisteredUser || IdentifiedAccess || PublicVisibility) && (!CourseLearnPath || Teacher || Admin || RegisteredUser || IdentifiedAccess || PublicVisibility) && (!CourseLearnPath || ReactivableByOwner || Admin || MembersAccess || IdentifiedAccess || FreeAccess) && (!CourseLearnPath || ReactivableByOwner || Admin || RegisteredUser || IdentifiedAccess || FreeAccess) && (!CourseWork || ReactivableByOwner || Admin || MembersAccess || IdentifiedAccess || FreeAccess) && (!CourseWork || ReactivableByOwner || Admin || MembersAccess || RegisteredUser || FreeAccess) && (!CourseLearnPath || Teacher || Admin || RegisteredUser || IdentifiedAccess || FreeAccess) && (!CourseWork || Teacher || Admin || MembersAccess || IdentifiedAccess || FreeAccess) && (!CourseLearnPath || Teacher || Admin || MembersAccess || IdentifiedAccess || FreeAccess) && (!CourseAgenda || Teacher || Admin || MembersAccess || IdentifiedAccess || PublicVisibility) && (!CourseAgenda || Teacher || Admin || RegisteredUser || IdentifiedAccess || PublicVisibility) && (!CourseAgenda || Teacher || Admin || MembersAccess || PublicVisibility || FreeAccess) && (!CourseAgenda || Teacher || Admin || RegisteredUser || PublicVisibility || FreeAccess) && (!CourseAgenda || Teacher || Admin || MembersAccess || IdentifiedAccess || FreeAccess) && (!CourseAgenda || Teacher || Admin || RegisteredUser || IdentifiedAccess || FreeAccess) && (!CourseAgenda || Teacher || Admin || RegisteredUser || RegisteredUser || FreeAccess) && (!CourseAgenda || Teacher || Admin || MembersAccess || RegisteredUser || FreeAccess) && (!CourseAgenda || Teacher || Admin || MembersAccess || PublicVisibility || PublicVisibility) && (!CourseAgenda || Teacher || Admin || MembersAccess || RegisteredUser || PublicVisibility) && (!CourseAgenda || Teacher || Admin || RegisteredUser || RegisteredUser || PublicVisibility) && (!CourseAgenda || ReactivableByOwner || Admin || MembersAccess || PublicVisibility || PublicVisibility) && (!CourseAgenda || ReactivableByOwner || Admin || MembersAccess || RegisteredUser || FreeAccess) && (!CourseAgenda || ReactivableByOwner || Admin || MembersAccess || PublicVisibility || FreeAccess) && (!CourseAgenda || ReactivableByOwner || Admin || RegisteredUser || PublicVisibility || FreeAccess) && (!CourseAgenda || ReactivableByOwner || Admin || RegisteredUser || RegisteredUser || FreeAccess) && (!CourseAgenda || ReactivableByOwner || Admin || MembersAccess || IdentifiedAccess || PublicVisibility) && (!CourseAgenda || ReactivableByOwner || Admin || RegisteredUser || IdentifiedAccess || PublicVisibility) && (!CourseAgenda || ReactivableByOwner || Admin || MembersAccess || RegisteredUser || PublicVisibility) && (!CourseAgenda || ReactivableByOwner || Admin || MembersAccess || IdentifiedAccess || FreeAccess) && (!CourseAgenda || ReactivableByOwner || Admin || RegisteredUser || IdentifiedAccess || FreeAccess) && (!CourseExercise || ReactivableByOwner || Admin || RegisteredUser || IdentifiedAccess || PublicVisibility) && (!CourseExercise || ReactivableByOwner || Admin || RegisteredUser || IdentifiedAccess || FreeAccess) && (!CourseExercise || ReactivableByOwner || Admin || MembersAccess || RegisteredUser || FreeAccess) && (!CourseExercise || ReactivableByOwner || Admin || MembersAccess || RegisteredUser || PublicVisibility) && (!CourseExercise || ReactivableByOwner || Admin || MembersAccess || IdentifiedAccess || PublicVisibility) && (!CourseExercise || ReactivableByOwner || Admin || MembersAccess || IdentifiedAccess || FreeAccess) && (!CourseExercise || ReactivableByOwner || Admin || MembersAccess || PublicVisibility || PublicVisibility) && (!CourseExercise || ReactivableByOwner || Admin || MembersAccess || PublicVisibility || FreeAccess) && (!CourseWiki || ReactivableByOwner || Admin || MembersAccess || IdentifiedAccess || PublicVisibility) && (!CourseWiki || ReactivableByOwner || Admin || RegisteredUser || IdentifiedAccess || PublicVisibility) && (!CourseWiki || ReactivableByOwner || Admin || MembersAccess || PublicVisibility || PublicVisibility) && (!CourseWiki || ReactivableByOwner || Admin || MembersAccess || RegisteredUser || PublicVisibility) && (!CourseWiki || ReactivableByOwner || Admin || MembersAccess || PublicVisibility || FreeAccess) && (!CourseWiki || ReactivableByOwner || Admin || MembersAccess || IdentifiedAccess || FreeAccess) && (!CourseWiki || ReactivableByOwner || Admin || MembersAccess || RegisteredUser || FreeAccess) && (!CourseWiki || ReactivableByOwner || Admin || RegisteredUser || IdentifiedAccess || FreeAccess) && (!CourseWiki || Teacher || Admin || RegisteredUser || IdentifiedAccess || PublicVisibility) && (!CourseWiki || Teacher || Admin || RegisteredUser || IdentifiedAccess || FreeAccess) && (!CourseWiki || Teacher || Admin || MembersAccess || RegisteredUser || PublicVisibility) && (!CourseWiki || Teacher || Admin || MembersAccess || RegisteredUser || FreeAccess) && (!CourseWiki || Teacher || Admin || MembersAccess || IdentifiedAccess || PublicVisibility) && (!CourseWiki || Teacher || Admin || MembersAccess || IdentifiedAccess || FreeAccess) && (!CourseWiki || Teacher || Admin || MembersAccess || PublicVisibility || PublicVisibility) && (!CourseWiki || Teacher || Admin || MembersAccess || PublicVisibility || FreeAccess) && (!CourseExercise || Teacher || Admin || MembersAccess || PublicVisibility || PublicVisibility) && (!CourseExercise || Teacher || Admin || MembersAccess || PublicVisibility || FreeAccess) && (!CourseExercise || Teacher || Admin || RegisteredUser || IdentifiedAccess || PublicVisibility) && (!CourseExercise || Teacher || Admin || MembersAccess || RegisteredUser || PublicVisibility) && (!CourseExercise || Teacher || Admin || MembersAccess || RegisteredUser || FreeAccess) && (!CourseExercise || Teacher || Admin || MembersAccess || IdentifiedAccess || FreeAccess) && (!CourseGroup || Teacher || Admin || RegisteredUser || IdentifiedAccess || PublicVisibility) && (!CourseGroup || Teacher || Admin || RegisteredUser || IdentifiedAccess || FreeAccess) && (!CourseGroup || Teacher || Admin || MembersAccess || RegisteredUser || PublicVisibility) && (!CourseGroup || Teacher || Admin || MembersAccess || RegisteredUser || FreeAccess) && (!CourseGroup || Teacher || Admin || MembersAccess || PublicVisibility || PublicVisibility) && (!CourseGroup || Teacher || Admin || MembersAccess || PublicVisibility || FreeAccess) && (!CourseGroup || Teacher || Admin || MembersAccess || IdentifiedAccess || PublicVisibility) && (!CourseGroup || Teacher || Admin || MembersAccess || IdentifiedAccess || FreeAccess) && (!CourseDocument || Teacher || Admin || RegisteredUser || IdentifiedAccess || PublicVisibility) && (!CourseDocument || Teacher || Admin || MembersAccess || RegisteredUser || PublicVisibility) && (!CourseExercise || Teacher || Admin || MembersAccess || IdentifiedAccess || PublicVisibility) && (!CourseDocument || Teacher || Admin || MembersAccess || RegisteredUser || FreeAccess) && (!CourseExercise || Teacher || Admin || RegisteredUser || IdentifiedAccess || FreeAccess) && (!CourseDocument || Teacher || Admin || RegisteredUser || IdentifiedAccess || FreeAccess) && (!CourseDocument || Teacher || Admin || MembersAccess || IdentifiedAccess || PublicVisibility) && (!CourseDocument || Teacher || Admin || MembersAccess || IdentifiedAccess || FreeAccess) && (!CourseDocument || Teacher || Admin || MembersAccess || PublicVisibility || PublicVisibility) && (!CourseDocument || Teacher || Admin || MembersAccess || PublicVisibility || FreeAccess) && (!CourseGroup || ReactivableByOwner || Admin || MembersAccess || RegisteredUser || PublicVisibility) && (!CourseGroup || ReactivableByOwner || Admin || MembersAccess || RegisteredUser || FreeAccess) && (!CourseGroup || ReactivableByOwner || Admin || RegisteredUser || IdentifiedAccess || FreeAccess) && (!CourseGroup || ReactivableByOwner || Admin || RegisteredUser || IdentifiedAccess || PublicVisibility) && (!CourseGroup || ReactivableByOwner || Admin || MembersAccess || IdentifiedAccess || PublicVisibility) && (!CourseGroup || ReactivableByOwner || Admin || MembersAccess || IdentifiedAccess || FreeAccess) && (!CourseGroup || ReactivableByOwner || Admin || MembersAccess || PublicVisibility || PublicVisibility) && (!CourseGroup || ReactivableByOwner || Admin || MembersAccess || PublicVisibility || FreeAccess) && (!CourseDocument || ReactivableByOwner || Admin || RegisteredUser || IdentifiedAccess || PublicVisibility) && (!CourseDocument || ReactivableByOwner || Admin || RegisteredUser || IdentifiedAccess || FreeAccess) && (!CourseDocument || ReactivableByOwner || Admin || MembersAccess || RegisteredUser || PublicVisibility) && (!CourseDocument || ReactivableByOwner || Admin || MembersAccess || RegisteredUser || FreeAccess) && (!CourseDocument || ReactivableByOwner || Admin || MembersAccess || PublicVisibility || PublicVisibility) && (!CourseDocument || ReactivableByOwner || Admin || MembersAccess || PublicVisibility || FreeAccess) && (!CourseDocument || ReactivableByOwner || Admin || MembersAccess || IdentifiedAccess || FreeAccess) && (!CourseDocument || ReactivableByOwner || Admin || MembersAccess || IdentifiedAccess || PublicVisibility) && (!Tools || ArtificialParentCourseGroup) && (!ArtificialParentCourseGroup || Tools) && (!CourseGroup || ArtificialParentCourseGroup) && (!Tools || ArtificialParentCourseDocument) && (!ArtificialParentCourseDocument || Tools) && (!CourseDocument || ArtificialParentCourseDocument) && (!Tools || ArtificialParentCourseWiki) && (!ArtificialParentCourseWiki || Tools) && (!CourseWiki || ArtificialParentCourseWiki) && (!ArtificialParentCourseExercise || Tools) && (!Tools || ArtificialParentCourseExercise) && (!CourseExercise || ArtificialParentCourseExercise) && (!Teacher || RegisteredUser) && (!CourseWiki || Teacher || Admin || Available || AvailableFromTo) && (!CourseWiki || ReactivableByOwner || Admin || Available || AvailableFromTo) && (!CourseExercise || Teacher || Admin || Available || AvailableFromTo) && (!CourseExercise || ReactivableByOwner || Admin || Available || AvailableFromTo) && (!CourseWiki || ReactivableByOwner || Admin || RegisteredUser || PublicVisibility || PublicVisibility) && (!CourseExercise || ReactivableByOwner || Admin || RegisteredUser || RegisteredUser || PublicVisibility) && (!CourseExercise || ReactivableByOwner || Admin || RegisteredUser || PublicVisibility || PublicVisibility) && (!CourseExercise || Teacher || Admin || RegisteredUser || PublicVisibility || PublicVisibility) && (!CourseWiki || Teacher || Admin || RegisteredUser || RegisteredUser || PublicVisibility) && (!CourseWiki || Teacher || Admin || RegisteredUser || PublicVisibility || PublicVisibility) && (!CourseWiki || ReactivableByOwner || Admin || RegisteredUser || RegisteredUser || FreeAccess) && (!CourseWiki || ReactivableByOwner || Admin || RegisteredUser || PublicVisibility || FreeAccess) && (!CourseWiki || ReactivableByOwner || Admin || RegisteredUser || RegisteredUser || PublicVisibility) && (!CourseWiki || Teacher || Admin || RegisteredUser || RegisteredUser || FreeAccess) && (!CourseWiki || Teacher || Admin || RegisteredUser || PublicVisibility || FreeAccess) && (!CourseExercise || ReactivableByOwner || Admin || RegisteredUser || RegisteredUser || FreeAccess) && (!CourseExercise || ReactivableByOwner || Admin || RegisteredUser || PublicVisibility || FreeAccess) && (!CourseExercise || Teacher || Admin || RegisteredUser || RegisteredUser || FreeAccess) && (!CourseExercise || Teacher || Admin || RegisteredUser || PublicVisibility || FreeAccess) && (!CourseExercise || Teacher || Admin || RegisteredUser || RegisteredUser || PublicVisibility) && (!CourseDocument || Teacher || Admin || RegisteredUser || RegisteredUser || PublicVisibility) && (!CourseDocument || Teacher || Admin || RegisteredUser || PublicVisibility || PublicVisibility) && (!CourseDocument || Teacher || Admin || RegisteredUser || RegisteredUser || FreeAccess) && (!CourseDocument || Teacher || Admin || RegisteredUser || PublicVisibility || FreeAccess) && (!CourseGroup || Teacher || Admin || RegisteredUser || PublicVisibility || FreeAccess) && (!CourseGroup || Teacher || Admin || RegisteredUser || RegisteredUser || FreeAccess) && (!CourseDocument || ReactivableByOwner || Admin || RegisteredUser || PublicVisibility || FreeAccess) && (!CourseDocument || ReactivableByOwner || Admin || RegisteredUser || RegisteredUser || FreeAccess) && (!CourseGroup || ReactivableByOwner || Admin || RegisteredUser || RegisteredUser || FreeAccess) && (!CourseGroup || ReactivableByOwner || Admin || RegisteredUser || PublicVisibility || FreeAccess) && (!CourseDocument || ReactivableByOwner || Admin || Available || AvailableFromTo) && (!CourseDocument || Teacher || Admin || Available || AvailableFromTo) && (!CourseGroup || Teacher || Admin || Available || AvailableFromTo) && (!CourseGroup || ReactivableByOwner || Admin || Available || AvailableFromTo) && (!CourseGroup || ReactivableByOwner || Admin || RegisteredUser || RegisteredUser || PublicVisibility) && (!CourseGroup || ReactivableByOwner || Admin || RegisteredUser || PublicVisibility || PublicVisibility) && (!CourseGroup || Teacher || Admin || RegisteredUser || PublicVisibility || PublicVisibility) && (!CourseGroup || Teacher || Admin || RegisteredUser || RegisteredUser || PublicVisibility) && (!CourseDocument || ReactivableByOwner || Admin || RegisteredUser || RegisteredUser || PublicVisibility) && (!CourseDocument || ReactivableByOwner || Admin || RegisteredUser || PublicVisibility || PublicVisibility) \ No newline at end of file diff --git a/engine/crates/vts-synth/models/vibes/claroline.fm b/engine/crates/vts-synth/models/vibes/claroline.fm new file mode 100644 index 00000000..ae3b1085 --- /dev/null +++ b/engine/crates/vts-synth/models/vibes/claroline.fm @@ -0,0 +1 @@ +(!CourseDiscussion || ReactivableByOwner || Admin || Available || AvailableFromTo) && (!CourseDiscussion || ReactivableByOwner || Admin || MembersAccess || IdentifiedAccess || FreeAccess) && (!CourseDiscussion || ReactivableByOwner || Admin || MembersAccess || IdentifiedAccess || PublicVisibility) && (!CourseDiscussion || ReactivableByOwner || Admin || MembersAccess || PublicVisibility || FreeAccess) && (!CourseDiscussion || ReactivableByOwner || Admin || MembersAccess || PublicVisibility || PublicVisibility) && (!CourseDiscussion || ReactivableByOwner || Admin || MembersAccess || RegisteredUser || FreeAccess) && (!CourseDiscussion || ReactivableByOwner || Admin || MembersAccess || RegisteredUser || PublicVisibility) && (!CourseDiscussion || ReactivableByOwner || Admin || RegisteredUser || IdentifiedAccess || FreeAccess) && (!CourseDiscussion || ReactivableByOwner || Admin || RegisteredUser || IdentifiedAccess || PublicVisibility) && (!CourseDiscussion || ReactivableByOwner || Admin || RegisteredUser || PublicVisibility || FreeAccess) && (!CourseDiscussion || ReactivableByOwner || Admin || RegisteredUser || PublicVisibility || PublicVisibility) && (!CourseDiscussion || ReactivableByOwner || Admin || RegisteredUser || RegisteredUser || FreeAccess) && (!CourseDiscussion || ReactivableByOwner || Admin || RegisteredUser || RegisteredUser || PublicVisibility) && (!CourseDiscussion || Teacher || Admin || Available || AvailableFromTo) && (!CourseDiscussion || Teacher || Admin || MembersAccess || IdentifiedAccess || FreeAccess) && (!CourseDiscussion || Teacher || Admin || MembersAccess || IdentifiedAccess || PublicVisibility) && (!CourseDiscussion || Teacher || Admin || MembersAccess || PublicVisibility || FreeAccess) && (!CourseDiscussion || Teacher || Admin || MembersAccess || PublicVisibility || PublicVisibility) && (!CourseDiscussion || Teacher || Admin || MembersAccess || RegisteredUser || FreeAccess) && (!CourseDiscussion || Teacher || Admin || MembersAccess || RegisteredUser || PublicVisibility) && (!CourseDiscussion || Teacher || Admin || RegisteredUser || IdentifiedAccess || FreeAccess) && (!CourseDiscussion || Teacher || Admin || RegisteredUser || IdentifiedAccess || PublicVisibility) && (!CourseDiscussion || Teacher || Admin || RegisteredUser || PublicVisibility || FreeAccess) && (!CourseDiscussion || Teacher || Admin || RegisteredUser || PublicVisibility || PublicVisibility) && (!CourseDiscussion || Teacher || Admin || RegisteredUser || RegisteredUser || FreeAccess) && (!CourseDiscussion || Teacher || Admin || RegisteredUser || RegisteredUser || PublicVisibility) && (!CourseWiki || ReactivableByOwner || Admin || Available || AvailableFromTo) && (!CourseWiki || ReactivableByOwner || Admin || MembersAccess || IdentifiedAccess || FreeAccess) && (!CourseWiki || ReactivableByOwner || Admin || MembersAccess || IdentifiedAccess || PublicVisibility) && (!CourseWiki || ReactivableByOwner || Admin || MembersAccess || PublicVisibility || FreeAccess) && (!CourseWiki || ReactivableByOwner || Admin || MembersAccess || PublicVisibility || PublicVisibility) && (!CourseWiki || ReactivableByOwner || Admin || MembersAccess || RegisteredUser || FreeAccess) && (!CourseWiki || ReactivableByOwner || Admin || MembersAccess || RegisteredUser || PublicVisibility) && (!CourseWiki || ReactivableByOwner || Admin || RegisteredUser || IdentifiedAccess || FreeAccess) && (!CourseWiki || ReactivableByOwner || Admin || RegisteredUser || IdentifiedAccess || PublicVisibility) && (!CourseWiki || ReactivableByOwner || Admin || RegisteredUser || PublicVisibility || FreeAccess) && (!CourseWiki || ReactivableByOwner || Admin || RegisteredUser || PublicVisibility || PublicVisibility) && (!CourseWiki || ReactivableByOwner || Admin || RegisteredUser || RegisteredUser || FreeAccess) && (!CourseWiki || ReactivableByOwner || Admin || RegisteredUser || RegisteredUser || PublicVisibility) && (!CourseWiki || Teacher || Admin || Available || AvailableFromTo) && (!CourseWiki || Teacher || Admin || MembersAccess || IdentifiedAccess || FreeAccess) && (!CourseWiki || Teacher || Admin || MembersAccess || IdentifiedAccess || PublicVisibility) && (!CourseWiki || Teacher || Admin || MembersAccess || PublicVisibility || FreeAccess) && (!CourseWiki || Teacher || Admin || MembersAccess || PublicVisibility || PublicVisibility) && (!CourseWiki || Teacher || Admin || MembersAccess || RegisteredUser || FreeAccess) && (!CourseWiki || Teacher || Admin || MembersAccess || RegisteredUser || PublicVisibility) && (!CourseWiki || Teacher || Admin || RegisteredUser || IdentifiedAccess || FreeAccess) && (!CourseWiki || Teacher || Admin || RegisteredUser || IdentifiedAccess || PublicVisibility) && (!CourseWiki || Teacher || Admin || RegisteredUser || PublicVisibility || FreeAccess) && (!CourseWiki || Teacher || Admin || RegisteredUser || PublicVisibility || PublicVisibility) && (!CourseWiki || Teacher || Admin || RegisteredUser || RegisteredUser || FreeAccess) && (!CourseWiki || Teacher || Admin || RegisteredUser || RegisteredUser || PublicVisibility) && (!CourseLearnPath || ReactivableByOwner || Admin || Available || AvailableFromTo) && (!CourseLearnPath || ReactivableByOwner || Admin || MembersAccess || IdentifiedAccess || FreeAccess) && (!CourseLearnPath || ReactivableByOwner || Admin || MembersAccess || IdentifiedAccess || PublicVisibility) && (!CourseLearnPath || ReactivableByOwner || Admin || MembersAccess || PublicVisibility || FreeAccess) && (!CourseLearnPath || ReactivableByOwner || Admin || MembersAccess || PublicVisibility || PublicVisibility) && (!CourseLearnPath || ReactivableByOwner || Admin || MembersAccess || RegisteredUser || FreeAccess) && (!CourseLearnPath || ReactivableByOwner || Admin || MembersAccess || RegisteredUser || PublicVisibility) && (!CourseLearnPath || ReactivableByOwner || Admin || RegisteredUser || IdentifiedAccess || FreeAccess) && (!CourseLearnPath || ReactivableByOwner || Admin || RegisteredUser || IdentifiedAccess || PublicVisibility) && (!CourseLearnPath || ReactivableByOwner || Admin || RegisteredUser || PublicVisibility || FreeAccess) && (!CourseLearnPath || ReactivableByOwner || Admin || RegisteredUser || PublicVisibility || PublicVisibility) && (!CourseLearnPath || ReactivableByOwner || Admin || RegisteredUser || RegisteredUser || FreeAccess) && (!CourseLearnPath || ReactivableByOwner || Admin || RegisteredUser || RegisteredUser || PublicVisibility) && (!CourseLearnPath || Teacher || Admin || Available || AvailableFromTo) && (!CourseLearnPath || Teacher || Admin || MembersAccess || IdentifiedAccess || FreeAccess) && (!CourseLearnPath || Teacher || Admin || MembersAccess || IdentifiedAccess || PublicVisibility) && (!CourseLearnPath || Teacher || Admin || MembersAccess || PublicVisibility || FreeAccess) && (!CourseLearnPath || Teacher || Admin || MembersAccess || PublicVisibility || PublicVisibility) && (!CourseLearnPath || Teacher || Admin || MembersAccess || RegisteredUser || FreeAccess) && (!CourseLearnPath || Teacher || Admin || MembersAccess || RegisteredUser || PublicVisibility) && (!CourseLearnPath || Teacher || Admin || RegisteredUser || IdentifiedAccess || FreeAccess) && (!CourseLearnPath || Teacher || Admin || RegisteredUser || IdentifiedAccess || PublicVisibility) && (!CourseLearnPath || Teacher || Admin || RegisteredUser || PublicVisibility || FreeAccess) && (!CourseLearnPath || Teacher || Admin || RegisteredUser || PublicVisibility || PublicVisibility) && (!CourseLearnPath || Teacher || Admin || RegisteredUser || RegisteredUser || FreeAccess) && (!CourseLearnPath || Teacher || Admin || RegisteredUser || RegisteredUser || PublicVisibility) && (!CourseWork || ReactivableByOwner || Admin || Available || AvailableFromTo) && (!CourseWork || ReactivableByOwner || Admin || MembersAccess || IdentifiedAccess || FreeAccess) && (!CourseWork || ReactivableByOwner || Admin || MembersAccess || IdentifiedAccess || PublicVisibility) && (!CourseWork || ReactivableByOwner || Admin || MembersAccess || PublicVisibility || FreeAccess) && (!CourseWork || ReactivableByOwner || Admin || MembersAccess || PublicVisibility || PublicVisibility) && (!CourseWork || ReactivableByOwner || Admin || MembersAccess || RegisteredUser || FreeAccess) && (!CourseWork || ReactivableByOwner || Admin || MembersAccess || RegisteredUser || PublicVisibility) && (!CourseWork || ReactivableByOwner || Admin || RegisteredUser || IdentifiedAccess || FreeAccess) && (!CourseWork || ReactivableByOwner || Admin || RegisteredUser || IdentifiedAccess || PublicVisibility) && (!CourseWork || ReactivableByOwner || Admin || RegisteredUser || PublicVisibility || FreeAccess) && (!CourseWork || ReactivableByOwner || Admin || RegisteredUser || PublicVisibility || PublicVisibility) && (!CourseWork || ReactivableByOwner || Admin || RegisteredUser || RegisteredUser || FreeAccess) && (!CourseWork || ReactivableByOwner || Admin || RegisteredUser || RegisteredUser || PublicVisibility) && (!CourseWork || Teacher || Admin || Available || AvailableFromTo) && (!CourseWork || Teacher || Admin || MembersAccess || IdentifiedAccess || FreeAccess) && (!CourseWork || Teacher || Admin || MembersAccess || IdentifiedAccess || PublicVisibility) && (!CourseWork || Teacher || Admin || MembersAccess || PublicVisibility || FreeAccess) && (!CourseWork || Teacher || Admin || MembersAccess || PublicVisibility || PublicVisibility) && (!CourseWork || Teacher || Admin || MembersAccess || RegisteredUser || FreeAccess) && (!CourseWork || Teacher || Admin || MembersAccess || RegisteredUser || PublicVisibility) && (!CourseWork || Teacher || Admin || RegisteredUser || IdentifiedAccess || FreeAccess) && (!CourseWork || Teacher || Admin || RegisteredUser || IdentifiedAccess || PublicVisibility) && (!CourseWork || Teacher || Admin || RegisteredUser || PublicVisibility || FreeAccess) && (!CourseWork || Teacher || Admin || RegisteredUser || PublicVisibility || PublicVisibility) && (!CourseWork || Teacher || Admin || RegisteredUser || RegisteredUser || FreeAccess) && (!CourseWork || Teacher || Admin || RegisteredUser || RegisteredUser || PublicVisibility) && (!CourseGroup || ReactivableByOwner || Admin || Available || AvailableFromTo) && (!CourseGroup || ReactivableByOwner || Admin || MembersAccess || IdentifiedAccess || FreeAccess) && (!CourseGroup || ReactivableByOwner || Admin || MembersAccess || IdentifiedAccess || PublicVisibility) && (!CourseGroup || ReactivableByOwner || Admin || MembersAccess || PublicVisibility || FreeAccess) && (!CourseGroup || ReactivableByOwner || Admin || MembersAccess || PublicVisibility || PublicVisibility) && (!CourseGroup || ReactivableByOwner || Admin || MembersAccess || RegisteredUser || FreeAccess) && (!CourseGroup || ReactivableByOwner || Admin || MembersAccess || RegisteredUser || PublicVisibility) && (!CourseGroup || ReactivableByOwner || Admin || RegisteredUser || IdentifiedAccess || FreeAccess) && (!CourseGroup || ReactivableByOwner || Admin || RegisteredUser || IdentifiedAccess || PublicVisibility) && (!CourseGroup || ReactivableByOwner || Admin || RegisteredUser || PublicVisibility || FreeAccess) && (!CourseGroup || ReactivableByOwner || Admin || RegisteredUser || PublicVisibility || PublicVisibility) && (!CourseGroup || ReactivableByOwner || Admin || RegisteredUser || RegisteredUser || FreeAccess) && (!CourseGroup || ReactivableByOwner || Admin || RegisteredUser || RegisteredUser || PublicVisibility) && (!CourseGroup || Teacher || Admin || Available || AvailableFromTo) && (!CourseGroup || Teacher || Admin || MembersAccess || IdentifiedAccess || FreeAccess) && (!CourseGroup || Teacher || Admin || MembersAccess || IdentifiedAccess || PublicVisibility) && (!CourseGroup || Teacher || Admin || MembersAccess || PublicVisibility || FreeAccess) && (!CourseGroup || Teacher || Admin || MembersAccess || PublicVisibility || PublicVisibility) && (!CourseGroup || Teacher || Admin || MembersAccess || RegisteredUser || FreeAccess) && (!CourseGroup || Teacher || Admin || MembersAccess || RegisteredUser || PublicVisibility) && (!CourseGroup || Teacher || Admin || RegisteredUser || IdentifiedAccess || FreeAccess) && (!CourseGroup || Teacher || Admin || RegisteredUser || IdentifiedAccess || PublicVisibility) && (!CourseGroup || Teacher || Admin || RegisteredUser || PublicVisibility || FreeAccess) && (!CourseGroup || Teacher || Admin || RegisteredUser || PublicVisibility || PublicVisibility) && (!CourseGroup || Teacher || Admin || RegisteredUser || RegisteredUser || FreeAccess) && (!CourseGroup || Teacher || Admin || RegisteredUser || RegisteredUser || PublicVisibility) && (!CourseIndex || ReactivableByOwner || Admin || Available || AvailableFromTo || Available || AvailableFromTo) && (!CourseIndex || ReactivableByOwner || Admin || Available || AvailableFromTo || PublicVisibility) && (!CourseIndex || ReactivableByOwner || Admin || MembersVisibility || Available || AvailableFromTo) && (!CourseIndex || ReactivableByOwner || Admin || MembersVisibility || PublicVisibility) && (!CourseIndex || ReactivableByOwner || Admin || RegisteredUser || Available || AvailableFromTo) && (!CourseIndex || ReactivableByOwner || Admin || RegisteredUser || PublicVisibility) && (!CourseIndex || Teacher || Admin || Available || AvailableFromTo || Available || AvailableFromTo) && (!CourseIndex || Teacher || Admin || Available || AvailableFromTo || PublicVisibility) && (!CourseIndex || Teacher || Admin || MembersVisibility || Available || AvailableFromTo) && (!CourseIndex || Teacher || Admin || MembersVisibility || PublicVisibility) && (!CourseIndex || Teacher || Admin || RegisteredUser || Available || AvailableFromTo) && (!CourseIndex || Teacher || Admin || RegisteredUser || PublicVisibility) && (!CourseDocument || ReactivableByOwner || Admin || Available || AvailableFromTo) && (!CourseDocument || ReactivableByOwner || Admin || MembersAccess || IdentifiedAccess || FreeAccess) && (!CourseDocument || ReactivableByOwner || Admin || MembersAccess || IdentifiedAccess || PublicVisibility) && (!CourseDocument || ReactivableByOwner || Admin || MembersAccess || PublicVisibility || FreeAccess) && (!CourseDocument || ReactivableByOwner || Admin || MembersAccess || PublicVisibility || PublicVisibility) && (!CourseDocument || ReactivableByOwner || Admin || MembersAccess || RegisteredUser || FreeAccess) && (!CourseDocument || ReactivableByOwner || Admin || MembersAccess || RegisteredUser || PublicVisibility) && (!CourseDocument || ReactivableByOwner || Admin || RegisteredUser || IdentifiedAccess || FreeAccess) && (!CourseDocument || ReactivableByOwner || Admin || RegisteredUser || IdentifiedAccess || PublicVisibility) && (!CourseDocument || ReactivableByOwner || Admin || RegisteredUser || PublicVisibility || FreeAccess) && (!CourseDocument || ReactivableByOwner || Admin || RegisteredUser || PublicVisibility || PublicVisibility) && (!CourseDocument || ReactivableByOwner || Admin || RegisteredUser || RegisteredUser || FreeAccess) && (!CourseDocument || ReactivableByOwner || Admin || RegisteredUser || RegisteredUser || PublicVisibility) && (!CourseDocument || Teacher || Admin || Available || AvailableFromTo) && (!CourseDocument || Teacher || Admin || MembersAccess || IdentifiedAccess || FreeAccess) && (!CourseDocument || Teacher || Admin || MembersAccess || IdentifiedAccess || PublicVisibility) && (!CourseDocument || Teacher || Admin || MembersAccess || PublicVisibility || FreeAccess) && (!CourseDocument || Teacher || Admin || MembersAccess || PublicVisibility || PublicVisibility) && (!CourseDocument || Teacher || Admin || MembersAccess || RegisteredUser || FreeAccess) && (!CourseDocument || Teacher || Admin || MembersAccess || RegisteredUser || PublicVisibility) && (!CourseDocument || Teacher || Admin || RegisteredUser || IdentifiedAccess || FreeAccess) && (!CourseDocument || Teacher || Admin || RegisteredUser || IdentifiedAccess || PublicVisibility) && (!CourseDocument || Teacher || Admin || RegisteredUser || PublicVisibility || FreeAccess) && (!CourseDocument || Teacher || Admin || RegisteredUser || PublicVisibility || PublicVisibility) && (!CourseDocument || Teacher || Admin || RegisteredUser || RegisteredUser || FreeAccess) && (!CourseDocument || Teacher || Admin || RegisteredUser || RegisteredUser || PublicVisibility) && (!CourseUser || ReactivableByOwner || Admin || Available || AvailableFromTo) && (!CourseUser || ReactivableByOwner || Admin || MembersAccess || IdentifiedAccess || FreeAccess) && (!CourseUser || ReactivableByOwner || Admin || MembersAccess || IdentifiedAccess || PublicVisibility) && (!CourseUser || ReactivableByOwner || Admin || MembersAccess || PublicVisibility || FreeAccess) && (!CourseUser || ReactivableByOwner || Admin || MembersAccess || PublicVisibility || PublicVisibility) && (!CourseUser || ReactivableByOwner || Admin || MembersAccess || RegisteredUser || FreeAccess) && (!CourseUser || ReactivableByOwner || Admin || MembersAccess || RegisteredUser || PublicVisibility) && (!CourseUser || ReactivableByOwner || Admin || RegisteredUser || IdentifiedAccess || FreeAccess) && (!CourseUser || ReactivableByOwner || Admin || RegisteredUser || IdentifiedAccess || PublicVisibility) && (!CourseUser || ReactivableByOwner || Admin || RegisteredUser || PublicVisibility || FreeAccess) && (!CourseUser || ReactivableByOwner || Admin || RegisteredUser || PublicVisibility || PublicVisibility) && (!CourseUser || ReactivableByOwner || Admin || RegisteredUser || RegisteredUser || FreeAccess) && (!CourseUser || ReactivableByOwner || Admin || RegisteredUser || RegisteredUser || PublicVisibility) && (!CourseUser || Teacher || Admin || Available || AvailableFromTo) && (!CourseUser || Teacher || Admin || MembersAccess || IdentifiedAccess || FreeAccess) && (!CourseUser || Teacher || Admin || MembersAccess || IdentifiedAccess || PublicVisibility) && (!CourseUser || Teacher || Admin || MembersAccess || PublicVisibility || FreeAccess) && (!CourseUser || Teacher || Admin || MembersAccess || PublicVisibility || PublicVisibility) && (!CourseUser || Teacher || Admin || MembersAccess || RegisteredUser || FreeAccess) && (!CourseUser || Teacher || Admin || MembersAccess || RegisteredUser || PublicVisibility) && (!CourseUser || Teacher || Admin || RegisteredUser || IdentifiedAccess || FreeAccess) && (!CourseUser || Teacher || Admin || RegisteredUser || IdentifiedAccess || PublicVisibility) && (!CourseUser || Teacher || Admin || RegisteredUser || PublicVisibility || FreeAccess) && (!CourseUser || Teacher || Admin || RegisteredUser || PublicVisibility || PublicVisibility) && (!CourseUser || Teacher || Admin || RegisteredUser || RegisteredUser || FreeAccess) && (!CourseUser || Teacher || Admin || RegisteredUser || RegisteredUser || PublicVisibility) && (!CourseDescription || ReactivableByOwner || Admin || Available || AvailableFromTo) && (!CourseDescription || ReactivableByOwner || Admin || MembersAccess || IdentifiedAccess || FreeAccess) && (!CourseDescription || ReactivableByOwner || Admin || MembersAccess || IdentifiedAccess || PublicVisibility) && (!CourseDescription || ReactivableByOwner || Admin || MembersAccess || PublicVisibility || FreeAccess) && (!CourseDescription || ReactivableByOwner || Admin || MembersAccess || PublicVisibility || PublicVisibility) && (!CourseDescription || ReactivableByOwner || Admin || MembersAccess || RegisteredUser || FreeAccess) && (!CourseDescription || ReactivableByOwner || Admin || MembersAccess || RegisteredUser || PublicVisibility) && (!CourseDescription || ReactivableByOwner || Admin || RegisteredUser || IdentifiedAccess || FreeAccess) && (!CourseDescription || ReactivableByOwner || Admin || RegisteredUser || IdentifiedAccess || PublicVisibility) && (!CourseDescription || ReactivableByOwner || Admin || RegisteredUser || PublicVisibility || FreeAccess) && (!CourseDescription || ReactivableByOwner || Admin || RegisteredUser || PublicVisibility || PublicVisibility) && (!CourseDescription || ReactivableByOwner || Admin || RegisteredUser || RegisteredUser || FreeAccess) && (!CourseDescription || ReactivableByOwner || Admin || RegisteredUser || RegisteredUser || PublicVisibility) && (!CourseDescription || Teacher || Admin || Available || AvailableFromTo) && (!CourseDescription || Teacher || Admin || MembersAccess || IdentifiedAccess || FreeAccess) && (!CourseDescription || Teacher || Admin || MembersAccess || IdentifiedAccess || PublicVisibility) && (!CourseDescription || Teacher || Admin || MembersAccess || PublicVisibility || FreeAccess) && (!CourseDescription || Teacher || Admin || MembersAccess || PublicVisibility || PublicVisibility) && (!CourseDescription || Teacher || Admin || MembersAccess || RegisteredUser || FreeAccess) && (!CourseDescription || Teacher || Admin || MembersAccess || RegisteredUser || PublicVisibility) && (!CourseDescription || Teacher || Admin || RegisteredUser || IdentifiedAccess || FreeAccess) && (!CourseDescription || Teacher || Admin || RegisteredUser || IdentifiedAccess || PublicVisibility) && (!CourseDescription || Teacher || Admin || RegisteredUser || PublicVisibility || FreeAccess) && (!CourseDescription || Teacher || Admin || RegisteredUser || PublicVisibility || PublicVisibility) && (!CourseDescription || Teacher || Admin || RegisteredUser || RegisteredUser || FreeAccess) && (!CourseDescription || Teacher || Admin || RegisteredUser || RegisteredUser || PublicVisibility) && (!CourseForum || ReactivableByOwner || Admin || Available || AvailableFromTo) && (!CourseForum || ReactivableByOwner || Admin || MembersAccess || IdentifiedAccess || FreeAccess) && (!CourseForum || ReactivableByOwner || Admin || MembersAccess || IdentifiedAccess || PublicVisibility) && (!CourseForum || ReactivableByOwner || Admin || MembersAccess || PublicVisibility || FreeAccess) && (!CourseForum || ReactivableByOwner || Admin || MembersAccess || PublicVisibility || PublicVisibility) && (!CourseForum || ReactivableByOwner || Admin || MembersAccess || RegisteredUser || FreeAccess) && (!CourseForum || ReactivableByOwner || Admin || MembersAccess || RegisteredUser || PublicVisibility) && (!CourseForum || ReactivableByOwner || Admin || RegisteredUser || IdentifiedAccess || FreeAccess) && (!CourseForum || ReactivableByOwner || Admin || RegisteredUser || IdentifiedAccess || PublicVisibility) && (!CourseForum || ReactivableByOwner || Admin || RegisteredUser || PublicVisibility || FreeAccess) && (!CourseForum || ReactivableByOwner || Admin || RegisteredUser || PublicVisibility || PublicVisibility) && (!CourseForum || ReactivableByOwner || Admin || RegisteredUser || RegisteredUser || FreeAccess) && (!CourseForum || ReactivableByOwner || Admin || RegisteredUser || RegisteredUser || PublicVisibility) && (!CourseForum || Teacher || Admin || Available || AvailableFromTo) && (!CourseForum || Teacher || Admin || MembersAccess || IdentifiedAccess || FreeAccess) && (!CourseForum || Teacher || Admin || MembersAccess || IdentifiedAccess || PublicVisibility) && (!CourseForum || Teacher || Admin || MembersAccess || PublicVisibility || FreeAccess) && (!CourseForum || Teacher || Admin || MembersAccess || PublicVisibility || PublicVisibility) && (!CourseForum || Teacher || Admin || MembersAccess || RegisteredUser || FreeAccess) && (!CourseForum || Teacher || Admin || MembersAccess || RegisteredUser || PublicVisibility) && (!CourseForum || Teacher || Admin || RegisteredUser || IdentifiedAccess || FreeAccess) && (!CourseForum || Teacher || Admin || RegisteredUser || IdentifiedAccess || PublicVisibility) && (!CourseForum || Teacher || Admin || RegisteredUser || PublicVisibility || FreeAccess) && (!CourseForum || Teacher || Admin || RegisteredUser || PublicVisibility || PublicVisibility) && (!CourseForum || Teacher || Admin || RegisteredUser || RegisteredUser || FreeAccess) && (!CourseForum || Teacher || Admin || RegisteredUser || RegisteredUser || PublicVisibility) && (!CourseAgenda || ReactivableByOwner || Admin || Available || AvailableFromTo) && (!CourseAgenda || ReactivableByOwner || Admin || MembersAccess || IdentifiedAccess || FreeAccess) && (!CourseAgenda || ReactivableByOwner || Admin || MembersAccess || IdentifiedAccess || PublicVisibility) && (!CourseAgenda || ReactivableByOwner || Admin || MembersAccess || PublicVisibility || FreeAccess) && (!CourseAgenda || ReactivableByOwner || Admin || MembersAccess || PublicVisibility || PublicVisibility) && (!CourseAgenda || ReactivableByOwner || Admin || MembersAccess || RegisteredUser || FreeAccess) && (!CourseAgenda || ReactivableByOwner || Admin || MembersAccess || RegisteredUser || PublicVisibility) && (!CourseAgenda || ReactivableByOwner || Admin || RegisteredUser || IdentifiedAccess || FreeAccess) && (!CourseAgenda || ReactivableByOwner || Admin || RegisteredUser || IdentifiedAccess || PublicVisibility) && (!CourseAgenda || ReactivableByOwner || Admin || RegisteredUser || PublicVisibility || FreeAccess) && (!CourseAgenda || ReactivableByOwner || Admin || RegisteredUser || PublicVisibility || PublicVisibility) && (!CourseAgenda || ReactivableByOwner || Admin || RegisteredUser || RegisteredUser || FreeAccess) && (!CourseAgenda || ReactivableByOwner || Admin || RegisteredUser || RegisteredUser || PublicVisibility) && (!CourseAgenda || Teacher || Admin || Available || AvailableFromTo) && (!CourseAgenda || Teacher || Admin || MembersAccess || IdentifiedAccess || FreeAccess) && (!CourseAgenda || Teacher || Admin || MembersAccess || IdentifiedAccess || PublicVisibility) && (!CourseAgenda || Teacher || Admin || MembersAccess || PublicVisibility || FreeAccess) && (!CourseAgenda || Teacher || Admin || MembersAccess || PublicVisibility || PublicVisibility) && (!CourseAgenda || Teacher || Admin || MembersAccess || RegisteredUser || FreeAccess) && (!CourseAgenda || Teacher || Admin || MembersAccess || RegisteredUser || PublicVisibility) && (!CourseAgenda || Teacher || Admin || RegisteredUser || IdentifiedAccess || FreeAccess) && (!CourseAgenda || Teacher || Admin || RegisteredUser || IdentifiedAccess || PublicVisibility) && (!CourseAgenda || Teacher || Admin || RegisteredUser || PublicVisibility || FreeAccess) && (!CourseAgenda || Teacher || Admin || RegisteredUser || PublicVisibility || PublicVisibility) && (!CourseAgenda || Teacher || Admin || RegisteredUser || RegisteredUser || FreeAccess) && (!CourseAgenda || Teacher || Admin || RegisteredUser || RegisteredUser || PublicVisibility) && (!CourseAnnoucements || ReactivableByOwner || Admin || Available || AvailableFromTo) && (!CourseAnnoucements || ReactivableByOwner || Admin || MembersAccess || IdentifiedAccess || FreeAccess) && (!CourseAnnoucements || ReactivableByOwner || Admin || MembersAccess || IdentifiedAccess || PublicVisibility) && (!CourseAnnoucements || ReactivableByOwner || Admin || MembersAccess || PublicVisibility || FreeAccess) && (!CourseAnnoucements || ReactivableByOwner || Admin || MembersAccess || PublicVisibility || PublicVisibility) && (!CourseAnnoucements || ReactivableByOwner || Admin || MembersAccess || RegisteredUser || FreeAccess) && (!CourseAnnoucements || ReactivableByOwner || Admin || MembersAccess || RegisteredUser || PublicVisibility) && (!CourseAnnoucements || ReactivableByOwner || Admin || RegisteredUser || IdentifiedAccess || FreeAccess) && (!CourseAnnoucements || ReactivableByOwner || Admin || RegisteredUser || IdentifiedAccess || PublicVisibility) && (!CourseAnnoucements || ReactivableByOwner || Admin || RegisteredUser || PublicVisibility || FreeAccess) && (!CourseAnnoucements || ReactivableByOwner || Admin || RegisteredUser || PublicVisibility || PublicVisibility) && (!CourseAnnoucements || ReactivableByOwner || Admin || RegisteredUser || RegisteredUser || FreeAccess) && (!CourseAnnoucements || ReactivableByOwner || Admin || RegisteredUser || RegisteredUser || PublicVisibility) && (!CourseAnnoucements || Teacher || Admin || Available || AvailableFromTo) && (!CourseAnnoucements || Teacher || Admin || MembersAccess || IdentifiedAccess || FreeAccess) && (!CourseAnnoucements || Teacher || Admin || MembersAccess || IdentifiedAccess || PublicVisibility) && (!CourseAnnoucements || Teacher || Admin || MembersAccess || PublicVisibility || FreeAccess) && (!CourseAnnoucements || Teacher || Admin || MembersAccess || PublicVisibility || PublicVisibility) && (!CourseAnnoucements || Teacher || Admin || MembersAccess || RegisteredUser || FreeAccess) && (!CourseAnnoucements || Teacher || Admin || MembersAccess || RegisteredUser || PublicVisibility) && (!CourseAnnoucements || Teacher || Admin || RegisteredUser || IdentifiedAccess || FreeAccess) && (!CourseAnnoucements || Teacher || Admin || RegisteredUser || IdentifiedAccess || PublicVisibility) && (!CourseAnnoucements || Teacher || Admin || RegisteredUser || PublicVisibility || FreeAccess) && (!CourseAnnoucements || Teacher || Admin || RegisteredUser || PublicVisibility || PublicVisibility) && (!CourseAnnoucements || Teacher || Admin || RegisteredUser || RegisteredUser || FreeAccess) && (!CourseAnnoucements || Teacher || Admin || RegisteredUser || RegisteredUser || PublicVisibility) && (!CourseExercise || ReactivableByOwner || Admin || Available || AvailableFromTo) && (!CourseExercise || ReactivableByOwner || Admin || MembersAccess || IdentifiedAccess || FreeAccess) && (!CourseExercise || ReactivableByOwner || Admin || MembersAccess || IdentifiedAccess || PublicVisibility) && (!CourseExercise || ReactivableByOwner || Admin || MembersAccess || PublicVisibility || FreeAccess) && (!CourseExercise || ReactivableByOwner || Admin || MembersAccess || PublicVisibility || PublicVisibility) && (!CourseExercise || ReactivableByOwner || Admin || MembersAccess || RegisteredUser || FreeAccess) && (!CourseExercise || ReactivableByOwner || Admin || MembersAccess || RegisteredUser || PublicVisibility) && (!CourseExercise || ReactivableByOwner || Admin || RegisteredUser || IdentifiedAccess || FreeAccess) && (!CourseExercise || ReactivableByOwner || Admin || RegisteredUser || IdentifiedAccess || PublicVisibility) && (!CourseExercise || ReactivableByOwner || Admin || RegisteredUser || PublicVisibility || FreeAccess) && (!CourseExercise || ReactivableByOwner || Admin || RegisteredUser || PublicVisibility || PublicVisibility) && (!CourseExercise || ReactivableByOwner || Admin || RegisteredUser || RegisteredUser || FreeAccess) && (!CourseExercise || ReactivableByOwner || Admin || RegisteredUser || RegisteredUser || PublicVisibility) && (!CourseExercise || Teacher || Admin || Available || AvailableFromTo) && (!CourseExercise || Teacher || Admin || MembersAccess || IdentifiedAccess || FreeAccess) && (!CourseExercise || Teacher || Admin || MembersAccess || IdentifiedAccess || PublicVisibility) && (!CourseExercise || Teacher || Admin || MembersAccess || PublicVisibility || FreeAccess) && (!CourseExercise || Teacher || Admin || MembersAccess || PublicVisibility || PublicVisibility) && (!CourseExercise || Teacher || Admin || MembersAccess || RegisteredUser || FreeAccess) && (!CourseExercise || Teacher || Admin || MembersAccess || RegisteredUser || PublicVisibility) && (!CourseExercise || Teacher || Admin || RegisteredUser || IdentifiedAccess || FreeAccess) && (!CourseExercise || Teacher || Admin || RegisteredUser || IdentifiedAccess || PublicVisibility) && (!CourseExercise || Teacher || Admin || RegisteredUser || PublicVisibility || FreeAccess) && (!CourseExercise || Teacher || Admin || RegisteredUser || PublicVisibility || PublicVisibility) && (!CourseExercise || Teacher || Admin || RegisteredUser || RegisteredUser || FreeAccess) && (!CourseExercise || Teacher || Admin || RegisteredUser || RegisteredUser || PublicVisibility) && (!Claroline || User) && (!Claroline || Course) && (!Claroline || Subscription) && (UnregisteredUser || x62 || !User) && (RegisteredUser || !x62 || !User) && (!UnregisteredUser || x63 || !User) && (!RegisteredUser || !x63 || !User) && (!UnregisteredUser || User) && (Teacher || x64 || !RegisteredUser) && (!x65 || !RegisteredUser) && (Admin || x66 || !RegisteredUser) && (!x64 || x66 || !RegisteredUser) && (Admin || !x64 || x67 || !RegisteredUser) && (!x65 || x67 || !RegisteredUser) && (Admin || !x65 || !RegisteredUser) && (Student || !x67 || !RegisteredUser) && (!Teacher || x68 || !RegisteredUser) && (!Admin || x69 || !RegisteredUser) && (!x68 || x69 || !RegisteredUser) && (!Admin || !x68 || !RegisteredUser) && (!Student || !x69 || !RegisteredUser) && (!Teacher || RegisteredUser) && (!Admin || RegisteredUser) && (!Student || RegisteredUser) && (!RegisteredUser || User) && (!User || Claroline) && (!Course || Status) && (!Course || Registration) && (!Course || Access) && (!Course || Tools) && (!Course || Visibility) && (AvailableFromTo || x70 || !Status) && (!x71 || !Status) && (Available || x72 || !Status) && (!x70 || x72 || !Status) && (Available || !x70 || x73 || !Status) && (!x71 || x73 || !Status) && (Available || !x71 || !Status) && (Unavailable || !x73 || !Status) && (!AvailableFromTo || x74 || !Status) && (!Available || x75 || !Status) && (!x74 || x75 || !Status) && (!Available || !x74 || !Status) && (!Unavailable || !x75 || !Status) && (!AvailableFromTo || Status) && (!Available || Status) && (ReactivableByAdmin || x76 || !Unavailable) && (!x77 || !Unavailable) && (ReactivableByOwner || x78 || !Unavailable) && (!x76 || x78 || !Unavailable) && (ReactivableByOwner || !x76 || x79 || !Unavailable) && (!x77 || x79 || !Unavailable) && (ReactivableByOwner || !x77 || !Unavailable) && (Deleted || !x79 || !Unavailable) && (!ReactivableByAdmin || x80 || !Unavailable) && (!ReactivableByOwner || x81 || !Unavailable) && (!x80 || x81 || !Unavailable) && (!ReactivableByOwner || !x80 || !Unavailable) && (!Deleted || !x81 || !Unavailable) && (!ReactivableByAdmin || Unavailable) && (!ReactivableByOwner || Unavailable) && (!Deleted || Unavailable) && (!Unavailable || Status) && (!Status || Course) && (AllowedRegistration || x82 || !Registration) && (RegistrationDenied || !x82 || !Registration) && (!AllowedRegistration || x83 || !Registration) && (!RegistrationDenied || !x83 || !Registration) && (!AllowedRegistration || ArtificialParentWithKeyRegistration) && (!AllowedRegistration || ArtificialParentWithValidationRegistration) && (!WithKeyRegistration || ArtificialParentWithKeyRegistration) && (!ArtificialParentWithKeyRegistration || AllowedRegistration) && (!WithValidationRegistration || ArtificialParentWithValidationRegistration) && (!ArtificialParentWithValidationRegistration || AllowedRegistration) && (!AllowedRegistration || Registration) && (!RegistrationDenied || Registration) && (!Registration || Course) && (IdentifiedAccess || x84 || !Access) && (!x85 || !Access) && (MembersAccess || x86 || !Access) && (!x84 || x86 || !Access) && (MembersAccess || !x84 || x87 || !Access) && (!x85 || x87 || !Access) && (MembersAccess || !x85 || !Access) && (FreeAccess || !x87 || !Access) && (!IdentifiedAccess || x88 || !Access) && (!MembersAccess || x89 || !Access) && (!x88 || x89 || !Access) && (!MembersAccess || !x88 || !Access) && (!FreeAccess || !x89 || !Access) && (!IdentifiedAccess || Access) && (!MembersAccess || Access) && (!FreeAccess || Access) && (!Access || Course) && (!Tools || ArtificialParentCourseDiscussion) && (!Tools || ArtificialParentCourseGroup) && (!Tools || ArtificialParentCourseDocument) && (!Tools || ArtificialParentCourseExercise) && (!Tools || ArtificialParentCourseAgenda) && (!Tools || ArtificialParentCourseWork) && (!Tools || ArtificialParentCourseAnnoucements) && (!Tools || CourseIndex) && (!Tools || ArtificialParentCourseDescription) && (!Tools || ArtificialParentCourseForum) && (!Tools || ArtificialParentCourseWiki) && (!Tools || ArtificialParentCourseLearnPath) && (!Tools || ArtificialParentCourseUser) && (!CourseDiscussion || ArtificialParentCourseDiscussion) && (!ArtificialParentCourseDiscussion || Tools) && (!CourseGroup || ArtificialParentCourseGroup) && (!ArtificialParentCourseGroup || Tools) && (!CourseDocument || ArtificialParentCourseDocument) && (!ArtificialParentCourseDocument || Tools) && (!CourseExercise || ArtificialParentCourseExercise) && (!ArtificialParentCourseExercise || Tools) && (!CourseAgenda || ArtificialParentCourseAgenda) && (!ArtificialParentCourseAgenda || Tools) && (!CourseWork || ArtificialParentCourseWork) && (!ArtificialParentCourseWork || Tools) && (!CourseAnnoucements || ArtificialParentCourseAnnoucements) && (!ArtificialParentCourseAnnoucements || Tools) && (!CourseIndex || Tools) && (!CourseDescription || ArtificialParentCourseDescription) && (!ArtificialParentCourseDescription || Tools) && (!CourseForum || ArtificialParentCourseForum) && (!ArtificialParentCourseForum || Tools) && (!CourseWiki || ArtificialParentCourseWiki) && (!ArtificialParentCourseWiki || Tools) && (!CourseLearnPath || ArtificialParentCourseLearnPath) && (!ArtificialParentCourseLearnPath || Tools) && (!CourseUser || ArtificialParentCourseUser) && (!ArtificialParentCourseUser || Tools) && (!Tools || Course) && (MembersVisibility || x90 || !Visibility) && (PublicVisibility || !x90 || !Visibility) && (!MembersVisibility || x91 || !Visibility) && (!PublicVisibility || !x91 || !Visibility) && (!MembersVisibility || Visibility) && (!PublicVisibility || Visibility) && (!Visibility || Course) && (!Course || Claroline) && (!Subscription || ArtificialParentLostPassword) && (!Subscription || ArtificialParentOpenSubscription) && (!LostPassword || ArtificialParentLostPassword) && (!ArtificialParentLostPassword || Subscription) && (!OpenSubscription || ArtificialParentOpenSubscription) && (!ArtificialParentOpenSubscription || Subscription) && (!Subscription || Claroline) && (Claroline) \ No newline at end of file diff --git a/engine/crates/vts-synth/models/vibes/claroline.out b/engine/crates/vts-synth/models/vibes/claroline.out new file mode 100644 index 00000000..9ea2a4bd --- /dev/null +++ b/engine/crates/vts-synth/models/vibes/claroline.out @@ -0,0 +1,596 @@ +Features: {"Admin", "CourseAnnoucements", "OpenSubscription", "LostPassword", "RegisteredUser", "CourseAgenda", "CourseDescription", "Teacher", "CourseDocument", "CourseExercise", "CourseGroup", "CourseLearnPath", "CourseDiscussion", "CourseForum", "CourseUser", "CourseWiki", "CourseWork", "Visibility", "Course", "Access", "Tools", "x65", "x64", "x67", "Student", "x69", "x66", "x68", "ReactivableByOwner", "PublicVisibility", "CourseIndex", "Available", "AvailableFromTo", "MembersVisibility", "ArtificialParentCourseUser", "x90", "x91", "ArtificialParentCourseDescription", "ArtificialParentCourseAnnoucements", "ArtificialParentCourseForum", "MembersAccess", "IdentifiedAccess", "FreeAccess", "x84", "x87", "x86", "x85", "x89", "x88", "User", "Claroline", "x62", "UnregisteredUser", "x63", "ArtificialParentLostPassword", "Subscription", "ArtificialParentOpenSubscription", "RegistrationDenied", "x83", "Registration", "AllowedRegistration", "x82", "Status", "ArtificialParentWithValidationRegistration", "WithValidationRegistration", "ArtificialParentWithKeyRegistration", "WithKeyRegistration", "x80", "Unavailable", "x81", "ReactivableByAdmin", "Deleted", "x79", "x77", "x76", "x78", "x70", "x73", "x71", "x75", "x74", "x72", "ArtificialParentCourseWork", "ArtificialParentCourseAgenda", "ArtificialParentCourseLearnPath", "ArtificialParentCourseDiscussion", "ArtificialParentCourseGroup", "ArtificialParentCourseDocument", "ArtificialParentCourseWiki", "ArtificialParentCourseExercise"} +Observables: {"clic(/claroline/admin/admin_category.php)", "clic(/claroline/admin/admin_class.php)", "clic(/claroline/admin/admin_courses.php)", "clic(/claroline/admin/admin_profile.php)", "clic(/claroline/admin/admin_user_course_settings.php)", "clic(/claroline/admin/admin_users.php)", "clic(/claroline/admin/adminaddnewuser.php)", "clic(/claroline/admin/admincourseusers.php)", "clic(/claroline/admin/adminmailsystem.php)", "clic(/claroline/admin/adminmergeuser.php)", "clic(/claroline/admin/adminregisteruser.php)", "clic(/claroline/admin/adminusercourses.php)", "clic(/claroline/admin/adminuserdeleted.php)", "clic(/claroline/admin/adminuserunregistered.php)", "clic(/claroline/admin/advanced_course_search.php)", "clic(/claroline/admin/advanced_user_search.php)", "clic(/claroline/admin/campusProblem.php)", "clic(/claroline/admin/clarolinenews.php)", "clic(/claroline/admin/index.php)", "clic(/claroline/admin/managing/editFile.php)", "clic(/claroline/admin/module/module_list.php)", "clic(/claroline/admin/module/module.php)", "clic(/claroline/admin/right/profile_list.php)", "clic(/claroline/admin/technical/files_stats.php)", "clic(/claroline/admin/technical/phpInfo.php)", "clic(/claroline/admin/tool/config_edit.php)", "clic(/claroline/admin/tool/config_list.php)", "clic(/claroline/admin/upgrade/upgrade_conf.php)", "clic(/claroline/admin/upgrade/upgrade_courses.php)", "clic(/claroline/admin/upgrade/upgrade_main_db.php)", "clic(/claroline/admin/upgrade/upgrade_modules.php)", "clic(/claroline/admin/upgrade/upgrade.php)", "clic(/claroline/announcements/announcements.php)", "clic(/claroline/auth/courses.php)", "clic(/claroline/auth/inscription.php)", "clic(/claroline/auth/login.php)", "clic(/claroline/auth/lostPassword.php)", "clic(/claroline/auth/profile.php)", "clic(/claroline/backends/download.php)", "clic(/claroline/backends/ical.php)", "clic(/claroline/calendar/agenda.php)", "clic(/claroline/course_description/index.php)", "clic(/claroline/course/create.php)", "clic(/claroline/course/index.php)", "clic(/claroline/course/platform_courses.php)", "clic(/claroline/course/settings.php)", "clic(/claroline/course/tools.php)", "clic(/claroline/desktop/config.php)", "clic(/claroline/desktop/index.php)", "clic(/claroline/document/document.php)", "clic(/claroline/document/rqmkhtml.php)", "clic(/claroline/exercise/admin/edit_answers.php)", "clic(/claroline/exercise/admin/edit_exercise.php)", "clic(/claroline/exercise/admin/edit_question.php)", "clic(/claroline/exercise/admin/question_category.php)", "clic(/claroline/exercise/admin/question_pool.php)", "clic(/claroline/exercise/exercise_submit.php)", "clic(/claroline/exercise/exercise.php)", "clic(/claroline/group/group_edit.php)", "clic(/claroline/group/group_properties.php)", "clic(/claroline/group/group_space.php)", "clic(/claroline/group/group.php)", "clic(/claroline/index.php)", "clic(/claroline/learnPath/importLearningPath.php)", "clic(/claroline/learnPath/insertMyDoc.php)", "clic(/claroline/learnPath/insertMyExercise.php)", "clic(/claroline/learnPath/insertMyModule.php)", "clic(/claroline/learnPath/learningPath.php)", "clic(/claroline/learnPath/learningPathAdmin.php)", "clic(/claroline/learnPath/learningPathList.php)", "clic(/claroline/learnPath/module.php)", "clic(/claroline/learnPath/modules_pool.php)", "clic(/claroline/messaging/admin_delete.php)", "clic(/claroline/messaging/admin_search.php)", "clic(/claroline/messaging/admin.php)", "clic(/claroline/messaging/index.php)", "clic(/claroline/messaging/messagebox.php)", "clic(/claroline/messaging/messagescourse.php)", "clic(/claroline/messaging/readmessage.php)", "clic(/claroline/messaging/sendmessage.php)", "clic(/claroline/module/CLCHAT/index.php)", "clic(/claroline/notification_date.php)", "clic(/claroline/phpbb/index.php)", "clic(/claroline/phpbb/viewforum.php)", "clic(/claroline/phpbb/viewtopic.php)", "clic(/claroline/tracking/course_access_details.php)", "clic(/claroline/tracking/courseReport.php)", "clic(/claroline/tracking/delete_course_stats.php)", "clic(/claroline/tracking/learnPath_details.php)", "clic(/claroline/tracking/lp_modules_details.php)", "clic(/claroline/tracking/platform_access_details.php)", "clic(/claroline/tracking/platform_report.php)", "clic(/claroline/tracking/userReport.php)", "clic(/claroline/user/addcsvusers.php)", "clic(/claroline/user/class_add.php)", "clic(/claroline/user/user_add.php)", "clic(/claroline/user/user_pictures.php)", "clic(/claroline/user/user.php)", "clic(/claroline/user/userInfo.php)", "clic(/claroline/wiki/page.php)", "clic(/claroline/wiki/wiki.php)", "clic(/claroline/work/feedback.php)", "clic(/claroline/work/user_work.php)", "clic(/claroline/work/work_list.php)", "clic(/claroline/work/work.php)", "clic(0)"} + +States: 106 (initial: 1) +Transitions: 11236 +SCCs: 1 +States: 10891 +States: 20856 +States: 30829 +States: 40808 +States: 50849 +States: 60779 +States: 70682 +States: 80701 +States: 90761 +States: 100850 +States: 110822 +States: 120711 +States: 130782 +States: 140724 +States: 150706 +States: 160635 +States: 170749 +States: 180654 +States: 190839 +States: 200781 +States: 210772 +States: 220801 +States: 230734 +States: 240806 +States: 250771 +States: 260738 +States: 270564 +States: 280499 +States: 290743 +States: 300728 +States: 310689 +States: 320703 +States: 330719 +States: 340650 +States: 350716 +States: 360548 +States: 370487 +States: 380842 +States: 390792 +States: 400761 +States: 410695 +States: 420741 +States: 430750 +States: 440763 +States: 450632 +States: 460855 +States: 470647 +States: 480657 +States: 490620 +States: 500617 +States: 510634 +States: 520530 +States: 530554 +States: 540590 +States: 550760 +States: 560734 +States: 570634 +States: 580608 +States: 590737 +States: 600673 +States: 610640 +States: 620549 +States: 630719 +States: 640653 +States: 650626 +States: 660584 +States: 670622 +States: 680605 +States: 690533 +States: 700510 +States: 710862 +States: 720685 +States: 730767 +States: 740728 +States: 750690 +States: 760702 +States: 770748 +States: 780843 +States: 790578 +States: 800512 +States: 810765 +States: 820581 +States: 830707 +States: 840607 +States: 850511 +States: 860599 +States: 870613 +States: 880594 +States: 890538 +States: 900692 +States: 910768 +States: 920687 +States: 930589 +States: 940520 +States: 950599 +States: 960657 +States: 970613 +States: 980549 +States: 990667 +States: 1000573 +States: 1010652 +States: 1020454 +States: 1030463 +States: 1040514 +States: 1050534 +States: 1060545 +States: 1070416 +States: 1080697 +States: 1090695 +States: 1100649 +States: 1110673 +States: 1120773 +States: 1130716 +States: 1140529 +States: 1150583 +States: 1160527 +States: 1170541 +States: 1180536 +States: 1190541 +States: 1200509 +States: 1210613 +States: 1220572 +States: 1230515 +States: 1240439 +States: 1250652 +States: 1260594 +States: 1270571 +States: 1280522 +States: 1290609 +States: 1300528 +States: 1310514 +States: 1320554 +States: 1330575 +States: 1340557 +States: 1350584 +States: 1360487 +States: 1370561 +States: 1380502 +States: 1390521 +States: 1400352 +States: 1410828 +States: 1420637 +States: 1430763 +States: 1440748 +States: 1450612 +States: 1460738 +States: 1470667 +States: 1480783 +States: 1490678 +States: 1500748 +States: 1510722 +States: 1520607 +States: 1530679 +States: 1540618 +States: 1550747 +States: 1560626 +States: 1570460 +States: 1580590 +States: 1590453 +States: 1600701 +States: 1610743 +States: 1620662 +States: 1630628 +States: 1640687 +States: 1650638 +States: 1660492 +States: 1670551 +States: 1680467 +States: 1690587 +States: 1700583 +States: 1710593 +States: 1720573 +States: 1730625 +States: 1740557 +States: 1750571 +States: 1760535 +States: 1770820 +States: 1780677 +States: 1790634 +States: 1800599 +States: 1810576 +States: 1820672 +States: 1830569 +States: 1840648 +States: 1850551 +States: 1860678 +States: 1870630 +States: 1880621 +States: 1890533 +States: 1900644 +States: 1910475 +States: 1920469 +States: 1930488 +States: 1940640 +States: 1950673 +States: 1960690 +States: 1970584 +States: 1980626 +States: 1990568 +States: 2000505 +States: 2010423 +States: 2020359 +States: 2030513 +States: 2040461 +States: 2050402 +States: 2060575 +States: 2070574 +States: 2080573 +States: 2090373 +States: 2100687 +States: 2110626 +States: 2120603 +States: 2130640 +States: 2140558 +States: 2150727 +States: 2160587 +States: 2170647 +States: 2180622 +States: 2190509 +States: 2200588 +States: 2210558 +States: 2220524 +States: 2230526 +States: 2240637 +States: 2250550 +States: 2260627 +States: 2270448 +States: 2280365 +States: 2290612 +States: 2300577 +States: 2310507 +States: 2320548 +States: 2330519 +States: 2340512 +States: 2350538 +States: 2360472 +States: 2370574 +States: 2380504 +States: 2390426 +States: 2400497 +States: 2410446 +States: 2420445 +States: 2430400 +States: 2440470 +States: 2450401 +States: 2460669 +States: 2470657 +States: 2480659 +States: 2490640 +States: 2500713 +States: 2510516 +States: 2520490 +States: 2530426 +States: 2540439 +States: 2550512 +States: 2560423 +States: 2570458 +States: 2580371 +States: 2590441 +States: 2600385 +States: 2610354 +States: 2620347 +States: 2630512 +States: 2640540 +States: 2650492 +States: 2660441 +States: 2670500 +States: 2680512 +States: 2690335 +States: 2700411 +States: 2710522 +States: 2720465 +States: 2730442 +States: 2740391 +States: 2750379 +States: 2760327 +States: 2770246 +States: 2780226 +States: 2790775 +States: 2800739 +States: 2810765 +States: 2820677 +States: 2830843 +States: 2840666 +States: 2850596 +States: 2860659 +States: 2870609 +States: 2880719 +States: 2890611 +States: 2900702 +States: 2910614 +States: 2920747 +States: 2930550 +States: 2940520 +States: 2950555 +States: 2960437 +States: 2970715 +States: 2980715 +States: 2990609 +States: 3000665 +States: 3010685 +States: 3020581 +States: 3030642 +States: 3040545 +States: 3050689 +States: 3060608 +States: 3070582 +States: 3080555 +States: 3090531 +States: 3100499 +States: 3110448 +States: 3120502 +States: 3130464 +States: 3140723 +States: 3150756 +States: 3160658 +States: 3170571 +States: 3180650 +States: 3190699 +States: 3200600 +States: 3210441 +States: 3220532 +States: 3230573 +States: 3240491 +States: 3250491 +States: 3260718 +States: 3270503 +States: 3280429 +States: 3290530 +States: 3300719 +States: 3310720 +States: 3320505 +States: 3330499 +States: 3340691 +States: 3350539 +States: 3360430 +States: 3370414 +States: 3380371 +States: 3390430 +States: 3400413 +States: 3410460 +States: 3420541 +States: 3430435 +States: 3440451 +States: 3450391 +States: 3460717 +States: 3470721 +States: 3480557 +States: 3490581 +States: 3500600 +States: 3510617 +States: 3520658 +States: 3530532 +States: 3540533 +States: 3550657 +States: 3560616 +States: 3570640 +States: 3580540 +States: 3590472 +States: 3600485 +States: 3610414 +States: 3620542 +States: 3630457 +States: 3640639 +States: 3650553 +States: 3660602 +States: 3670548 +States: 3680554 +States: 3690479 +States: 3700424 +States: 3710419 +States: 3720416 +States: 3730553 +States: 3740530 +States: 3750509 +States: 3760391 +States: 3770387 +States: 3780465 +States: 3790435 +States: 3800421 +States: 3810662 +States: 3820585 +States: 3830657 +States: 3840535 +States: 3850641 +States: 3860574 +States: 3870529 +States: 3880484 +States: 3890719 +States: 3900419 +States: 3910357 +States: 3920531 +States: 3930544 +States: 3940454 +States: 3950487 +States: 3960426 +States: 3970577 +States: 3980522 +States: 3990509 +States: 4000495 +States: 4010475 +States: 4020474 +States: 4030433 +States: 4040446 +States: 4050513 +States: 4060401 +States: 4070378 +States: 4080313 +States: 4090340 +States: 4100303 +States: 4110348 +States: 4120207 +States: 4130767 +States: 4140607 +States: 4150651 +States: 4160667 +States: 4170762 +States: 4180549 +States: 4190489 +States: 4200566 +States: 4210464 +States: 4220592 +States: 4230579 +States: 4240568 +States: 4250521 +States: 4260544 +States: 4270592 +States: 4280494 +States: 4290539 +States: 4300743 +States: 4310548 +States: 4320562 +States: 4330528 +States: 4340431 +States: 4350558 +States: 4360541 +States: 4370503 +States: 4380471 +States: 4390490 +States: 4400531 +States: 4410449 +States: 4420475 +States: 4430521 +States: 4440473 +States: 4450447 +States: 4460396 +States: 4470648 +States: 4480679 +States: 4490516 +States: 4500504 +States: 4510434 +States: 4520528 +States: 4530472 +States: 4540480 +States: 4550310 +States: 4560515 +States: 4570420 +States: 4580416 +States: 4590520 +States: 4600479 +States: 4610522 +States: 4620412 +States: 4630594 +States: 4640508 +States: 4650552 +States: 4660488 +States: 4670498 +States: 4680459 +States: 4690518 +States: 4700384 +States: 4710494 +States: 4720487 +States: 4730419 +States: 4740329 +States: 4750428 +States: 4760295 +States: 4770268 +States: 4780143 +States: 4790540 +States: 4800610 +States: 4810512 +States: 4820544 +States: 4830483 +States: 4840525 +States: 4850515 +States: 4860468 +States: 4870612 +States: 4880427 +States: 4890382 +States: 4900454 +States: 4910406 +States: 4920470 +States: 4930376 +States: 4940365 +States: 4950299 +States: 4960538 +States: 4970459 +States: 4980529 +States: 4990443 +States: 5000546 +States: 5010394 +States: 5020282 +States: 5030291 +States: 5040537 +States: 5050336 +States: 5060335 +States: 5070322 +States: 5080327 +States: 5090381 +States: 5100316 +States: 5110285 +States: 5120276 +States: 5130470 +States: 5140479 +States: 5150517 +States: 5160429 +States: 5170532 +States: 5180347 +States: 5190301 +States: 5200334 +States: 5210534 +States: 5220427 +States: 5230402 +States: 5240272 +States: 5250376 +States: 5260188 +States: 5270271 +States: 5280149 +States: 5290346 +States: 5300302 +States: 5310305 +States: 5320542 +States: 5330241 +States: 5340204 +States: 5350293 +States: 5360438 +States: 5370392 +States: 5380271 +States: 5390207 +States: 5400277 +States: 5410246 +States: 5420189 +States: 5430115 + +== Initial VTS == +States: 5431296 (initial: 1) +Transitions: 575717376 +SCCs: 65536 +Deterministic: true +Monotonic: true + +== Refined VTS == +States: 5431296 (initial: 1) +Transitions: 575717376 +SCCs: 65536 +Deterministic: true +Monotonic: true + +== Observability Projection == +States: 5431296 (initial: 1) +Transitions: 575717376 +SCCs: 65536 +Deterministic: true +Monotonic: true + +== Determinize == +States: 5431296 (initial: 1) +Transitions: 575717376 +SCCs: 65536 + +== Flatten Beliefs == +States: 5431296 (initial: 1) +Transitions: 575717376 +SCCs: 65536 +Deterministic: true +Monotonic: true + +== Compress SCCs == +States: 65536 (initial: 1) +Transitions: 6946816 +SCCs: 65536 +Deterministic: true +Monotonic: true + +== Compress Transitions == +States: 65536 (initial: 1) +Transitions: 6946816 +SCCs: 65536 +Deterministic: true +Monotonic: true diff --git a/engine/crates/vts-synth/models/vibes/claroline.tvl b/engine/crates/vts-synth/models/vibes/claroline.tvl new file mode 100644 index 00000000..28ef79a9 --- /dev/null +++ b/engine/crates/vts-synth/models/vibes/claroline.tvl @@ -0,0 +1,190 @@ + +root Claroline { + group allof{ + User, + Course, + Subscription + } +} + +/* + * The type of user loged in + */ +User{ + group oneof{ + UnregisteredUser, + RegisteredUser group oneof{ + Admin, + Teacher, + Student + } + } +} + + +Course{ + group allof { + Access group oneof{ + FreeAccess, + IdentifiedAccess, + MembersAccess + }, + Registration group oneof{ + AllowedRegistration group allof{ + opt WithValidationRegistration, + opt WithKeyRegistration + }, + RegistrationDenied + }, + Visibility group oneof{ + PublicVisibility, + MembersVisibility + }, + Tools group allof{ + CourseIndex, // /claroline/claroline/course/index.php + opt CourseDescription, // /claroline/claroline/course_description/index.php + opt CourseAgenda, // /claroline/claroline/calendar/agenda.php + opt CourseAnnoucements, // /claroline/claroline/announcements/announcements.php + opt CourseDocument, // /claroline/claroline/document/document.php + opt CourseExercise, // /claroline/claroline/exercise/exercise.php + opt CourseLearnPath, // /claroline/claroline/learnPath/learningPathList.php + opt CourseWork, // /claroline/claroline/work/work.php + opt CourseForum, // /claroline/claroline/phpbb/index.php + opt CourseGroup, // /claroline/claroline/group/group.php + opt CourseUser, // /claroline/claroline/user/user.php + opt CourseWiki, // /claroline/claroline/wiki/wiki.php + opt CourseDiscussion // /claroline/module/CLCHAT/index.php + }, + Status group oneof{ + Available, + AvailableFromTo, + Unavailable group oneOf{ + ReactivableByOwner, + ReactivableByAdmin, + Deleted + } + } + } +} + +CourseIndex{ + ifin: ((Available || AvailableFromTo) && PublicVisibility) + || ((Available || AvailableFromTo) && MembersVisibility && RegisteredUser) + || Admin + || (ReactivableByOwner && Teacher); +} + +CourseDescription{ + ifin: ((Available || AvailableFromTo) + && ((FreeAccess && PublicVisibility) + || (IdentifiedAccess && PublicVisibility && RegisteredUser) + || (MembersAccess && RegisteredUser) )) + || Admin + || (ReactivableByOwner && Teacher); +} + +CourseAgenda{ + ifin: ((Available || AvailableFromTo) + && ((FreeAccess && PublicVisibility) + || (IdentifiedAccess && PublicVisibility && RegisteredUser) + || (MembersAccess && RegisteredUser) )) + || Admin + || (ReactivableByOwner && Teacher); +} + +CourseAnnoucements{ + ifin: ((Available || AvailableFromTo) + && ((FreeAccess && PublicVisibility) + || (IdentifiedAccess && PublicVisibility && RegisteredUser) + || (MembersAccess && RegisteredUser) )) + || Admin + || (ReactivableByOwner && Teacher); +} + +CourseDocument{ + ifin: ((Available || AvailableFromTo) + && ((FreeAccess && PublicVisibility) + || (IdentifiedAccess && PublicVisibility && RegisteredUser) + || (MembersAccess && RegisteredUser) )) + || Admin + || (ReactivableByOwner && Teacher); +} + +CourseExercise{ + ifin: ((Available || AvailableFromTo) + && ((FreeAccess && PublicVisibility) + || (IdentifiedAccess && PublicVisibility && RegisteredUser) + || (MembersAccess && RegisteredUser) )) + || Admin + || (ReactivableByOwner && Teacher); +} + +CourseLearnPath{ + ifin: ((Available || AvailableFromTo) + && ((FreeAccess && PublicVisibility) + || (IdentifiedAccess && PublicVisibility && RegisteredUser) + || (MembersAccess && RegisteredUser) )) + || Admin + || (ReactivableByOwner && Teacher); +} + +CourseWork{ + ifin: ((Available || AvailableFromTo) + && ((FreeAccess && PublicVisibility) + || (IdentifiedAccess && PublicVisibility && RegisteredUser) + || (MembersAccess && RegisteredUser) )) + || Admin + || (ReactivableByOwner && Teacher); +} + +CourseForum{ + ifin: ((Available || AvailableFromTo) + && ((FreeAccess && PublicVisibility) + || (IdentifiedAccess && PublicVisibility && RegisteredUser) + || (MembersAccess && RegisteredUser) )) + || Admin + || (ReactivableByOwner && Teacher); +} + +CourseGroup{ + ifin: ((Available || AvailableFromTo) + && ((FreeAccess && PublicVisibility) + || (IdentifiedAccess && PublicVisibility && RegisteredUser) + || (MembersAccess && RegisteredUser) )) + || Admin + || (ReactivableByOwner && Teacher); +} + +CourseUser{ + ifin: ((Available || AvailableFromTo) + && ((FreeAccess && PublicVisibility) + || (IdentifiedAccess && PublicVisibility && RegisteredUser) + || (MembersAccess && RegisteredUser) )) + || Admin + || (ReactivableByOwner && Teacher); +} + +CourseWiki{ + ifin: ((Available || AvailableFromTo) + && ((FreeAccess && PublicVisibility) + || (IdentifiedAccess && PublicVisibility && RegisteredUser) + || (MembersAccess && RegisteredUser) )) + || Admin + || (ReactivableByOwner && Teacher); +} + +CourseDiscussion{ + ifin: ((Available || AvailableFromTo) + && ((FreeAccess && PublicVisibility) + || (IdentifiedAccess && PublicVisibility && RegisteredUser) + || (MembersAccess && RegisteredUser) )) + || Admin + || (ReactivableByOwner && Teacher); +} + +Subscription{ + group allof{ + opt OpenSubscription, // /claroline/claroline/auth/inscription.php + opt LostPassword + } +} \ No newline at end of file diff --git a/engine/crates/vts-synth/models/vibes/cpterminal.fm b/engine/crates/vts-synth/models/vibes/cpterminal.fm new file mode 100644 index 00000000..f2ff3c7e --- /dev/null +++ b/engine/crates/vts-synth/models/vibes/cpterminal.fm @@ -0,0 +1 @@ +(!DebitCard || PIN) && (Chip || NFC || !EPurse) && (Chip || NFC || !PrepaidCard) && (!CreditCard || Identification) && (!CPTerminal || CardReader) && (!CPTerminal || Connectivity) && (!CPTerminal || ArtificialParentIdentification) && (!CPTerminal || PaymentSchema) && (!CardReader || MagStrip || NFC || Chip) && (!MagStrip || CardReader) && (!NFC || CardReader) && (!Chip || CardReader) && (!CardReader || CPTerminal) && (!Connectivity || Offline || Online) && (!Offline || Connectivity) && (!Online || Mobile3G || VPN || PSTN || PrivateWAN) && (!Mobile3G || Online) && (!VPN || Online) && (!PSTN || Online) && (!PrivateWAN || Online) && (!Online || Connectivity) && (!Connectivity || CPTerminal) && (!Identification || Signature) && (!Identification || ArtificialParentPIN) && (!Signature || Identification) && (!PIN || ArtificialParentPIN) && (!ArtificialParentPIN || Identification) && (!Identification || ArtificialParentIdentification) && (!ArtificialParentIdentification || CPTerminal) && (!PaymentSchema || DirectDebit || CreditCard) && (!DirectDebit || PrepaidCard || EPurse || DebitCard) && (!PrepaidCard || DirectDebit) && (!EPurse || DirectDebit) && (!DebitCard || DirectDebit) && (!DirectDebit || PaymentSchema) && (!CreditCard || PaymentSchema) && (!PaymentSchema || CPTerminal) && (CPTerminal) \ No newline at end of file diff --git a/engine/crates/vts-synth/models/vibes/cpterminal.fts.xml b/engine/crates/vts-synth/models/vibes/cpterminal.fts.xml new file mode 100644 index 00000000..6d25bf23 --- /dev/null +++ b/engine/crates/vts-synth/models/vibes/cpterminal.fts.xml @@ -0,0 +1,45 @@ + + + Init + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/engine/crates/vts-synth/models/vibes/cpterminal.out b/engine/crates/vts-synth/models/vibes/cpterminal.out new file mode 100644 index 00000000..66dccbfd --- /dev/null +++ b/engine/crates/vts-synth/models/vibes/cpterminal.out @@ -0,0 +1,57 @@ +Features: {"CreditCard", "DirectDebit", "Signature", "PIN", "Online", "Offline", "DebitCard", "Chip", "NFC", "EPurse", "PrepaidCard", "Identification", "CPTerminal", "CardReader", "Connectivity", "ArtificialParentIdentification", "PaymentSchema", "MagStrip", "Mobile3G", "VPN", "PSTN", "PrivateWAN", "ArtificialParentPIN"} +Observables: {"insert_card", "initSchema", "init_schema", "abort", "check_signature", "check_PIN_online", "check_PIN_offline", "remove_card", "no_go", "go_online", "go_offline", "ask_issuer", "accepts", "issuer_accepts", "issuer_rejects"} + +States: 11 (initial: 1) +Transitions: 17 +SCCs: 1 + +== Initial VTS == +States: 104 (initial: 1) +Transitions: 163 +SCCs: 14 +Deterministic: true +Monotonic: true + +== Refined VTS == +States: 102 (initial: 1) +Transitions: 161 +SCCs: 12 +Deterministic: true +Monotonic: true + +== Observability Projection == +States: 102 (initial: 1) +Transitions: 161 +SCCs: 12 +Deterministic: true +Monotonic: true + +== Determinize == +States: 102 (initial: 1) +Transitions: 161 +SCCs: 12 + +== Flatten Beliefs == +States: 102 (initial: 1) +Transitions: 161 +SCCs: 12 +Deterministic: true +Monotonic: true + +== Compress SCCs == +States: 12 (initial: 1) +Transitions: 142 +SCCs: 12 +Deterministic: true +Monotonic: true + +== Compress Transitions == +States: 12 (initial: 1) +Transitions: 142 +SCCs: 12 +Deterministic: true +Monotonic: true + +real 0m0.126s +user 0m0.087s +sys 0m0.044s \ No newline at end of file diff --git a/engine/crates/vts-synth/models/vibes/cpterminal.tvl b/engine/crates/vts-synth/models/vibes/cpterminal.tvl new file mode 100644 index 00000000..7e56d0c4 --- /dev/null +++ b/engine/crates/vts-synth/models/vibes/cpterminal.tvl @@ -0,0 +1,74 @@ + +/* +Card Payment Terminal root feature. +*/ +root CPTerminal { + group allof { + PaymentSchema, + Connectivity, + CardReader, + opt Identification + } +} + +/* +Payment schema is either direct debit card or credit card (Visa, Mastercard, AMEX). +*/ +PaymentSchema { + group someof { + DirectDebit, + CreditCard + } + /* CreditCard requires an identification */ + CreditCard -> Identification; +} + +/* +Direct debit card is either a debit card (BCMC, Maestro, V-Pay, Visa Electron), an electronic purse (Proton) or a prepaid card (usually made by Visa or Mastercard) +*/ +DirectDebit{ + group someof { + DebitCard, + EPurse, + PrepaidCard + } + /* EPurse or prepaid needs to store information on the card */ + (EPurse || PrepaidCard) -> (Chip || NFC); + /* DebitCard requires a PIN identification */ + DebitCard -> PIN; +} + +Connectivity{ + group someof { + Online, + Offline + } +} + +Online{ + group someof { + /* Dial-up connection */ + PSTN, + /* Connection using mobile network */ + Mobile3G, + /* VPN connection over an internet access */ + VPN, + /* Dedicated WAN */ + PrivateWAN + } +} + +CardReader{ + group someof { + Chip, + MagStrip, + NFC + } +} + +Identification{ + group allof { + opt PIN, + Signature + } +} diff --git a/engine/crates/vts-synth/models/vibes/minepump.fm b/engine/crates/vts-synth/models/vibes/minepump.fm new file mode 100644 index 00000000..f9a78c28 --- /dev/null +++ b/engine/crates/vts-synth/models/vibes/minepump.fm @@ -0,0 +1 @@ +(!MinePumpSys || ArtificialParentMethaneDetect) && (!MinePumpSys || WaterRegulation) && (!MinePumpSys || ArtificialParentCommand) && (!MethaneDetect || ArtificialParentMethaneDetect) && (!ArtificialParentMethaneDetect || MinePumpSys) && (!WaterRegulation || High) && (!WaterRegulation || ArtificialParentNormal) && (!WaterRegulation || ArtificialParentLow) && (!High || WaterRegulation) && (!Normal || ArtificialParentNormal) && (!ArtificialParentNormal || WaterRegulation) && (!Low || ArtificialParentLow) && (!ArtificialParentLow || WaterRegulation) && (!WaterRegulation || MinePumpSys) && (!Command || Start || Stop) && (!Start || Command) && (!Stop || Command) && (!Command || ArtificialParentCommand) && (!ArtificialParentCommand || MinePumpSys) && (MinePumpSys) \ No newline at end of file diff --git a/engine/crates/vts-synth/models/vibes/minepump.fts.xml b/engine/crates/vts-synth/models/vibes/minepump.fts.xml new file mode 100644 index 00000000..6cabd9cf --- /dev/null +++ b/engine/crates/vts-synth/models/vibes/minepump.fts.xml @@ -0,0 +1,97 @@ + + + s6 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/engine/crates/vts-synth/models/vibes/minepump.out b/engine/crates/vts-synth/models/vibes/minepump.out new file mode 100644 index 00000000..186970fc --- /dev/null +++ b/engine/crates/vts-synth/models/vibes/minepump.out @@ -0,0 +1,57 @@ +Features: {"Command", "MethaneDetect", "WaterRegulation", "Stop", "Start", "Normal", "High", "Low", "MinePump", "WaterSensor", "MethaneSensor", "MethaneAlarm", "MethaneQuery"} +Observables: {"receiveMsg", "commandMsg", "palarmMsg", "levelMsg", "stopCmd", "startCmd", "isRunning", "isNotRunning", "end", "highLevel", "lowLevel", "isReady", "pumpStop", "setStop", "setReady", "setMethaneStop", "isLowStop", "isStopped", "isMethaneStop", "isNotReady", "pumpStart", "setRunning", "setLowStop"} + +States: 25 (initial: 1) +Transitions: 41 +SCCs: 1 + +== Initial VTS == +States: 928 (initial: 1) +Transitions: 1600 +SCCs: 89 +Deterministic: true +Monotonic: true + +== Refined VTS == +States: 928 (initial: 1) +Transitions: 1600 +SCCs: 89 +Deterministic: true +Monotonic: true + +== Observability Projection == +States: 928 (initial: 1) +Transitions: 1600 +SCCs: 89 +Deterministic: true +Monotonic: true + +== Determinize == +States: 928 (initial: 1) +Transitions: 1600 +SCCs: 89 + +== Flatten Beliefs == +States: 928 (initial: 1) +Transitions: 1600 +SCCs: 89 +Deterministic: true +Monotonic: true + +== Compress SCCs == +States: 447 (initial: 1) +Transitions: 1360 +SCCs: 89 +Deterministic: true +Monotonic: true + +== Compress Transitions == +States: 389 (initial: 1) +Transitions: 1360 +SCCs: 89 +Deterministic: true +Monotonic: true + +real 0m0.131s +user 0m0.096s +sys 0m0.041s diff --git a/engine/crates/vts-synth/models/vibes/minepump.tvl b/engine/crates/vts-synth/models/vibes/minepump.tvl new file mode 100644 index 00000000..ca95dca9 --- /dev/null +++ b/engine/crates/vts-synth/models/vibes/minepump.tvl @@ -0,0 +1,25 @@ + +root MinePumpSys { + group allof { + opt Command, + opt MethaneDetect, + WaterRegulation + } +} + + +root Command { + group someof { + Start, + Stop + } +} + + +root WaterRegulation { + group allof { + opt Low, + opt Normal, + High + } +} \ No newline at end of file diff --git a/engine/crates/vts-synth/models/vibes/svm.fm b/engine/crates/vts-synth/models/vibes/svm.fm new file mode 100644 index 00000000..c64e9c7d --- /dev/null +++ b/engine/crates/vts-synth/models/vibes/svm.fm @@ -0,0 +1 @@ +(!VendingMachine || ArtificialParentFreeDrinks) && (!VendingMachine || Currency) && (!VendingMachine || ArtificialParentCancelPurchase) && (!VendingMachine || Beverages) && (!FreeDrinks || ArtificialParentFreeDrinks) && (!ArtificialParentFreeDrinks || VendingMachine) && (Dollar || x13 || !Currency) && (Euro || !x13 || !Currency) && (!Dollar || x14 || !Currency) && (!Euro || !x14 || !Currency) && (!Dollar || Currency) && (!Euro || Currency) && (!Currency || VendingMachine) && (!CancelPurchase || ArtificialParentCancelPurchase) && (!ArtificialParentCancelPurchase || VendingMachine) && (!Beverages || Tea || Soda) && (!Tea || Beverages) && (!Soda || Beverages) && (!Beverages || VendingMachine) && (VendingMachine) \ No newline at end of file diff --git a/engine/crates/vts-synth/models/vibes/svm.fts.xml b/engine/crates/vts-synth/models/vibes/svm.fts.xml new file mode 100644 index 00000000..e6a43993 --- /dev/null +++ b/engine/crates/vts-synth/models/vibes/svm.fts.xml @@ -0,0 +1,37 @@ + + + state1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/engine/crates/vts-synth/models/vibes/svm.out b/engine/crates/vts-synth/models/vibes/svm.out new file mode 100644 index 00000000..3ba91b0d --- /dev/null +++ b/engine/crates/vts-synth/models/vibes/svm.out @@ -0,0 +1,57 @@ +Features: {"FreeDrinks", "CancelPurchase", "Tea", "Soda", "VendingMachine", "ArtificialParentFreeDrinks", "Currency", "ArtificialParentCancelPurchase", "Beverages", "Dollar", "x13", "Euro", "x14"} +Observables: {"pay", "free", "change", "cancel", "tea", "soda", "return", "serveTea", "serveSoda", "open", "take", "close"} + +States: 9 (initial: 1) +Transitions: 13 +SCCs: 1 + +== Initial VTS == +States: 87 (initial: 1) +Transitions: 120 +SCCs: 18 +Deterministic: true +Monotonic: true + +== Refined VTS == +States: 87 (initial: 1) +Transitions: 120 +SCCs: 18 +Deterministic: true +Monotonic: true + +== Observability Projection == +States: 87 (initial: 1) +Transitions: 120 +SCCs: 18 +Deterministic: true +Monotonic: true + +== Determinize == +States: 87 (initial: 1) +Transitions: 120 +SCCs: 18 + +== Flatten Beliefs == +States: 87 (initial: 1) +Transitions: 120 +SCCs: 18 +Deterministic: true +Monotonic: true + +== Compress SCCs == +States: 18 (initial: 1) +Transitions: 120 +SCCs: 18 +Deterministic: true +Monotonic: true + +== Compress Transitions == +States: 18 (initial: 1) +Transitions: 120 +SCCs: 18 +Deterministic: true +Monotonic: true + +real 0m0.123s +user 0m0.093s +sys 0m0.036s \ No newline at end of file diff --git a/engine/crates/vts-synth/models/vibes/svm.tvl b/engine/crates/vts-synth/models/vibes/svm.tvl new file mode 100644 index 00000000..05da12f5 --- /dev/null +++ b/engine/crates/vts-synth/models/vibes/svm.tvl @@ -0,0 +1,25 @@ +root VendingMachine { + + group allof { + opt CancelPurchase, + Beverages, + opt FreeDrinks, + Currency + } +} + + +root Beverages { + group someof { + Soda, + Tea + } +} + + +root Currency { + group oneof { + Euro, + Dollar + } +} \ No newline at end of file diff --git a/engine/crates/vts-synth/src/algorithms.rs b/engine/crates/vts-synth/src/algorithms.rs new file mode 100644 index 00000000..8cba52e1 --- /dev/null +++ b/engine/crates/vts-synth/src/algorithms.rs @@ -0,0 +1,614 @@ +//! Algorithms for constructing and transforming belief transition systems. + +use std::{collections::VecDeque, fmt::Debug, hash::Hash}; + +use hashbrown::{HashMap, HashSet}; +use indexmap::IndexSet; +use tracing::{info, span, trace, warn, Level}; + +use crate::{ + lattice::{HasBottom, Join, Meet, MeetSemiLattice}, + ts::{ + algorithms::dfs::Dfs, + traits::{ + BaseTs, InitialStates, MakeDenseStateMap, MakeSparseStateSet, StateMap, StateSet, + States, Transitions, + }, + types::{Vats, Vts, VtsState}, + SparseStateSet, StateId, Transition, Ts, TsBuilder, + }, +}; + +pub mod minimize; + +/// Constructs an _annotation tracking_ VTS from a _verdict-annotated_ TS. +pub fn annotation_tracking( + domain: &V, + vats: &Vats, + constraint: &V::Element, + do_bottom_elimination: bool, +) -> Vts +where + S: Clone + Eq + Hash, + A: Clone + Eq + Hash, + V: MeetSemiLattice + HasBottom, + V::Element: Clone + Hash + Eq + Meet, +{ + let _span = span!(Level::INFO, "annotation_tracking").entered(); + + // Start with an empty VTS and add the initial states to it. + let mut builder = TsBuilder::new(); + for vats_state in vats.initial_states() { + let id = builder.insert_state(VtsState::new(vats_state, constraint.clone())); + builder.mark_initial(id); + } + + let mut queue = builder.initial_states().collect::>(); + let mut iteration = 0; + while let Some(source_id) = queue.pop_front() { + let vats_state = builder.get_label(&source_id).control; + iteration += 1; + if iteration % 20_000 == 0 { + info!( + "VTS has {} states. {} states in waiting.", + builder.num_states(), + queue.len() + ); + } + for transition in vats.outgoing(&vats_state) { + let target_belief = builder + .get_label(&source_id) + .verdict + .meet(&transition.action().guard); + + // Bottom elimination (should not be done for diagnosis). + if domain.is_bottom(&target_belief) && do_bottom_elimination { + continue; + } + + let target = VtsState::new(transition.target(), target_belief); + + let target_id = match builder.lookup_state(&target) { + Some(id) => id, + None => { + let id = builder.insert_state(target); + queue.push_back(id); + id + } + }; + + builder.insert_transition(source_id, transition.action().action.clone(), target_id); + } + } + + builder.build() +} + +/// Refines the verdicts of a VTS by looking ahead. +pub fn lookahead_refinement(original: &Vts) -> Vts +where + Q: Clone + Eq + Hash, + L: Clone + Eq + Hash, + B: Clone + Hash + Eq + Join, +{ + let mut refined_verdicts = + original.create_state_labeling(|state| original.get_label(&state).verdict.clone()); + + let mut iteration = 0; + let mut changed = true; + while changed { + info!("Starting iteration {iteration}."); + changed = false; + let mut visited = 0; + for state in Dfs::new(&original).iter_post_order() { + let mut verdicts = Vec::new(); + for transition in original.outgoing(&state) { + verdicts.push(&refined_verdicts[transition.target()]); + } + + let verdict = verdicts + .iter() + .fold(None, |acc: Option, belief| { + Some( + acc.map(|acc| acc.join(belief)) + .unwrap_or_else(|| (*belief).clone()), + ) + }) + .unwrap_or_else(|| original.get_label(&state).verdict.clone()); + + if refined_verdicts[state] != verdict { + refined_verdicts[state] = verdict; + changed = true; + } + visited += 1; + if visited % 100_000 == 0 { + info!( + "Processed {} out of {} states ({} %).", + visited, + original.num_states(), + (visited * 100) / original.num_states(), + ) + } + } + iteration += 1; + } + info!("Building refined VTS."); + original + .map_states(|id, state| VtsState::new(state.control.clone(), refined_verdicts[id].clone())) +} + +/// Determinizes the provided LTS using the usual power set construction. +pub fn determinize(original: &Ts) -> Ts +where + S: Clone + Eq + Hash, + A: Clone + Eq + Hash, +{ + let mut builder = TsBuilder::new(); + + // 1️⃣ Create the initial state. + let mut initial_state_set = original.make_sparse_state_set(); + for state in original.initial_states() { + initial_state_set.insert(state); + } + let initial_state = builder.insert_state(initial_state_set); + builder.mark_initial(initial_state); + + // 2️⃣ Incrementally construct the reachable fragment of the determinized TS. + let mut stack = vec![initial_state]; + while let Some(source_det) = stack.pop() { + let source_set = builder.get_label(&source_det); + // Collect the outgoing transitions for each label. + let mut outgoing = HashMap::new(); + for source in source_set.iter() { + for transition in original.outgoing(&source) { + outgoing + .entry(transition.action().clone()) + .or_insert_with(|| original.make_sparse_state_set()) + .insert(transition.target()); + } + } + // Build the transitions. + for (act, target_set) in outgoing { + let target_det = builder.lookup_state(&target_set).unwrap_or_else(|| { + let target_det = builder.insert_state(target_set); + stack.push(target_det); + target_det + }); + builder.insert_transition(source_det, act, target_det); + } + } + + builder.build() +} + +/// Turns a TS labeled with sets of states of a VTS into a VTS by joining the verdicts. +pub fn join_verdicts( + vts: &Vts, + ts: &Ts, + bottom: &V, +) -> Vts +where + Q: Clone + Eq + Hash, + A: Clone + Eq + Hash, + V: Clone + Hash + Eq + Join, +{ + ts.map_states(|_, set| { + VtsState::new(set.clone(), { + set.iter() + .map(|id| vts.get_label(&id)) + .fold(None, |verdict: Option, state| { + Some( + verdict + .map(|acc| acc.join(&state.verdict)) + .unwrap_or_else(|| state.verdict.clone()), + ) + }) + .unwrap_or_else(|| bottom.clone()) + }) + }) +} + +// /// Uniquely identifies an equivalence class of a partition. +// struct EquivCls(usize); + +// pub struct Partition { +// num_classes: usize, +// } + +pub struct Minimize<'cx, Q, V, A> { + vts: &'cx Vts, + actions: &'cx [A], + language_insensitive: bool, + eliminate_self_loops: bool, +} + +impl<'cx, Q, V, A> Minimize<'cx, Q, V, A> +where + V: Clone + Hash + Eq, + A: Debug + Clone + Hash + Eq, +{ + pub fn new(vts: &'cx Vts, actions: &'cx [A]) -> Self { + Self { + vts, + actions, + language_insensitive: false, + eliminate_self_loops: false, + } + } + + pub fn with_language_insensitive(mut self, language_insensitive: bool) -> Self { + self.language_insensitive = language_insensitive; + self.eliminate_self_loops = language_insensitive; + self + } + + pub fn with_eliminate_self_loops(mut self, eliminate_self_loops: bool) -> Self { + self.eliminate_self_loops = eliminate_self_loops; + self + } + + /// Minimizes the given `vts` using a variant of Hopcroft's algorithm[^1]. + /// + /// If `language_insensitive` is set, the language of the original VTS is not preserved. + /// + /// [^1]: John Hopcroft. [An `n log n` Algorithm for Minimizing States in Finite Automata](https://doi.org/10.1016/B978-0-12-417750-5.50022-1). 1971. + pub fn run(self) -> Vts { + let _span = span!(Level::INFO, "minimize").entered(); + + info!( + "Minimizing VTS with {} states and {} transitions.", + self.vts.num_states(), + self.vts.num_transitions(), + ); + + if self.eliminate_self_loops && !self.language_insensitive { + warn!("Removing self loops will change the accepted language."); + } + + let empty_set = im::HashSet::new(); + + // 1️⃣ Partition the states based on the verdicts they yield. + let mut partition = HashMap::new(); + for state_id in self.vts.states() { + let verdict = self.vts.get_label(&state_id).verdict.clone(); + partition + .entry(verdict) + .or_insert_with(|| empty_set.clone()) + .insert(state_id); + } + let mut partition = partition.into_values().collect::>(); + debug_assert_eq!( + partition.iter().map(|cls| cls.len()).sum::(), + self.vts.num_states(), + "No states of the original VTS must get lost." + ); + info!( + "Initial partition has {} equivalence classes.", + partition.len() + ); + info!("Refining equivalence classes."); + + // 2️⃣ Refine the partition according to Hopcroft's algorithm. + let mut work_set = partition.iter().cloned().collect::>(); + let mut iteration = 0; + while let Some(splitter_cls) = work_set.pop() { + iteration += 1; + if iteration % 10 == 0 { + info!( + "Partition has {} equivalence classes, {} splitters are waiting.", + partition.len(), + work_set.len() + ); + } + for action in self.actions { + let splitter = Splitter::new(splitter_cls.clone(), action); + for cls in partition.clone() { + let Some((inside, outside)) = + split(self.vts, &cls, &splitter, self.language_insensitive) + else { + // The class is not split by the splitter. Continue! + continue; + }; + // The class must be split. Remove it from the partition and update the work + // set based on the returned split. + partition.remove(&cls); + // for action in self.actions { + // let splitter = Splitter::new(cls.clone(), action); + if work_set.swap_remove(&splitter_cls) { + work_set.insert(inside.clone()); + work_set.insert(outside.clone()); + } else { + if inside.len() < outside.len() { + work_set.insert(inside.clone()); + } else { + work_set.insert(outside.clone()); + } + } + // } + partition.insert(inside); + partition.insert(outside); + } + } + } + info!("Found {} equivalence classes.", partition.len()); + assert_eq!( + partition.iter().map(|cls| cls.len()).sum::(), + self.vts.num_states(), + "No states of the original VTS must get lost." + ); + + // 3️⃣ Build a lookup tables for equivalence classes. + let mut state_to_cls = self.vts.make_dense_state_map(); + let mut cls_to_verdict = Vec::new(); + let mut cls_to_set = Vec::new(); + for (cls_idx, cls) in partition.iter().enumerate() { + let mut cls_set = self.vts.make_sparse_state_set(); + for state in cls { + cls_set.insert(*state); + state_to_cls.insert(*state, cls_idx); + // TODO: Check whether there was no class for the state before. + // debug_assert!(inserted.is_none(), "A state be in a single class."); + } + cls_to_set.push(cls_set); + let cls_verdict = self + .vts + .get_label( + cls.iter() + .next() + .expect("Equivalence classes must not be empty"), + ) + .verdict + .clone(); + cls_to_verdict.push(cls_verdict); + } + // TODO: Check whether any states got lost. + // assert_eq!( + // state_to_cls.len(), + // vts.num_states(), + // "No states of the original VTS must get lost." + // ); + + // 3️⃣ Build the minimized VTS using the computed classes. + info!("Constructing minimized VTS using the computed classes."); + let mut builder = TsBuilder::new(); + // Add the states to the new VTS. + let cls_states = partition + .iter() + .enumerate() + .map(|(cls_idx, cls)| { + let cls_set = cls_to_set[cls_idx].clone(); + let verdict = cls_to_verdict[cls_idx].clone(); + let cls_state = builder.insert_state(VtsState::new(cls_set, verdict)); + for state in cls { + if self.vts.is_initial(state) { + builder.mark_initial(cls_state); + break; + } + } + cls_state + }) + .collect::>(); + // Add the transitions to the new VTS. + for (source_cls_idx, source) in cls_states.iter().enumerate() { + for state in cls_to_set[source_cls_idx].iter() { + for transition in self.vts.outgoing(&state) { + let target_cls_idx = *state_to_cls + .get(&transition.target()) + .expect("Every state must belong to a class."); + if !self.eliminate_self_loops || source_cls_idx != target_cls_idx { + let target = cls_states[target_cls_idx]; + builder.insert_transition(*source, transition.action().clone(), target); + } + } + } + } + + builder.build() + } +} + +/// A splitter for minimization. +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +struct Splitter<'act, A> { + cls: im::HashSet, + action: &'act A, +} + +impl<'act, A> Splitter<'act, A> { + /// Constructs a new [`Splitter`]. + pub fn new(cls: im::HashSet, action: &'act A) -> Self { + Self { cls, action } + } +} + +/// Tries to split an equivalence class `cls` with a [`Splitter`]. +fn split( + vts: &Vts, + cls: &im::HashSet, + splitter: &Splitter<'_, A>, + language_insensitive: bool, +) -> Option<(im::HashSet, im::HashSet)> +where + A: Eq, +{ + #[derive(Debug, Clone, Copy)] + enum Hit { + /// Transition hits inside of the splitter's class. + Inside, + /// Transition hits outside of the splitter's class. + Outside, + /// There is no transition with the splitter's action. + Disabled, + } + + // 1️⃣ Determine a `Hit` for each of the states of the class. + let default_hit = if language_insensitive { + Hit::Disabled + } else { + Hit::Outside + }; + let mut hits = Vec::new(); + let mut hits_inside = 0; + let mut hits_outside = 0; + for state in cls { + let mut hit = default_hit; + for transition in vts.outgoing(state) { + if transition.action() == splitter.action { + if splitter.cls.contains(&transition.target()) { + hit = Hit::Inside; + } else { + hit = Hit::Outside; + } + // The TS is assumed to be deterministic, hence, there is no other + // transition with the action of the splitter. Stop searching! + break; + } + } + hits.push(hit); + match hit { + Hit::Inside => hits_inside += 1, + Hit::Outside => hits_outside += 1, + Hit::Disabled => { /* ignore */ } + }; + } + debug_assert_eq!( + hits.len(), + cls.len(), + "There must be a `Hit` for each state." + ); + trace!( + inside = hits_inside, + outside = hits_outside, + none = cls.len() - hits_inside - hits_outside, + ); + + // 2️⃣ Decide whether to split based on the hit counters. + if hits_inside > 0 && hits_outside > 0 { + // The class needs to be split as some transitions hit inside and some + // outside of the splitter's class. We use the previously collected hits + // to decide which states go where. + let mut inside = cls.new_from(); + let mut outside = cls.new_from(); + cls.iter().zip(hits).for_each(|(state, hit)| { + match hit { + Hit::Inside => inside.insert(*state), + Hit::Outside | Hit::Disabled => outside.insert(*state), + }; + }); + debug_assert_eq!( + cls.len(), + inside.len() + outside.len(), + "No states of the original class must get lost." + ); + Some((inside, outside)) + } else { + None + } +} + +/// Computes the observability projection of a TS. +pub fn observability_projection(ts: &Ts, is_observable: F) -> Ts +where + S: Debug + Clone + Eq + Hash, + A: Debug + Clone + Eq + Hash, + F: Fn(&A) -> bool, +{ + // 1️⃣ For each state, determine all outgoing transitions and indiscernible states. + let mut outgoing = ts.create_default_state_labeling::>(); + let mut indiscernible = ts.create_state_labeling(|id| im::HashSet::::from_iter([id])); + + let mut iteration = 0; + let mut changed = true; + while changed { + info!("Starting iteration {iteration}."); + changed = false; + let mut visited = 0; + for state in Dfs::new(ts).iter_post_order() { + for transition in ts.outgoing(&state) { + if is_observable(transition.action()) { + changed |= outgoing[state] + .insert(TransitionParts::from_transition(transition)) + .is_none(); + } else { + let target_outgoing = outgoing[transition.target()].clone(); + for transition in target_outgoing { + changed |= outgoing[state].insert(transition).is_none(); + } + let target_indiscernible = indiscernible[transition.target()].clone(); + for x in target_indiscernible { + changed |= indiscernible[state].insert(x).is_none(); + } + } + } + visited += 1; + if visited % 100_000 == 0 { + info!( + "Processed {} out of {} states ({} %).", + visited, + ts.num_states(), + (visited * 100) / ts.num_states(), + ) + } + } + iteration += 1; + } + + info!("Building VTS."); + + // 2️⃣ Construct the resulting TS. + let mut builder = TsBuilder::new(); + + for state_id in ts.states() { + let mut reachable_states = ts.make_sparse_state_set(); + reachable_states.insert(state_id); + for other_id in &indiscernible[state_id] { + reachable_states.insert(*other_id); + } + let new_state_id = builder.insert_state(reachable_states); + if ts.is_initial(&state_id) { + builder.mark_initial(new_state_id); + } + } + + for state_id in ts.states() { + let mut source_states = ts.make_sparse_state_set(); + for weak_reach in &indiscernible[state_id] { + source_states.insert(*weak_reach); + } + let source_id = builder.lookup_state(&source_states).unwrap(); + + for transition in &outgoing[state_id] { + let mut target_states = ts.make_sparse_state_set(); + for weak_reach in &indiscernible[transition.target] { + target_states.insert(*weak_reach); + } + let target_id = builder.lookup_state(&target_states).unwrap(); + + builder.insert_transition(source_id.clone(), transition.action.clone(), target_id) + } + } + + builder.build() +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +struct TransitionParts { + action: A, + target: StateId, +} + +impl TransitionParts { + pub fn new(act: A, target: StateId) -> Self { + Self { + action: act, + target, + } + } + + pub fn from_transition(transition: &Transition) -> Self + where + A: Clone, + { + Self::new(transition.action().clone(), transition.target()) + } +} diff --git a/engine/crates/vts-synth/src/algorithms/minimize.rs b/engine/crates/vts-synth/src/algorithms/minimize.rs new file mode 100644 index 00000000..cf05a229 --- /dev/null +++ b/engine/crates/vts-synth/src/algorithms/minimize.rs @@ -0,0 +1,662 @@ +use std::{fmt::Debug, hash::Hash, marker::PhantomData}; + +use indexmap::IndexMap; +use tracing::{info, span, warn, Level}; + +use crate::ts::{ + traits::{ + BaseTs, InitialStates, MakeDenseStateMap, MakeSparseStateSet, StateMap, StateSet, States, + Transitions, + }, + types::{Vts, VtsState}, + SparseStateSet, StateId, TransitionId, TsBuilder, +}; + +/// An index into an [`IdxVec`]. +pub trait Idx: Copy { + /// Constructs an index from [`usize`]. + fn from_usize(idx: usize) -> Self; + + /// Converts the index to [`usize`]. + fn as_usize(self) -> usize; + + /// Increment the index. + fn increment(&mut self) { + *self = Self::from_usize(self.as_usize() + 1) + } +} + +/// A vector with a typed index. +pub struct IdxVec { + data: Vec, + _phantom_index: PhantomData, +} + +impl IdxVec { + /// Creates an empty [`IdxVec`]. + pub fn new() -> Self { + Self::from_vec(Vec::new()) + } + + /// Converts a [`Vec`] to an [`IdxVec`]. + pub fn from_vec(data: Vec) -> Self { + Self { + data, + _phantom_index: PhantomData, + } + } +} + +impl Clone for IdxVec { + fn clone(&self) -> Self { + Self::from_vec(self.data.clone()) + } +} + +impl Default for IdxVec { + fn default() -> Self { + Self::new() + } +} + +impl IdxVec { + /// Adds a value to the vector. + pub fn push(&mut self, value: T) -> I { + let idx = self.next_idx(); + self.data.push(value); + idx + } + + /// The index of the last element. + pub fn last_idx(&self) -> I { + assert!(self.data.len() > 0); + I::from_usize(self.data.len() - 1) + } + + /// A mutable reference to the last element. + pub fn last_mut(&mut self) -> Option<&mut T> { + self.data.last_mut() + } + + /// The index of the next element. + pub fn next_idx(&self) -> I { + I::from_usize(self.data.len()) + } +} + +impl std::ops::Index for IdxVec { + type Output = T; + + fn index(&self, index: I) -> &Self::Output { + &self.data[index.as_usize()] + } +} + +impl std::ops::IndexMut for IdxVec { + fn index_mut(&mut self, index: I) -> &mut Self::Output { + &mut self.data[index.as_usize()] + } +} + +impl std::ops::Index> for IdxVec { + type Output = [T]; + + fn index(&self, index: std::ops::Range) -> &Self::Output { + &self.data[index.start.as_usize()..index.end.as_usize()] + } +} + +macro_rules! new_idx_type { + ($(#[$meta:meta])* $name:ident($int:ty)) => { + $(#[$meta])* + #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] + pub struct $name($int); + + impl Idx for $name { + fn from_usize(idx: usize) -> Self { + #[cfg(debug_assertions)] + return Self(idx.try_into().unwrap()); + #[cfg(not(debug_assertions))] + return Self(idx as $int); + } + + + fn as_usize(self) -> usize { + self.0 as usize + } + } + }; +} + +new_idx_type! { + /// Uniquely identifies a location in [`Partition`]. + LocationIdx(u32) +} + +new_idx_type! { + /// Uniquely identifies a class of a partition. + ClsIdx(u32) +} + +impl ClsIdx { + /// Invalid class index. + pub fn invalid() -> Self { + Self(u32::MAX) + } + + /// Returns whether the index is invalid. + pub fn is_invalid(self) -> bool { + self.0 == u32::MAX + } +} + +/// A partition of a set. +/// +/// Based on *Efficient Minimization of DFAs with Partial Transitions* by Almari and Lehtinen. +/// +/// [1]: https://doi.org/10.4230/LIPIcs.STACS.2008.1328 +pub struct Partition { + location_to_element: IdxVec, + element_to_location: IdxVec, + cls: IdxVec, + first: IdxVec, + marker: IdxVec, + last: IdxVec, +} + +impl Partition { + /// Creates a new partition of an empty set. + pub fn new(capacity: usize) -> Self { + // let mut last = IdxVec::new(); + // last.push(LocationIdx::from_usize(0)); + Self { + location_to_element: IdxVec::new(), + element_to_location: IdxVec::from_vec(vec![LocationIdx::from_usize(0); capacity]), + cls: IdxVec::from_vec(vec![ClsIdx::invalid(); capacity]), + first: IdxVec::new(), + marker: IdxVec::new(), + last: IdxVec::new(), + } + } + + /// Iterator over the classes. + pub fn iter_classes(&self) -> impl Iterator { + (0..self.first.data.len()).map(ClsIdx::from_usize) + } + + /// Returns the number of classes. + pub fn num_classes(&self) -> usize { + self.first.data.len() + } + + pub fn last_cls(&self) -> ClsIdx { + ClsIdx(self.first.data.len() as u32) + } + + pub fn size(&self, cls: ClsIdx) -> usize { + self.last[cls].as_usize() - self.first[cls].as_usize() + } + + /// Creates a new class. + pub fn create_class(&mut self) -> ClsIdx { + let first = self.location_to_element.next_idx(); + let cls = self.first.push(first); + self.last.push(first); + self.marker.push(first); + cls + } + + /// Adds an element to the current class. + pub fn push(&mut self, element: E) { + let mut location = self.location_to_element.push(element); + self.element_to_location[element] = location; + self.cls[element] = self.first.last_idx(); + location.increment(); + *self.last.last_mut().unwrap() = location; + } + + /// Returns the class of the element. + pub fn cls_of(&self, element: E) -> ClsIdx { + self.cls[element] + } + + /// Marks an element of a class. + pub fn mark(&mut self, element: E) { + let cls = self.cls_of(element); + let location = self.element_to_location[element]; + let marker = self.marker[cls]; + if location >= marker { + self.location_to_element[location] = self.location_to_element[marker]; + self.element_to_location[self.location_to_element[location]] = location; + self.location_to_element[marker] = element; + self.element_to_location[element] = marker; + self.marker[cls].increment(); + } + } + + /// Resets the marker. + pub fn reset_marker(&mut self, cls: ClsIdx) { + self.marker[cls] = self.first[cls]; + } + + /// Splits a class based on the marked elements. + pub fn split(&mut self, cls: ClsIdx) -> Option { + if self.marker[cls] == self.last[cls] { + self.marker[cls] = self.first[cls]; + } + if self.marker[cls] == self.first[cls] { + None + } else { + let new_cls = self.first.push(self.first[cls]); + self.marker.push(self.first[cls]); + self.last.push(self.marker[cls]); + self.first[cls] = self.marker[cls]; + let mut location = self.first[new_cls]; + while location < self.last[new_cls] { + self.cls[self.location_to_element[location]] = new_cls; + location.increment(); + } + Some(new_cls) + } + } + + /// The elements of the class. + pub fn elements(&self, cls: ClsIdx) -> &[E] { + let first = self.first[cls]; + let last = self.last[cls]; + &self.location_to_element[first..last] + } + + /// The marked elements of the class. + pub fn marked(&self, cls: ClsIdx) -> &[E] { + let first = self.first[cls]; + let marker = self.marker[cls]; + &self.location_to_element[first..marker] + } + + /// The unmarked elements of the class. + pub fn unmarked(&self, cls: ClsIdx) -> &[E] { + let marker = self.marker[cls]; + let last = self.last[cls]; + &self.location_to_element[marker..last] + } + + /// Returns whether a class has marked elements. + pub fn has_marks(&self, cls: ClsIdx) -> bool { + self.marker[cls] != self.first[cls] + } + + #[track_caller] + pub fn assert_valid(&self) { + for cls in self.iter_classes() { + assert!( + self.first[cls] < self.last[cls], + "Classes must be non-empty." + ); + } + } +} + +impl Idx for StateId { + fn from_usize(idx: usize) -> Self { + StateId::from_parts(0, idx) + } + + fn as_usize(self) -> usize { + self.idx() + } +} + +impl Idx for TransitionId { + fn from_usize(idx: usize) -> Self { + TransitionId(idx) + } + + fn as_usize(self) -> usize { + self.0 + } +} + +pub struct MinimizeFast<'cx, Q, V, A> { + vts: &'cx Vts, + language_insensitive: bool, + eliminate_self_loops: bool, + state_partition: Partition, + transition_partition: Partition, + touched_splitters: Vec, + waiting_splitters: Vec, +} + +impl<'cx, Q, V, A> MinimizeFast<'cx, Q, V, A> +where + V: Clone + Hash + Eq, + A: Debug + Clone + Hash + Eq, +{ + pub fn new(vts: &'cx Vts) -> Self { + Self { + vts, + language_insensitive: false, + eliminate_self_loops: false, + state_partition: Partition::new(vts.num_states()), + transition_partition: Partition::new(vts.num_transitions()), + touched_splitters: Vec::new(), + waiting_splitters: Vec::new(), + } + } + + pub fn with_language_insensitive(mut self, language_insensitive: bool) -> Self { + self.language_insensitive = language_insensitive; + self.eliminate_self_loops = language_insensitive; + self + } + + pub fn with_eliminate_self_loops(mut self, eliminate_self_loops: bool) -> Self { + self.eliminate_self_loops = eliminate_self_loops; + self + } + + #[track_caller] + fn sanity_check_state_partition(&self) { + self.state_partition.assert_valid(); + assert_eq!( + self.state_partition + .iter_classes() + .map(|cls| self.state_partition.elements(cls).len()) + .sum::(), + self.vts.num_states(), + "No states of the original VTS must get lost." + ); + } + + #[track_caller] + fn sanity_check_transition_partition(&self) { + self.transition_partition.assert_valid(); + assert_eq!( + self.transition_partition + .iter_classes() + .map(|cls| self.transition_partition.elements(cls).len()) + .sum::(), + self.vts.num_transitions(), + "No transitions of the original VTS must get lost." + ); + } + + fn transition_cls_target(&self, transition_cls: ClsIdx) -> ClsIdx { + self.state_partition.cls_of( + self.vts + .get_transition( + *self + .transition_partition + .elements(transition_cls) + .iter() + .next() + .unwrap(), + ) + .target(), + ) + } + + #[track_caller] + fn sanity_check_partition_consistency(&self) { + for transition_cls in self.transition_partition.iter_classes() { + let target_cls = self.transition_cls_target(transition_cls); + for transition_id in self.transition_partition.elements(transition_cls) { + let transition = self.vts.get_transition(*transition_id); + assert_eq!(self.state_partition.cls_of(transition.target()), target_cls); + } + } + } + + fn emit_partition_info(&self) { + info!( + "Partitions have {} state and {} transition classes.", + self.state_partition.num_classes(), + self.transition_partition.num_classes() + ); + } + + fn initialize_partitions(&mut self) { + self.vts.assert_invariants(); + + // Assert that the partitions have not been initialized yet. + assert!(self.state_partition.num_classes() == 0); + assert!(self.transition_partition.num_classes() == 0); + + // Initialize state partition. + let mut verdict_to_states = IndexMap::new(); + for state_id in self.vts.states() { + let verdict = &self.vts.get_label(&state_id).verdict; + verdict_to_states + .entry(verdict) + .or_insert_with(Vec::new) + .push(state_id); + } + assert_eq!( + verdict_to_states + .values() + .map(|cls_states| cls_states.len()) + .sum::(), + self.vts.num_states(), + "No states of the original VTS must get lost." + ); + for cls_states in verdict_to_states.values() { + self.state_partition.create_class(); + for cls_state in cls_states { + self.state_partition.push(*cls_state); + } + } + assert_eq!( + verdict_to_states.len(), + self.state_partition.num_classes(), + "Number of initial classes is incorrect." + ); + self.sanity_check_state_partition(); + + // Initialize transition partition. + let mut label_to_transitions = IndexMap::new(); + for state_cls in self.state_partition.iter_classes() { + label_to_transitions.clear(); + for state in self.state_partition.elements(state_cls) { + for transition_id in self.vts.incoming_ids(state) { + let transition = self.vts.get_transition(transition_id); + assert_eq!(transition.target(), *state); + label_to_transitions + .entry(transition.action()) + .or_insert_with(Vec::new) + .push(transition_id); + } + } + if label_to_transitions.is_empty() { + continue; + } + // if state_cls.as_usize() > 0 { + // self.transition_partition.create_class(); + // } + for cls_transitions in label_to_transitions.values() { + self.transition_partition.create_class(); + for cls_transition in cls_transitions { + self.transition_partition.push(*cls_transition); + } + } + } + self.emit_partition_info(); + + self.sanity_check_transition_partition(); + self.sanity_check_partition_consistency(); + } + + fn split_class(&mut self, state_cls: ClsIdx) { + if let Some(mut new_state_cls) = self.state_partition.split(state_cls) { + if self.state_partition.size(state_cls) < self.state_partition.size(new_state_cls) { + new_state_cls = state_cls; + } + let state_cls = new_state_cls; + for state in self.state_partition.elements(state_cls) { + for transition_id in self.vts.incoming_ids(state) { + let transition_cls = self.transition_partition.cls_of(transition_id); + if !self.transition_partition.has_marks(transition_cls) { + self.touched_splitters.push(transition_cls); + } + self.transition_partition.mark(transition_id); + } + } + while let Some(splitter) = self.touched_splitters.pop() { + if let Some(new_transition_cls) = self.transition_partition.split(splitter) { + self.waiting_splitters.push(new_transition_cls); + } + } + } + } + + fn refine(&mut self) { + self.waiting_splitters + .extend(self.transition_partition.iter_classes()); + let mut touched_classes = Vec::new(); + let mut iteration = 0; + while let Some(splitter) = self.waiting_splitters.pop() { + // self.sanity_check_partition_consistency(); + if iteration % 10_000 == 0 { + info!("Waiting splitters {}.", self.waiting_splitters.len()); + self.emit_partition_info(); + } + iteration += 1; + let action = self + .vts + .get_transition( + *self + .transition_partition + .elements(splitter) + .iter() + .next() + .unwrap(), + ) + .action(); + for transition_id in self.transition_partition.elements(splitter) { + let source = self.vts.get_transition(*transition_id).source(); + let source_cls = self.state_partition.cls_of(source); + if !self.state_partition.has_marks(source_cls) { + touched_classes.push(source_cls); + } + self.state_partition.mark(source); + } + while let Some(state_cls) = touched_classes.pop() { + let mut do_split = !self.language_insensitive; + if self.language_insensitive { + for unmarked in self.state_partition.unmarked(state_cls) { + for transition in self.vts.outgoing(unmarked) { + if transition.action() == action { + do_split = true; + break; + } + } + } + } + if do_split { + self.split_class(state_cls); + } else { + self.state_partition.reset_marker(state_cls) + } + } + } + } + + /// Minimizes the given `vts` using a variant of Hopcroft's algorithm[^1]. + /// + /// If `language_insensitive` is set, the language of the original VTS is not preserved. + /// + /// [^1]: John Hopcroft. [An `n log n` Algorithm for Minimizing States in Finite Automata](https://doi.org/10.1016/B978-0-12-417750-5.50022-1). 1971. + pub fn run(mut self) -> Vts { + let _span = span!(Level::INFO, "minimize_fast").entered(); + + info!( + "Minimizing VTS with {} states and {} transitions.", + self.vts.num_states(), + self.vts.num_transitions(), + ); + + if self.eliminate_self_loops && !self.language_insensitive { + warn!("Removing self loops will change the accepted language."); + } + + self.initialize_partitions(); + + self.sanity_check_state_partition(); + self.sanity_check_transition_partition(); + self.sanity_check_partition_consistency(); + + info!("Refining equivalence classes."); + self.refine(); + + self.sanity_check_state_partition(); + self.sanity_check_transition_partition(); + self.sanity_check_partition_consistency(); + + // 1️⃣ Partition the states based on the verdicts they yield. + + // 3️⃣ Build the minimized VTS using the computed classes. + info!("Constructing minimized VTS using the computed classes."); + let mut builder = TsBuilder::new(); + + // 3️⃣ Build a lookup tables for equivalence classes. + let mut state_to_cls = self.vts.make_dense_state_map(); + let mut cls_to_verdict = Vec::new(); + let mut cls_to_set = Vec::new(); + for state_cls in self.state_partition.iter_classes() { + let mut cls_set = self.vts.make_sparse_state_set(); + for state in self.state_partition.elements(state_cls) { + cls_set.insert(*state); + state_to_cls.insert(*state, state_cls); + // TODO: Check whether there was no class for the state before. + // debug_assert!(inserted.is_none(), "A state be in a single class."); + } + cls_to_set.push(cls_set); + let cls_verdict = self + .vts + .get_label( + self.state_partition + .elements(state_cls) + .iter() + .next() + .expect("Equivalence classes must not be empty"), + ) + .verdict + .clone(); + cls_to_verdict.push(cls_verdict); + } + + // Add the states to the new VTS. + let cls_states = self + .state_partition + .iter_classes() + .map(|state_cls| { + let cls_set = cls_to_set[state_cls.as_usize()].clone(); + let verdict = cls_to_verdict[state_cls.as_usize()].clone(); + let cls_state = builder.insert_state(VtsState::new(cls_set, verdict)); + for state in self.state_partition.elements(state_cls) { + if self.vts.is_initial(&state) { + builder.mark_initial(cls_state); + break; + } + } + cls_state + }) + .collect::>(); + // Add the transitions to the new VTS. + for (source_cls_idx, source) in cls_states.iter().enumerate() { + for state in cls_to_set[source_cls_idx].iter() { + for transition in self.vts.outgoing(&state) { + let target_cls_idx = state_to_cls + .get(&transition.target()) + .expect("Every state must belong to a class.") + .as_usize(); + if !self.eliminate_self_loops || source_cls_idx != target_cls_idx { + let target = cls_states[target_cls_idx]; + builder.insert_transition(*source, transition.action().clone(), target); + } + } + } + } + + builder.build() + } +} diff --git a/engine/crates/vts-synth/src/cudd.rs b/engine/crates/vts-synth/src/cudd.rs new file mode 100644 index 00000000..e95e6fa2 --- /dev/null +++ b/engine/crates/vts-synth/src/cudd.rs @@ -0,0 +1,345 @@ +//! A safe abstraction for BDDs provided by CUDD. + +use std::{hash::Hash, marker::PhantomData, ptr::NonNull}; + +use rand::Rng; + +use crate::lattice::{HasBottom, HasTop, Join, Meet, PartiallyOrdered, Poset}; + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub enum Trinary { + False = 0, + True = 1, + Any = 2, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub enum ReorderingType { + Same, + None, + Sift, +} + +impl ReorderingType { + fn raw(&self) -> cudd_sys::cudd::Cudd_ReorderingType { + match self { + ReorderingType::Same => cudd_sys::cudd::Cudd_ReorderingType::CUDD_REORDER_SAME, + ReorderingType::None => cudd_sys::cudd::Cudd_ReorderingType::CUDD_REORDER_NONE, + ReorderingType::Sift => cudd_sys::cudd::Cudd_ReorderingType::CUDD_REORDER_SIFT, + } + } +} + +#[derive(Debug, PartialEq, Eq, Hash)] +pub struct BddManager { + ptr: NonNull, +} + +impl BddManager { + pub fn new() -> Self { + Self::with_vars(0) + } + + pub fn with_vars(num_vars: u32) -> Self { + let manager = NonNull::new(unsafe { + cudd_sys::cudd::Cudd_Init( + num_vars, + 0, + cudd_sys::cudd::CUDD_UNIQUE_SLOTS, + cudd_sys::cudd::CUDD_CACHE_SLOTS, + 0, + ) + }) + .expect("`Cudd_Init` returned a null pointer. Out of memory?"); + Self { ptr: manager } + } + + pub fn one(&self) -> BddNode { + BddNode::new(self.ptr, unsafe { + cudd_sys::cudd::Cudd_ReadOne(self.ptr.as_ptr()) + }) + } + + pub fn zero(&self) -> BddNode { + BddNode::new(self.ptr, unsafe { + cudd_sys::cudd::Cudd_ReadLogicZero(self.ptr.as_ptr()) + }) + } + + pub fn var(&self, index: i32) -> BddNode { + BddNode::new(self.ptr, unsafe { + cudd_sys::cudd::Cudd_bddIthVar(self.ptr.as_ptr(), index) + }) + } + + pub fn new_var(&self) -> BddNode { + BddNode::new(self.ptr, unsafe { + cudd_sys::cudd::Cudd_bddNewVar(self.ptr.as_ptr()) + }) + } + + pub fn reorder_now(&mut self, typ: ReorderingType) { + unsafe { cudd_sys::cudd::Cudd_ReduceHeap(self.ptr.as_mut(), typ.raw(), 1000) }; + } + + pub fn enable_reordering(&mut self, typ: ReorderingType) { + unsafe { cudd_sys::cudd::Cudd_AutodynEnable(self.ptr.as_mut(), typ.raw()) }; + } +} + +impl Default for BddManager { + fn default() -> Self { + Self::new() + } +} + +impl Drop for BddManager { + fn drop(&mut self) { + unsafe { + cudd_sys::cudd::Cudd_Quit(self.ptr.as_ptr()); + } + } +} + +#[derive(Debug, PartialEq, Eq, Ord, Hash)] +pub struct BddNode<'m> { + // We store a direct pointer to the `DdManager` instead of a reference to + // `CuddManager` to avoid an additional indirection. + mgr: NonNull, + node: NonNull, + _phantom_lifetime: PhantomData<&'m BddManager>, +} + +impl<'m> BddNode<'m> { + fn new(mgr: NonNull, node: *mut cudd_sys::DdNode) -> Self { + let node = NonNull::new(node).expect("Node pointer must not be NULL."); + unsafe { + cudd_sys::cudd::Cudd_Ref(node.as_ptr()); + } + BddNode { + mgr, + node, + _phantom_lifetime: PhantomData, + } + } + + pub fn index(&self) -> u32 { + unsafe { cudd_sys::cudd::Cudd_NodeReadIndex(self.node.as_ptr()) } + } + + pub fn is_constant(&self) -> bool { + unsafe { cudd_sys::cudd::Cudd_IsConstant(self.node.as_ptr()) != 0 } + } + + pub fn is_zero(&self) -> bool { + self.node.as_ptr() == unsafe { cudd_sys::cudd::Cudd_ReadLogicZero(self.mgr.as_ptr()) } + } + + pub fn is_one(&self) -> bool { + self.node.as_ptr() == unsafe { cudd_sys::cudd::Cudd_ReadOne(self.mgr.as_ptr()) } + } + + pub fn and(&self, other: &Self) -> Self { + assert_eq!( + self.mgr, other.mgr, + "Manager of both nodes must be the same." + ); + Self::new(self.mgr, unsafe { + cudd_sys::cudd::Cudd_bddAnd(self.mgr.as_ptr(), self.node.as_ptr(), other.node.as_ptr()) + }) + } + + pub fn or(&self, other: &Self) -> Self { + assert_eq!( + self.mgr, other.mgr, + "Manager of both nodes must be the same." + ); + Self::new(self.mgr, unsafe { + cudd_sys::cudd::Cudd_bddOr(self.mgr.as_ptr(), self.node.as_ptr(), other.node.as_ptr()) + }) + } + + pub fn xor(&self, other: &Self) -> Self { + assert_eq!( + self.mgr, other.mgr, + "Manager of both nodes must be the same." + ); + Self::new(self.mgr, unsafe { + cudd_sys::cudd::Cudd_bddXor(self.mgr.as_ptr(), self.node.as_ptr(), other.node.as_ptr()) + }) + } + + pub fn complement(&self) -> Self { + Self::new(self.mgr, unsafe { + cudd_sys::cudd::Cudd_Complement(self.node.as_ptr()) + }) + } + + pub fn implies(&self, other: &Self) -> Self { + self.complement().or(other) + } + + pub fn cubes(&self) -> Vec> { + let mut cubes = Vec::new(); + let num_vars = unsafe { cudd_sys::cudd::Cudd_ReadSize(self.mgr.as_ptr()) }; + unsafe { + cudd_sys::cudd::Cudd_ForeachCube(self.mgr.as_ptr(), self.node.as_ptr(), |cube, _| { + let mut cube_vec = Vec::new(); + for offset in 0..num_vars { + match *cube.offset(offset as isize) { + 0 => cube_vec.push(Trinary::False), + 1 => cube_vec.push(Trinary::True), + 2 => cube_vec.push(Trinary::Any), + x => panic!("Invalid value `{x}` for variable in cube."), + } + } + cubes.push(cube_vec); + }) + }; + cubes + } + + pub fn count_solutions(&self) -> f64 { + let num_vars = unsafe { cudd_sys::cudd::Cudd_ReadSize(self.mgr.as_ptr()) }; + unsafe { + cudd_sys::cudd::Cudd_CountMinterm(self.mgr.as_ptr(), self.node.as_ptr(), num_vars) + } + } + + pub fn sample_solution(&self) -> (BddNode<'m>, Vec) { + let mgr = self.mgr.as_ptr(); + let num_vars = unsafe { cudd_sys::cudd::Cudd_ReadSize(mgr) }; + let mut rng = rand::thread_rng(); + let mut current = self.clone(); + let solution = (0..num_vars) + .map(|var| { + let var = Self::new(self.mgr, unsafe { + cudd_sys::cudd::Cudd_bddIthVar(mgr, var) + }); + let if_true = current.and(&var); + let if_false = current.and(&var.complement()); + let true_count = if_true.count_solutions(); + let false_count = if_false.count_solutions(); + if rng.gen_bool(true_count / (true_count + false_count)) { + current = if_true; + true + } else { + current = if_false; + false + } + }) + .collect(); + debug_assert_eq!(current.count_solutions(), 1.0); + (current, solution) + } +} + +impl<'m> PartialOrd for BddNode<'m> { + fn partial_cmp(&self, other: &Self) -> Option { + if self.mgr != other.mgr { + return None; + } + self.index().partial_cmp(&other.index()) + } +} + +impl<'m> Clone for BddNode<'m> { + fn clone(&self) -> Self { + unsafe { + cudd_sys::cudd::Cudd_Ref(self.node.as_ptr()); + } + Self { + mgr: self.mgr, + node: self.node, + _phantom_lifetime: PhantomData, + } + } +} + +impl<'m> Drop for BddNode<'m> { + fn drop(&mut self) { + unsafe { + cudd_sys::cudd::Cudd_RecursiveDeref(self.mgr.as_ptr(), self.node.as_ptr()); + } + } +} + +impl<'m> PartiallyOrdered for BddNode<'m> { + fn is_less_than(&self, other: &Self) -> bool { + self != other && self.and(other) == *self + } +} + +// impl<'m> HasTop for BddNode<'m> { +// fn is_top(&self) -> bool { +// self.is_one() +// } +// } + +// impl<'m> HasBottom for BddNode<'m> { +// fn is_bottom(&self) -> bool { +// self.is_zero() +// } +// } + +impl<'m> Join for BddNode<'m> { + fn join(&self, other: &Self) -> Self { + self.or(other) + } +} + +impl<'m> Meet for BddNode<'m> { + fn meet(&self, other: &Self) -> Self { + self.and(other) + } +} + +impl<'m> Poset for &'m BddManager { + type Element = BddNode<'m>; +} + +impl<'m> HasBottom for &'m BddManager { + fn bottom(&self) -> Self::Element { + self.zero() + } + + fn is_bottom(&self, element: &Self::Element) -> bool { + element.is_zero() + } +} + +impl<'m> HasTop for &'m BddManager { + fn top(&self) -> Self::Element { + self.one() + } + + fn is_top(&self, element: &Self::Element) -> bool { + element.is_one() + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn simple_test() { + let manager = BddManager::new(); + + let zero = manager.zero(); + let one = manager.one(); + + assert_eq!(one.and(&one), manager.one()); + assert_eq!(zero.and(&zero), manager.zero()); + assert_eq!(zero.and(&one), manager.zero()); + + let x = manager.new_var(); + assert_eq!(x.index(), 0); + assert_eq!(manager.var(0), x); + + let y = manager.new_var(); + + assert_eq!(one.and(&x), x); + assert_eq!(zero.and(&y), zero); + } +} diff --git a/engine/crates/vts-synth/src/domains.rs b/engine/crates/vts-synth/src/domains.rs new file mode 100644 index 00000000..786a0fe5 --- /dev/null +++ b/engine/crates/vts-synth/src/domains.rs @@ -0,0 +1,523 @@ +//! Verdict domains. + +use std::{fmt, hash::Hash, str::FromStr}; + +use serde::{Deserialize, Serialize}; +use thiserror::Error; + +use crate::lattice::{HasTop, Join, Meet, PartiallyOrdered, Poset, Set}; + +/// Error parsing a value from a string. +#[derive(Debug, Error)] +#[error("{0}")] +pub struct ParseError(String); + +/// Klenee's [three-valued truth domain](https://en.wikipedia.org/wiki/Three-valued_logic). +/// +/// ```plain +/// Unknown +/// / \ +/// True False +/// ``` +#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, Serialize, Deserialize)] +pub enum Bool3 { + /// Unknown truth value. + Unknown, + /// True. + True, + /// False. + False, +} + +impl Bool3 { + /// Returns `true` if and only if the truth value is [`Bool3::Unknown`]. + pub fn is_unknown(self) -> bool { + matches!(self, Self::Unknown) + } + + /// Returns `true` if and only if the truth value is [`Bool3::True`]. + pub fn is_true(self) -> bool { + matches!(self, Self::True) + } + + /// Returns `true` if and only if the truth value is [`Bool3::False`]. + pub fn is_false(self) -> bool { + matches!(self, Self::False) + } + + /// Negates the value. + pub fn not(self) -> Self { + match self { + Self::True => Self::False, + Self::False => Self::True, + _ => self, + } + } + + /// Computes the conjunction of two values. + pub fn and(self, other: Self) -> Self { + use Bool3::*; + match (self, other) { + (_, False) | (False, _) => False, + (True, True) => True, + _ => Unknown, + } + } + + /// Computes the disjunction of two values. + pub fn or(self, other: Self) -> Self { + use Bool3::*; + match (self, other) { + (_, True) | (True, _) => True, + (False, False) => False, + _ => Unknown, + } + } +} + +impl fmt::Display for Bool3 { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Debug::fmt(self, f) + } +} + +impl FromStr for Bool3 { + type Err = ParseError; + + fn from_str(s: &str) -> Result { + use Bool3::*; + match s { + "U" | "-" | "Unknown" | "unknown" | "UNKNOWN" => Ok(Unknown), + "T" | "1" | "True" | "true" | "TRUE" => Ok(True), + "F" | "0" | "False" | "false" | "FALSE" => Ok(False), + _ => Err(ParseError(format!("`{s}` is not a valid Bool3"))), + } + } +} + +impl PartiallyOrdered for Bool3 { + #[inline] + fn is_less_than(&self, other: &Self) -> bool { + use Bool3::*; + match (self, other) { + (Unknown, _) => false, + (_, Unknown) => true, + _ => false, + } + } +} + +impl Join for Bool3 { + #[inline] + fn join(&self, other: &Self) -> Self { + use Bool3::*; + match (self, other) { + (True, True) => True, + (False, False) => False, + _ => Unknown, + } + } +} + +// impl HasTop for Bool3 { +// #[inline(always)] +// fn top() -> Self +// where +// Self: Sized, +// { +// Self::Unknown +// } + +// #[inline(always)] +// fn is_top(&self) -> bool { +// self.is_unknown() +// } +// } + +/// Belnap's [four-valued truth domain](https://en.wikipedia.org/wiki/Four-valued_logic). +/// +/// ```plain +/// Both +/// / \ +/// True False +/// \ / +/// None +/// ``` +#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, Serialize, Deserialize)] +pub enum Bool4 { + /// Truth value is *true or false*. + Both, + /// True. + True, + /// False. + False, + /// Truth value is *neither true nor false*. + None, +} + +impl Bool4 { + /// Returns `true` if and only if the truth value is [`Bool4::Both`]. + pub fn is_both(self) -> bool { + matches!(self, Self::Both) + } + + /// Returns `true` if and only if the truth value is [`Bool4::True`]. + pub fn is_true(self) -> bool { + matches!(self, Self::True) + } + + /// Returns `true` if and only if the truth value is [`Bool4::Both`] or + /// [`Bool4::True`]. + pub fn maybe_true(self) -> bool { + self.is_both() || self.is_true() + } + + /// Returns `true` if and only if the truth value is [`Bool4::False`]. + pub fn is_false(self) -> bool { + matches!(self, Self::False) + } + + /// Returns `true` if and only if the truth value is [`Bool4::Both`] or + /// [`Bool4::False`]. + pub fn maybe_false(self) -> bool { + self.is_both() || self.is_false() + } + + /// Returns `true` if and only if the truth value is [`Bool4::None`]. + pub fn is_none(self) -> bool { + matches!(self, Self::None) + } + + /// Negates the value. + pub fn not(self) -> Self { + match self { + Self::True => Self::False, + Self::False => Self::True, + _ => self, + } + } + + /// Computes the conjunction of two values. + pub fn and(self, other: Self) -> Self { + use Bool4::*; + match (self, other) { + (_, False) | (False, _) => False, + (None, Both) | (Both, None) => False, + (None, None) => None, + (True, right) => right, + (left, True) => left, + _ => Both, + } + } + + /// Computes the disjunction of two values. + pub fn or(self, other: Self) -> Self { + use Bool4::*; + match (self, other) { + (_, True) | (True, _) => True, + (None, Both) | (Both, None) => True, + (Both, Both) => Both, + (False, right) => right, + (left, False) => left, + _ => None, + } + } +} + +impl From for Bool4 { + fn from(value: Bool3) -> Self { + match value { + Bool3::Unknown => Self::Both, + Bool3::True => Self::True, + Bool3::False => Self::False, + } + } +} + +impl fmt::Display for Bool4 { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Debug::fmt(self, f) + } +} + +impl FromStr for Bool4 { + type Err = ParseError; + + fn from_str(s: &str) -> Result { + use Bool4::*; + match s { + // We allow unknown for compatibility with `Bool3`. + "U" | "-" | "Unknown" | "unknown" | "UNKNOWN" => Ok(Both), + "T" | "1" | "True" | "true" | "TRUE" => Ok(True), + "F" | "0" | "False" | "false" | "FALSE" => Ok(False), + "B" | "Both" | "both" | "BOTH" => Ok(None), + "N" | "None" | "none" | "NONE" => Ok(None), + _ => Err(ParseError(format!("`{s}` is not a valid Bool3"))), + } + } +} + +impl PartiallyOrdered for Bool4 { + fn is_less_than(&self, other: &Self) -> bool { + use Bool4::*; + match (self, other) { + (Both, _) => false, + (_, Both) => true, + (_, None) => false, + (None, _) => true, + _ => false, + } + } +} + +impl Meet for Bool4 { + #[inline] + fn meet(&self, other: &Self) -> Self { + use Bool4::*; + match (self, other) { + (None, _) | (_, None) => None, + (True, Both) | (Both, True) | (True, True) => True, + (False, Both) | (Both, False) | (False, False) => False, + (True, False) | (False, True) => None, + _ => Both, + } + } +} + +impl Join for Bool4 { + #[inline] + fn join(&self, other: &Self) -> Self { + use Bool4::*; + match (self, other) { + (Both, _) | (_, Both) => Both, + (True, None) | (None, True) | (True, True) => True, + (False, None) | (None, False) | (False, False) => False, + (True, False) | (False, True) => Both, + _ => None, + } + } +} + +// impl HasTop for Bool4 { +// fn top() -> Self +// where +// Self: Sized, +// { +// Self::Both +// } + +// #[inline(always)] +// fn is_top(&self) -> bool { +// self.is_both() +// } +// } + +// impl HasBottom for Bool4 { +// fn bottom() -> Self +// where +// Self: Sized, +// { +// Self::None +// } + +// fn is_bottom(&self) -> bool { +// self.is_none() +// } +// } + +pub struct PowerSet { + empty_set: Set, +} + +impl PowerSet { + pub fn new() -> Self { + Self { + empty_set: Set::new(), + } + } + pub fn singleton(&self, value: T) -> Set { + let mut set = self.empty_set.clone(); + set.insert(value); + set + } +} + +impl Poset for PowerSet { + type Element = Set; +} + +impl HasTop for PowerSet { + fn top(&self) -> Self::Element { + self.empty_set.clone() + } + + fn is_top(&self, element: &Self::Element) -> bool { + element.0.is_empty() + } +} + +#[cfg(test)] +mod tests { + use super::*; + + macro_rules! assert_order { + ($value:expr, [$($lt:expr),*], [$($nlt:expr),*]) => { + assert!(!$value.is_less_than($value)); + $(assert!($value.is_less_than($lt));)* + $(assert!(!$value.is_less_than($nlt));)* + }; + } + + macro_rules! assert_table_binary { + ([$($values:expr),*], $func:ident, $table:expr) => {{ + let table = $table; + for (i, left) in [$($values),*].into_iter().enumerate() { + for (j, right) in [$($values),*].into_iter().enumerate() { + assert_eq!( + left.$func(right), + table[i][j], + "{}({left:?}, {right:?}) is {:?}, expected {:?}", + stringify!($func), + left.$func(right), + table[i][j] + ); + } + } + }}; + } + + #[test] + fn test_bool3_order() { + use Bool3::*; + assert_order!(&Unknown, [], [&True, &False]); + assert_order!(&True, [&Unknown], [&False]); + assert_order!(&False, [&Unknown], [&True]); + } + + #[test] + fn test_bool3_not() { + use Bool3::*; + assert_eq!(Unknown.not(), Unknown); + assert_eq!(True.not(), False); + assert_eq!(False.not(), True); + } + + #[test] + fn test_bool3_and() { + use Bool3::*; + assert_table_binary!( + [Unknown, True, False], + and, + [ + [Unknown, Unknown, False], + [Unknown, True, False], + [False, False, False] + ] + ); + } + + #[test] + fn test_bool3_join() { + use Bool3::*; + assert_table_binary!( + [Unknown, True, False], + join_move, + [ + [Unknown, Unknown, Unknown], + [Unknown, True, Unknown], + [Unknown, Unknown, False] + ] + ); + } + + #[test] + fn test_bool3_or() { + use Bool3::*; + assert_table_binary!( + [Unknown, True, False], + or, + [ + [Unknown, True, Unknown], + [True, True, True], + [Unknown, True, False] + ] + ); + } + + #[test] + fn test_bool4_order() { + use Bool4::*; + assert_order!(&Both, [], [&None, &True, &False]); + assert_order!(&True, [&Both], [&None, &False]); + assert_order!(&False, [&Both], [&None, &True]); + assert_order!(&None, [&Both, &True, &False], []); + } + + #[test] + fn test_bool4_not() { + use Bool4::*; + assert_eq!(Both.not(), Both); + assert_eq!(True.not(), False); + assert_eq!(False.not(), True); + assert_eq!(None.not(), None); + } + + #[test] + fn test_bool4_and() { + use Bool4::*; + assert_table_binary!( + [Both, True, False, None], + and, + [ + [Both, Both, False, False], + [Both, True, False, None], + [False, False, False, False], + [False, None, False, None] + ] + ); + } + + #[test] + fn test_bool4_or() { + use Bool4::*; + assert_table_binary!( + [Both, True, False, None], + or, + [ + [Both, True, Both, True], + [True, True, True, True], + [Both, True, False, None], + [True, True, None, None] + ] + ); + } + + #[test] + fn test_bool4_join() { + use Bool4::*; + assert_table_binary!( + [Both, True, False, None], + join_move, + [ + [Both, Both, Both, Both], + [Both, True, Both, True], + [Both, Both, False, False], + [Both, True, False, None], + ] + ); + } + #[test] + fn test_bool4_meet() { + use Bool4::*; + assert_table_binary!( + [Both, True, False, None], + meet_move, + [ + [Both, True, False, None], + [True, True, None, None], + [False, None, False, None], + [None, None, None, None], + ] + ); + } +} diff --git a/engine/crates/vts-synth/src/frontends/mod.rs b/engine/crates/vts-synth/src/frontends/mod.rs new file mode 100644 index 00000000..0cc9a60a --- /dev/null +++ b/engine/crates/vts-synth/src/frontends/mod.rs @@ -0,0 +1,4 @@ +//! Frontends for different model formats. + +pub mod vibes; +pub mod yaml; diff --git a/engine/crates/vts-synth/src/frontends/vibes.rs b/engine/crates/vts-synth/src/frontends/vibes.rs new file mode 100644 index 00000000..cf193849 --- /dev/null +++ b/engine/crates/vts-synth/src/frontends/vibes.rs @@ -0,0 +1,132 @@ +//! Frontend for the XML-based FTS format used by the +//! [VIBeS project](https://projects.info.unamur.be/vibes/index.html). + +use std::{str::FromStr, sync::Arc}; + +use serde::Deserialize; +use thiserror::Error; + +use crate::{ + logic::propositional::Formula, + ts::{ + types::{Vats, VatsLabel}, + TsBuilder, + }, +}; + +/// An XML `` element specifying the FTS. +#[derive(Debug, Clone, Deserialize)] +struct XmlFts { + /// The initial state of the FTS. + start: Arc, + /// The states of the FTS. + states: XmlStates, +} + +/// An XML `` element containing the states of the FTS. +#[derive(Debug, Clone, Deserialize)] +struct XmlStates { + /// The states. + #[serde(rename = "state")] + states: Vec, +} + +/// An XML `` element describing a state and its transitions. +#[derive(Debug, Clone, Deserialize)] +struct XmlState { + /// The id of the state. + #[serde(rename = "@id")] + id: Arc, + /// The transitions of the state. + #[serde(rename = "transition", default)] + transitions: Vec, +} + +/// An XML `` element describing a transition. +#[derive(Debug, Clone, Deserialize)] +struct XmlTransition { + /// The optional action of the transition. + #[serde(rename = "@action")] + action: Option>, + /// The feature guard of the transition. + #[serde(rename = "@fexpression")] + guard: Option>, + /// The target state of the transition. + #[serde(rename = "@target")] + target: Arc, +} + +/// A parsing error. +#[derive(Debug, Error)] +#[error(transparent)] +pub struct Error(#[from] ErrorInner); + +/// Inner error type. +#[derive(Debug, Error)] +enum ErrorInner { + /// An XML parsing error. + #[error(transparent)] + Xml(#[from] quick_xml::DeError), + /// Error parsing a feature guard. + #[error("Error parsing formula {formula}.")] + Guard { + formula: Arc, + error: as FromStr>::Err, + }, +} + +/// Constructs an FTS from the provided string. +pub fn from_str(xml: &str) -> Result, Option>, Formula>, Error> { + quick_xml::de::from_str(xml) + .map_err(ErrorInner::Xml) + .and_then(|xml_fts: XmlFts| { + let mut builder = TsBuilder::new(); + let initial = builder.insert_state(xml_fts.start.clone()); + builder.mark_initial(initial); + for xml_state in xml_fts.states.states { + let source = builder.insert_state(xml_state.id.clone()); + for xml_transition in xml_state.transitions { + let guard = xml_transition.guard.map_or_else( + || Ok(Formula::True), + |guard| { + guard.parse().map_err(|error| ErrorInner::Guard { + formula: guard.clone(), + error, + }) + }, + )?; + let target = builder.insert_state(xml_transition.target.clone()); + builder.insert_transition( + source, + VatsLabel::new(xml_transition.action, guard), + target, + ) + } + } + Ok(builder.build()) + }) + .map_err(Into::into) +} + +#[cfg(test)] +mod tests { + use std::fs; + + use super::*; + + macro_rules! impl_test_fn_for_model { + ($func:ident, $file:literal) => { + #[test] + fn $func() { + let xml = fs::read_to_string(concat!("../../models/vibes/", $file)).unwrap(); + from_str(&xml).unwrap(); + } + }; + } + + impl_test_fn_for_model!(test_aerouc5, "aerouc5.fts.xml"); + impl_test_fn_for_model!(test_claroline, "claroline-pow.fts.xml"); + impl_test_fn_for_model!(test_cpterminal, "cpterminal.fts.xml"); + impl_test_fn_for_model!(test_minepump, "minepump.fts.xml"); + impl_test_fn_for_model!(test_svm, "svm.fts.xml"); +} diff --git a/engine/crates/vts-synth/src/frontends/yaml.rs b/engine/crates/vts-synth/src/frontends/yaml.rs new file mode 100644 index 00000000..7a7a1a06 --- /dev/null +++ b/engine/crates/vts-synth/src/frontends/yaml.rs @@ -0,0 +1,60 @@ +use std::{collections::HashMap, sync::Arc}; + +use serde::Deserialize; +use thiserror::Error; + +use crate::ts::{Ts, TsBuilder}; + +#[derive(Debug, Clone, Deserialize)] +struct YamlTs { + states: HashMap, YamlStateData>, + transitions: Vec, +} + +#[derive(Debug, Clone, Deserialize)] +#[serde(rename_all = "camelCase")] +struct YamlStateData { + is_initial: Option, +} + +#[derive(Debug, Clone, Deserialize)] +struct YamlTransition { + source: Arc, + target: Arc, + action: Option>, +} + +/// A parsing error. +#[derive(Debug, Error)] +#[error(transparent)] +pub struct Error(#[from] ErrorInner); + +/// Inner error type. +#[derive(Debug, Error)] +enum ErrorInner { + /// A YAML parsing error. + #[error(transparent)] + Yaml(#[from] serde_yaml::Error), +} + +/// Constructs an FTS from the provided string. +pub fn from_str(yaml: &str) -> Result, Option>>, Error> { + serde_yaml::from_str(yaml) + .map_err(ErrorInner::Yaml) + .and_then(|yaml_ts: YamlTs| { + let mut builder = TsBuilder::new(); + for (state_name, state_data) in yaml_ts.states { + let state_id = builder.insert_state(state_name.clone()); + if state_data.is_initial.unwrap_or_default() { + builder.mark_initial(state_id); + } + } + for transition in yaml_ts.transitions { + let source_id = builder.insert_state(transition.source); + let target_id = builder.insert_state(transition.target); + builder.insert_transition(source_id, transition.action, target_id); + } + Ok(builder.build()) + }) + .map_err(Into::into) +} diff --git a/engine/crates/vts-synth/src/lattice.rs b/engine/crates/vts-synth/src/lattice.rs new file mode 100644 index 00000000..c23ce705 --- /dev/null +++ b/engine/crates/vts-synth/src/lattice.rs @@ -0,0 +1,299 @@ +//! Data structures and abstractions for partially ordered sets. + +use std::hash::Hash; + +/// A type whose values are partially ordered. +pub trait PartiallyOrdered: Eq { + /// Returns `true` if and only if `self` is strictly less than `other`. + fn is_less_than(&self, other: &Self) -> bool; + + /// Returns `true` if and only if `self` is strictly greater than `other`. + #[inline] + fn is_greater_than(&self, other: &Self) -> bool { + other.is_less_than(self) + } + + /// Returns `true` if and only if `self` is at most `other`. + #[inline] + fn is_at_most(&self, other: &Self) -> bool { + self.is_less_than(other) || self == other + } + + /// Returns `true` if and only if `self` is at least `other`. + #[inline] + fn is_at_least(&self, other: &Self) -> bool { + self.is_greater_than(other) || self == other + } + + /// Returns `true` if and only if `self` and `other` are incomparable. + #[inline] + fn are_incomparable(&self, other: &Self) -> bool { + !self.is_less_than(other) && !other.is_less_than(self) && self != other + } +} + +/// A type with a least upper bound (_meet_) for any pair of values. +pub trait Meet: PartiallyOrdered { + /// Computes the meet of `self` and `other`. + fn meet(&self, other: &Self) -> Self; + + /// Computes the meet of `self` and `other` and stores the result in `self`. + /// + /// Returns `true` if `self` has changed. + #[inline] + fn meet_assign(&mut self, other: &Self) -> bool + where + Self: Sized, + { + let mut meet = self.meet(other); + std::mem::swap(self, &mut meet); + return *self != meet; + } + + /// Computes the meet of `self` and `other`. + #[inline] + fn meet_move(self, other: Self) -> Self + where + Self: Sized, + { + self.meet(&other) + } +} + +/// A type with a greatest lower bound (_join_) for any pair of values. +pub trait Join: PartiallyOrdered { + /// Computes the join of `self` and `other`. + fn join(&self, other: &Self) -> Self; + + /// Computes the join of `self` and `other` and stores the result in `self`. + /// + /// Returns `true` if `self` has changed. + #[inline] + fn join_assign(&mut self, other: &Self) -> bool + where + Self: Sized, + { + let mut join = self.join(other); + std::mem::swap(self, &mut join); + return *self != join; + } + + /// Computes the join of `self` and `other`. + #[inline] + fn join_move(self, other: Self) -> Self + where + Self: Sized, + { + self.join(&other) + } +} + +/// A partially ordered set. +pub trait Poset { + /// The element type of the set implementing [`PartiallyOrdered`]. + type Element: PartiallyOrdered; +} + +/// An upper-bounded partially ordered set with a unique top element. +pub trait HasTop: Poset { + /// Returns the top element. + fn top(&self) -> Self::Element; + + /// Checks whether the given `element` is the top element. + fn is_top(&self, element: &Self::Element) -> bool; +} + +/// A lower-bounded partially ordered set with a unique bottom element. +pub trait HasBottom: Poset { + /// Returns the bottom element. + fn bottom(&self) -> Self::Element; + + /// Checks whether the given `element` is the bottom element. + fn is_bottom(&self, element: &Self::Element) -> bool; +} + +/// A join-semilattice is a partially ordered set where every pair of elements has a join. +/// +/// This trait is automatically implemented for every eligible [`Poset`]. +pub trait JoinSemiLattice: Poset {} + +impl JoinSemiLattice for T where T::Element: Join {} + +/// A meet-semilattice is a partially ordered set where every pair of elements has a meet. +/// +/// This trait is automatically implemented for every eligible [`Poset`]. +pub trait MeetSemiLattice: Poset {} + +impl MeetSemiLattice for T where T::Element: Meet {} + +/// A lattice is a partially ordered set where every pair of elements has a join and meet. +/// +/// This trait is automatically implemented for every eligible [`Poset`]. +pub trait Lattice: JoinSemiLattice + MeetSemiLattice {} + +impl Lattice for T where T::Element: Meet + Join {} + +/// A set of elements of type `T`. +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct Set(pub(crate) im::HashSet); + +impl Set { + pub fn new() -> Self { + Self(im::HashSet::new()) + } + + /// Inserts an element into the set. + pub fn insert(&mut self, element: T) { + self.0.insert(element); + } + + /// Removes an element from the set. + pub fn remove(&mut self, element: &T) -> Option { + self.0.remove(element) + } + + /// An iterator over the set's elements. + pub fn iter(&self) -> impl Iterator { + self.0.iter() + } +} + +impl<'s, T: Clone + Hash + Eq> IntoIterator for &'s Set { + type Item = &'s T; + + type IntoIter = im::hashset::Iter<'s, T>; + + fn into_iter(self) -> Self::IntoIter { + self.0.iter() + } +} + +impl PartiallyOrdered for Set { + fn is_less_than(&self, other: &Self) -> bool { + self.0.is_proper_subset(&other.0) + } + + fn is_at_most(&self, other: &Self) -> bool { + self.0.is_subset(&other.0) + } +} + +impl Meet for Set { + fn meet(&self, other: &Self) -> Self { + Self(self.0.clone().intersection(other.0.clone())) + } +} + +impl Join for Set { + fn join(&self, other: &Self) -> Self { + Self(self.0.clone().union(other.0.clone())) + } +} + +// impl HasBottom for Set { +// fn is_bottom(&self) -> bool { +// self.0.is_empty() +// } +// } + +impl Default for Set { + fn default() -> Self { + Self(Default::default()) + } +} + +impl FromIterator for Set { + fn from_iter>(iter: I) -> Self { + Self(iter.into_iter().collect()) + } +} + +/// The dual of a partially ordered type `T`. +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct Dual(T); + +impl Dual { + pub fn new(inner: T) -> Self { + Self(inner) + } + + pub fn into_inner(self) -> T { + self.0 + } + + pub fn inner(&self) -> &T { + &self.0 + } +} + +impl PartiallyOrdered for Dual { + fn is_less_than(&self, other: &Self) -> bool { + self.0.is_greater_than(&other.0) + } + + fn is_greater_than(&self, other: &Self) -> bool { + self.0.is_less_than(&other.0) + } + + fn is_at_most(&self, other: &Self) -> bool { + self.0.is_at_least(&other.0) + } + + fn is_at_least(&self, other: &Self) -> bool { + self.0.is_at_most(&other.0) + } + + fn are_incomparable(&self, other: &Self) -> bool { + self.0.are_incomparable(&other.0) + } +} + +impl Join for Dual { + fn join(&self, other: &Self) -> Self { + Self(self.0.meet(&other.0)) + } + + fn join_assign(&mut self, other: &Self) -> bool + where + Self: Sized, + { + self.0.meet_assign(&other.0) + } +} + +impl Meet for Dual { + fn meet(&self, other: &Self) -> Self { + Self(self.0.join(&other.0)) + } + + fn meet_assign(&mut self, other: &Self) -> bool + where + Self: Sized, + { + self.0.join_assign(&other.0) + } +} + +impl Poset for Dual { + type Element = Dual; +} + +impl HasTop for Dual { + fn top(&self) -> Self::Element { + Dual(self.0.bottom()) + } + + fn is_top(&self, element: &Self::Element) -> bool { + self.0.is_bottom(&element.0) + } +} + +impl HasBottom for Dual { + fn bottom(&self) -> Self::Element { + Dual(self.0.top()) + } + + fn is_bottom(&self, element: &Self::Element) -> bool { + self.0.is_top(&element.0) + } +} diff --git a/engine/crates/vts-synth/src/logic.rs b/engine/crates/vts-synth/src/logic.rs new file mode 100644 index 00000000..5866b94e --- /dev/null +++ b/engine/crates/vts-synth/src/logic.rs @@ -0,0 +1,3 @@ +//! Functionality for dealing with logical formulas. + +pub mod propositional; diff --git a/engine/crates/vts-synth/src/logic/propositional.rs b/engine/crates/vts-synth/src/logic/propositional.rs new file mode 100644 index 00000000..300168c2 --- /dev/null +++ b/engine/crates/vts-synth/src/logic/propositional.rs @@ -0,0 +1,170 @@ +//! Symbolic propositional logic feature guards. + +use std::{fmt::Write, str::FromStr}; + +mod parser; + +/// A symbolic propositional logic formula usable as a guard. +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub enum Formula { + Atom(F), + And(Vec), + Or(Vec), + Xor(Vec), + Not(Box), + True, + False, +} + +impl Formula { + pub fn and(self, other: Self) -> Self { + match (self, other) { + (Self::False, _) => Self::False, + (_, Self::False) => Self::False, + (left, Self::True) => left, + (Self::True, right) => right, + (Self::And(mut left), Self::And(right)) => { + left.extend(right.into_iter()); + Self::And(left) + } + (Self::And(mut left), right) => { + left.push(right); + Self::And(left) + } + (left, Self::And(mut right)) => { + right.insert(0, left); + Self::And(right) + } + (left, right) => Self::And(vec![left, right]), + } + } + + pub fn or(self, other: Self) -> Self { + match (self, other) { + (Self::True, Self::True) => Self::False, + (_, Self::True) => Self::True, + (Self::True, _) => Self::True, + (left, Self::False) => left, + (Self::False, right) => right, + (Self::Or(mut left), Self::Or(right)) => { + left.extend(right.into_iter()); + Self::Or(left) + } + (Self::Or(mut left), right) => { + left.push(right); + Self::Or(left) + } + (left, Self::Or(mut right)) => { + right.insert(0, left); + Self::Or(right) + } + (left, right) => Self::Or(vec![left, right]), + } + } + + pub fn xor(self, other: Self) -> Self { + match (self, other) { + (Self::True, _) => Self::True, + (_, Self::True) => Self::True, + (left, Self::False) => left, + (Self::False, right) => right, + (Self::Xor(mut left), Self::Xor(right)) => { + left.extend(right.into_iter()); + Self::Xor(left) + } + (Self::Xor(mut left), right) => { + left.push(right); + Self::Xor(left) + } + (left, Self::Xor(mut right)) => { + right.insert(0, left); + Self::Xor(right) + } + (left, right) => Self::Xor(vec![left, right]), + } + } + + pub fn not(self) -> Self { + match self { + Formula::True => Self::False, + Formula::False => Self::True, + Formula::Not(operand) => *operand, + guard => Self::Not(Box::new(guard)), + } + } + + pub fn traverse(&self) -> impl Iterator { + let mut stack = vec![self]; + std::iter::from_fn(move || { + stack.pop().map(|top| { + match top { + Formula::And(children) | Formula::Or(children) => stack.extend(children), + Formula::Not(child) => stack.push(child.as_ref()), + _ => { + // Nothing to do! + } + }; + top + }) + }) + } +} + +impl std::fmt::Display for Formula { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Formula::Atom(feature) => feature.fmt(f), + Formula::And(operands) => { + f.write_char('(')?; + let mut first = true; + for operand in operands { + if !first { + f.write_str(" && ")?; + } + operand.fmt(f)?; + first = false; + } + f.write_char(')') + } + Formula::Or(operands) => { + f.write_char('(')?; + let mut first = true; + for operand in operands { + if !first { + f.write_str(" || ")?; + } + operand.fmt(f)?; + first = false; + } + f.write_char(')') + } + Formula::Xor(operands) => { + f.write_char('(')?; + let mut first = true; + for operand in operands { + if !first { + f.write_str(" xor ")?; + } + operand.fmt(f)?; + first = false; + } + f.write_char(')') + } + Formula::Not(operand) => { + f.write_char('!')?; + operand.fmt(f) + } + Formula::True => f.write_str("true"), + Formula::False => f.write_str("false"), + } + } +} + +impl FromStr for Formula { + type Err = Vec; + + fn from_str(s: &str) -> Result { + let (formula, errors) = parser::parse(s); + formula.ok_or(errors) + } +} diff --git a/engine/crates/vts-synth/src/logic/propositional/parser.rs b/engine/crates/vts-synth/src/logic/propositional/parser.rs new file mode 100644 index 00000000..fc39ee89 --- /dev/null +++ b/engine/crates/vts-synth/src/logic/propositional/parser.rs @@ -0,0 +1,320 @@ +//! Parser for propositional logic formulae. + +use std::{ + fmt::{self, Write}, + ops::Range, +}; + +use ariadne::{Color, Fmt, Label, Report, ReportKind, Source}; +use chumsky::{error::SimpleReason, prelude::*, Stream}; + +use super::Formula; + +type Span = Range; + +type Spanned = (T, Span); + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +enum Token { + Identifier(String), + + ParenthesisOpen, + ParenthesisClose, + + OperatorNot, + OperatorAnd, + OperatorOr, + OperatorImplies, + OperatorIff, + OperatorXor, + + ConstantTrue, + ConstantFalse, +} + +impl fmt::Display for Token { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Token::Identifier(identifier) => f.write_str(identifier), + Token::ParenthesisOpen => f.write_char('('), + Token::ParenthesisClose => f.write_char(')'), + Token::OperatorNot => f.write_char('!'), + Token::OperatorAnd => f.write_str("and"), + Token::OperatorOr => f.write_str("or"), + Token::OperatorImplies => f.write_str("==>"), + Token::OperatorIff => f.write_str("<=>"), + Token::OperatorXor => f.write_str("xor"), + Token::ConstantTrue => f.write_str("True"), + Token::ConstantFalse => f.write_str("False"), + } + } +} + +/// Lexer for propositional logic formulae. +fn lexer() -> impl Parser>, Error = Simple> { + fn lexer_operator_not() -> impl Parser> { + just('!').or(just('~')).or(just('¬')).to(Token::OperatorNot) + } + + fn lexer_operator_and() -> impl Parser> { + just("&&") + .or(just("&")) + .or(just("AND")) + .or(just("and")) + .or(just("∧")) + .to(Token::OperatorAnd) + } + + fn lexer_operator_or() -> impl Parser> { + just("||") + .or(just("|")) + .or(just("OR")) + .or(just("or")) + .or(just("v")) + .or(just("∨")) + .to(Token::OperatorOr) + } + + fn lexer_operator_xor() -> impl Parser> { + just("XOR").or(just("xor")).to(Token::OperatorXor) + } + + fn lexer_operator_implies() -> impl Parser> { + just("==>") + .or(just("=>")) + .or(just("->")) + .or(just("⇒")) + .or(just("→")) + .to(Token::OperatorImplies) + } + + fn lexer_operator_iff() -> impl Parser> { + just("<=>") + .or(just("<->")) + .or(just("⇔")) + .or(just("↔")) + .to(Token::OperatorIff) + } + + fn lexer_constant_true() -> impl Parser> { + just("1").or(just("⊤")).to(Token::ConstantTrue) + } + + fn lexer_constant_false() -> impl Parser> { + just("0").or(just("⊥")).to(Token::ConstantFalse) + } + + let token = text::ident() + .map(|identifier: String| match identifier.as_str() { + "True" | "true" => Token::ConstantTrue, + "False" | "false" => Token::ConstantFalse, + _ => Token::Identifier(identifier), + }) + .or(just('(').to(Token::ParenthesisOpen)) + .or(just(')').to(Token::ParenthesisClose)) + .or(lexer_operator_not()) + .or(lexer_operator_and()) + .or(lexer_operator_or()) + .or(lexer_operator_implies()) + .or(lexer_operator_iff()) + .or(lexer_operator_xor()) + .or(lexer_constant_true()) + .or(lexer_constant_false()); + + token + .map_with_span(|token, span| (token, span)) + .padded() + .repeated() + .then_ignore(end()) +} + +/// Parser for propositional logic formulae. +fn parser() -> impl Parser, Error = Simple> { + recursive(|formula| { + let primary = formula + .delimited_by(just(Token::ParenthesisOpen), just(Token::ParenthesisClose)) + .or(select! { + Token::Identifier(identifier) => Formula::Atom(identifier), + Token::ConstantTrue => Formula::True, + Token::ConstantFalse => Formula::False, + }); + + let negation = just(Token::OperatorNot) + .repeated() + .then(primary) + .foldr(|_, operand| operand.not()); + + let xor = negation + .clone() + .then(just(Token::OperatorXor).ignore_then(negation).repeated()) + .foldl(|left, right| left.xor(right)); + + let conjunction = xor + .clone() + .then(just(Token::OperatorAnd).ignore_then(xor).repeated()) + .foldl(|left, right| left.and(right)); + + let disjunction = conjunction + .clone() + .then(just(Token::OperatorOr).ignore_then(conjunction).repeated()) + .foldl(|left, right| left.or(right)); + + let implication = recursive(|implication| { + disjunction + .then( + just(Token::OperatorImplies) + .ignore_then(implication) + .or_not(), + ) + .map(|(left, right)| { + if let Some(right) = right { + left.not().or(right) + } else { + left + } + }) + }); + + let equivalence = recursive(|equivalence| { + implication + .then(just(Token::OperatorIff).ignore_then(equivalence).or_not()) + .map(|(left, right): (_, Option>)| { + if let Some(right) = right { + left.clone() + .and(right.clone()) + .or(left.not().and(right.not())) + } else { + left + } + }) + }); + + equivalence + }) + .then_ignore(end()) +} + +#[derive(Debug)] +pub struct Error(Simple); + +impl Error { + pub fn eprint(&self, formula: &str) { + let report = Report::build(ReportKind::Error, (), self.0.span().start); + + match self.0.reason() { + SimpleReason::Unclosed { span, delimiter } => report + .with_message(format!( + "Unclosed delimiter {}", + delimiter.fg(Color::Yellow) + )) + .with_label( + Label::new(span.clone()) + .with_message(format!( + "Unclosed delimiter {}", + delimiter.fg(Color::Yellow) + )) + .with_color(Color::Yellow), + ) + .with_label( + Label::new(self.0.span()) + .with_message(format!( + "Must be closed before this {}", + self.0 + .found() + .unwrap_or(&"end of file".to_string()) + .fg(Color::Red) + )) + .with_color(Color::Red), + ), + SimpleReason::Unexpected => report + .with_message(format!( + "{}, expected {}", + if self.0.found().is_some() { + "Unexpected token in input" + } else { + "Unexpected end of input" + }, + if self.0.expected().len() == 0 { + "something else".to_string() + } else { + self.0 + .expected() + .map(|expected| match expected { + Some(expected) => expected.to_string(), + None => "end of input".to_string(), + }) + .collect::>() + .join(", ") + } + )) + .with_label( + Label::new(self.0.span()) + .with_message(format!( + "Unexpected token {}", + self.0 + .found() + .unwrap_or(&"end of file".to_string()) + .fg(Color::Red) + )) + .with_color(Color::Red), + ), + chumsky::error::SimpleReason::Custom(msg) => report.with_message(msg).with_label( + Label::new(self.0.span()) + .with_message(format!("{}", msg.fg(Color::Red))) + .with_color(Color::Red), + ), + } + .finish() + .eprint(Source::from(formula)) + .unwrap(); + } +} + +pub fn parse(formula: &str) -> (Option>, Vec) { + let (tokens, lexer_errors) = lexer().parse_recovery(formula); + + let mut errors = lexer_errors + .into_iter() + .map(|error| Error(error.map(|c| c.to_string()))) + .collect::>(); + + let result = tokens.and_then(|tokens| { + let len = formula.chars().count(); + + let (formula, parser_errors) = + parser().parse_recovery(Stream::from_iter(len..len + 1, tokens.into_iter())); + + errors.extend( + parser_errors + .into_iter() + .map(|error| Error(error.map(|t| t.to_string()))), + ); + + formula + }); + + (result, errors) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + pub fn test_parser() { + assert_eq!( + parse("!a && b || c").0.unwrap(), + Formula::Atom("a".to_owned()) + .not() + .and(Formula::Atom("b".to_owned())) + .or(Formula::Atom("c".to_owned())) + ); + + assert_eq!( + parse("!a && (b || c)").0.unwrap(), + Formula::Atom("a".to_owned()) + .not() + .and(Formula::Atom("b".to_owned()).or(Formula::Atom("c".to_owned()))) + ); + } +} diff --git a/engine/crates/vts-synth/src/main.rs b/engine/crates/vts-synth/src/main.rs new file mode 100644 index 00000000..65512f48 --- /dev/null +++ b/engine/crates/vts-synth/src/main.rs @@ -0,0 +1,71 @@ +// Required for hiding iterator types returned by TS traits. +#![feature(impl_trait_in_assoc_type)] + +use std::{path::PathBuf, sync::Arc}; + +use clap::{Args, Parser, Subcommand}; + +pub mod algorithms; +pub mod cudd; +pub mod domains; +pub mod frontends; +pub mod lattice; +pub mod logic; +pub mod synthesis; +pub mod ts; +pub mod ts_traits; + +#[derive(Parser)] +pub struct Arguments { + model_path: PathBuf, + + feature_model: Option, + + #[clap(long)] + steps: Option, + + #[clap(long)] + output_path: Option, + + #[clap(long)] + unobservable: Vec>, + + // #[clap(long)] + // feature: Vec, + #[clap(long)] + output_state_spaces: bool, + + #[clap(long)] + with_lookahead_refinement: bool, + + #[clap(long)] + without_minimization: bool, + + #[command(flatten)] + minimization: MinimizationOpts, + + #[clap(long)] + simulate: bool, +} + +#[derive(Args)] +pub struct MinimizationOpts { + #[clap(long)] + relax_language: bool, +} + +#[derive(Subcommand)] +pub enum Command { + /// Synthesize a configuration monitor. + Confmon, + /// Synthesize a diagnoser. + Diagnoser, +} + +pub fn main() { + tracing_subscriber::fmt::init(); + + let arguments = Arguments::parse(); + + synthesis::confmon::synthesize(&arguments); +} diff --git a/engine/crates/vts-synth/src/synthesis/confmon.rs b/engine/crates/vts-synth/src/synthesis/confmon.rs new file mode 100644 index 00000000..d76f9baa --- /dev/null +++ b/engine/crates/vts-synth/src/synthesis/confmon.rs @@ -0,0 +1,856 @@ +//! Synthesis of configuration monitors. + +use std::{collections::HashMap, hash::Hash, ops::Deref, time::Instant}; + +use hashbrown::HashSet; +use indexmap::IndexSet; +use rand::seq::SliceRandom; +use serde::{Deserialize, Serialize}; +use tracing::info; + +use crate::{ + algorithms, + cudd::{self, BddManager, BddNode}, + frontends, + lattice::PartiallyOrdered, + logic::propositional::Formula, + ts::{ + self, + output::dot::TransitionAttributes, + traits::{ + BaseTs, InitialStates, MakeDenseStateSet, MakeSparseStateSet, Predecessors, StateSet, + States, Successors, Transitions, TsRef, + }, + types::{VatsLabel, Vts, VtsState}, + StateId, Ts, + }, + Arguments, +}; + +pub fn bdd_to_formula(atoms: &[T], node: &BddNode) -> Formula { + let mut result = Formula::False; + for cube in node.cubes() { + let mut formula = Formula::True; + for (idx, trinary) in cube.iter().enumerate() { + match trinary { + cudd::Trinary::False => { + formula = formula.and(Formula::Atom(atoms[idx].clone()).not()) + } + cudd::Trinary::True => formula = formula.and(Formula::Atom(atoms[idx].clone())), + cudd::Trinary::Any => { + // Value is irrelevant. Do nothing. + } + } + } + result = result.or(formula); + } + result +} + +fn translate_guard<'m>( + manager: &'m BddManager, + features: &mut IndexSet, + formula: &Formula, +) -> BddNode<'m> { + match formula { + Formula::Atom(feature) => { + if let Some(index) = features.get_index_of(feature) { + manager.var(index as i32) + } else { + features.insert(feature.clone()); + manager.var(features.get_index_of(feature).unwrap() as i32) + } + } + Formula::And(operators) => operators + .iter() + .map(|operand| translate_guard(manager, features, operand)) + .fold(manager.one(), |left, right| left.and(&right)), + Formula::Or(operators) => operators + .iter() + .map(|operand| translate_guard(manager, features, operand)) + .fold(manager.zero(), |left, right| left.or(&right)), + Formula::Not(operand) => translate_guard(manager, features, operand).complement(), + Formula::True => manager.one(), + Formula::False => manager.zero(), + Formula::Xor(operators) => operators + .iter() + .map(|operand| translate_guard(manager, features, operand)) + .fold(manager.zero(), |left, right| left.xor(&right)), + } +} + +fn print_ts_info< + TS: TsRef + States + InitialStates + Transitions + Successors + Predecessors + MakeDenseStateSet, +>( + ts: TS, +) { + println!( + "States: {} (initial: {})", + ts.num_states(), + ts.num_initial_states() + ); + println!("Transitions: {}", ts.num_transitions()); + println!("SCCs: {}", ts::algorithms::scc(ts).count()); +} + +pub fn is_state_deterministic( + vts: &Ts, + state: StateId, +) -> bool { + let mut labels = HashSet::new(); + for outgoing in vts.outgoing(&state) { + if !labels.insert(outgoing.action()) { + println!("non-deterministic action: {:?}", outgoing.action()); + return false; + } + } + true +} + +pub fn is_deterministic(vts: &Ts) -> bool { + vts.states().all(|state| is_state_deterministic(vts, state)) +} + +pub fn is_monotonic(vts: &Vts) -> bool +where + V: PartiallyOrdered, +{ + vts.transitions().all(|transition| { + let source = vts.get_label(&transition.source()); + let target = vts.get_label(&transition.target()); + target.verdict.is_at_most(&source.verdict) + }) +} + +fn print_vts_info(vts: &Vts) { + print_ts_info(vts); + println!("Deterministic: {:?}", is_deterministic(vts)); + println!("Monotonic: {:?}", is_monotonic(vts)); +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct StepInfo { + duration_ms: u64, + input_ts: TsInfo, + output_ts: TsInfo, + data: HashMap, +} + +impl StepInfo { + pub fn new(duration_ms: u64, input_ts: TsInfo, output_ts: TsInfo) -> Self { + Self { + duration_ms, + input_ts, + output_ts, + data: HashMap::new(), + } + } + + pub fn set>(&mut self, key: &str, value: V) { + self.data.insert(key.to_owned(), value.into()); + } +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct TsInfo { + num_states: u64, + num_transitions: u64, +} + +pub fn ts_info(ts: T) -> TsInfo { + TsInfo { + num_states: ts.num_states() as u64, + num_transitions: ts.num_transitions() as u64, + } +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct Report { + input: TsInfo, + output: TsInfo, + data: HashMap, + steps: Vec, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct QualityMetrics { + num_runs: u64, + run_length: u64, + non_live_runs: u64, + score: f64, + final_state: f64, + num_runs_end: u64, + run_length_end: f64, +} + +pub fn synthesize(arguments: &Arguments) { + let xml = std::fs::read_to_string(&arguments.model_path).unwrap(); + + if let Some(output_path) = &arguments.output_path { + std::fs::create_dir_all(output_path).unwrap(); + } + + let mut steps = Vec::new(); + + let fts = frontends::vibes::from_str(&xml).unwrap(); + + if let Some(output_path) = &arguments.output_path { + ts::output::dot::write_to_file( + &fts, + "Input FTS", + &output_path.join("0_input_fts.dot"), + |_, state| state.to_string(), + |transition| { + ts::output::dot::TransitionAttributes::new(&format!( + "{} [{}]", + transition.action().action.as_deref().unwrap_or("τ"), + transition.action().guard.to_string() + )) + }, + ) + .unwrap(); + } + + // Extract the features of the model. + let mut features = fts + .transitions() + .map(|transition| { + transition.action().guard.traverse().filter_map(|formula| { + if let Formula::Atom(feature) = formula { + Some(feature) + } else { + None + } + }) + }) + .flatten() + .cloned() + .collect::>(); + + // Extract the observables of the model. + let observables = fts + .transitions() + .filter_map(|transition| transition.action().action.clone()) + .filter(|action| { + arguments + .unobservable + .iter() + .all(|unobservable| !action.starts_with(unobservable.deref())) + }) + .collect::>(); + + let actions = observables + .iter() + .map(|act| Some(act.clone())) + .collect::>(); + + // Construct an equivalent FTS using BDDs as guards. + let manager = BddManager::with_vars(features.len() as u32); + // manager.enable_reordering(cudd::ReorderingType::Sift); + + let feature_model = if let Some(feature_model_path) = &arguments.feature_model { + let formula = std::fs::read_to_string(feature_model_path) + .unwrap() + .parse::>() + .unwrap(); + + translate_guard(&manager, &mut features, &formula) + } else { + manager.one() + }; + + let fts = fts.map_labels(|label| { + VatsLabel::new( + label.action.clone(), + translate_guard(&manager, &mut features, &label.guard), + ) + }); + + let atoms = features.iter().cloned().collect::>(); + + println!("Features: {features:?}"); + println!("\n\nConfigs: {}", feature_model.count_solutions()); + println!("\n\nObservables: {} {observables:?}\n\n", observables.len()); + + println!(""); + print_ts_info(&fts); + + let manager = &manager; + + // 1️⃣ Construct the initial VTS. + let start = Instant::now(); + let vts = algorithms::annotation_tracking(&manager, &fts, &feature_model, true); + info!( + "Annotation tracking completed in {:.2}s.", + start.elapsed().as_secs_f32() + ); + + steps.push(StepInfo::new( + start.elapsed().as_millis().try_into().unwrap(), + ts_info(&fts), + ts_info(&vts), + )); + + println!("\n== Initial VTS =="); + print_vts_info(&vts); + + if let Some(output_path) = &arguments.output_path { + if arguments.output_state_spaces { + ts::output::dot::write_to_file( + &vts, + "Initial VTS", + &output_path.join("1_initial_vts.dot"), + |_, state| { + format!( + "({}, {})", + fts.get_label(&state.control), + bdd_to_formula(&atoms, &state.verdict) + ) + }, + |transition| { + let mut attrs = + TransitionAttributes::new(transition.action().as_deref().unwrap_or("τ")); + let target = vts.get_label(&transition.target()); + let source = vts.get_label(&transition.source()); + if !target.verdict.is_at_most(&source.verdict) { + attrs = attrs.with_color("red".to_owned()); + } + attrs + }, + ) + .unwrap(); + } + } + + // 2️⃣ Refine the beliefs by lookahead propagation. + let vts = if !arguments.with_lookahead_refinement { + vts + } else { + let new_vts = algorithms::lookahead_refinement(&vts); + drop(vts); + let vts = new_vts; + println!("\n== Refined VTS =="); + print_vts_info(&vts); + + if let Some(output_path) = &arguments.output_path { + if arguments.output_state_spaces { + ts::output::dot::write_to_file( + &vts, + "Refined VTS", + &output_path.join("2_refined_vts.dot"), + |_, state| { + format!( + "{}", + // "(q = {}, B = {})", + //fts.get_state(&state.control), + bdd_to_formula(&atoms, &state.verdict) + ) + }, + |transition| { + let mut attrs = TransitionAttributes::new( + transition.action().as_deref().unwrap_or("τ"), + ); + let target = vts.get_label(&transition.target()); + let source = vts.get_label(&transition.source()); + if !target.verdict.is_at_most(&source.verdict) { + attrs = attrs.with_color("red".to_owned()); + } + attrs + }, + ) + .unwrap(); + } + } + vts + }; + + let has_unobservable_transitions = vts.transitions().any(|transition| { + transition + .action() + .as_ref() + .map(|action| !observables.contains(action)) + .unwrap_or(true) + }); + + let vts = if has_unobservable_transitions { + // 3️⃣ Remove unobservables. + println!("\n== Observability Projection =="); + let projection = algorithms::observability_projection(&vts, |action| { + action + .as_ref() + .map(|action| observables.contains(action)) + .unwrap_or(false) + }); + print_ts_info(&projection); + + if let Some(output_path) = &arguments.output_path { + if arguments.output_state_spaces { + ts::output::dot::write_to_file( + &projection, + "Observability Projection", + &output_path.join("3_observability_projection.dot"), + |_, state| { + state + .iter() + .map(|id| { + let state = vts.get_label(&id); + bdd_to_formula(&atoms, &state.verdict).to_string() + // state + // .verdict + // .iter() + // .map(|bdd| { + // format!( + // "{}", + // // fts.get_label(&state.control), + // bdd_to_formula(&atoms, bdd) + // ) + // }) + // .collect::>() + // .join(", ") + }) + .collect::>() + .join(", ") + }, + |transition| { + TransitionAttributes::new(transition.action().as_deref().unwrap_or("τ")) + }, + ) + .unwrap(); + } + } + + // 5️⃣ Flatten beliefs. + println!("\n== Flatten Verdicts =="); + let new_vts = algorithms::join_verdicts(&vts, &projection, &manager.one()); // &power_set.top()); + drop(vts); + let vts = new_vts; + print_vts_info(&vts); + + if let Some(output_path) = &arguments.output_path { + if arguments.output_state_spaces { + ts::output::dot::write_to_file( + &vts, + "Flattened Verdicts", + &output_path.join("3_2_flattened_beliefs.dot"), + |_, state| { + bdd_to_formula(&atoms, &state.verdict).to_string() + // state + // .verdict + // .iter() + // .map(|bdd| { + // format!( + // "{}", + // // fts.get_label(&state.control), + // bdd_to_formula(&atoms, bdd) + // ) + // }) + // .collect::>() + // .join(", ") + }, + |transition| { + TransitionAttributes::new(transition.action().as_deref().unwrap_or("τ")) + }, + ) + .unwrap(); + ts::output::dot::write_to_file( + &vts, + "Flattened Verdicts", + &output_path.join("3_2_flattened_beliefs_count.dot"), + |_, state| { + bdd_to_formula(&atoms, &state.verdict).to_string() + // state + // .verdict + // .iter() + // .map(|bdd| bdd.count_solutions().to_string()) + // .collect::>() + // .join(", ") + }, + |transition| { + TransitionAttributes::new(transition.action().as_deref().unwrap_or("τ")) + }, + ) + .unwrap(); + } + } + vts + } else { + println!("VTS does not contain unobservable transitions!"); + vts.map_states(|id, state| { + let mut control = vts.make_sparse_state_set(); + control.insert(id); + VtsState { + control, + verdict: state.verdict.clone(), + } + }) + }; + // else { + // let power_set = PowerSet::new(); + + // vts.map_states(|x| VtsState::new(x.control, power_set.singleton(x.verdict.clone()))) + // }; + + let is_deterministic = is_deterministic(&vts); + + let vts = if !is_deterministic { + // 4️⃣ Determinize. + println!("\n== Determinize =="); + let determinized = algorithms::determinize(&vts); + print_ts_info(&determinized); + + if let Some(output_path) = &arguments.output_path { + if arguments.output_state_spaces { + ts::output::dot::write_to_file( + &determinized, + "Determinized", + &output_path.join("4_determinized.dot"), + |_, state| { + state + .iter() + .map(|id| { + let state = vts.get_label(&id); + bdd_to_formula(&atoms, &state.verdict).to_string() + // state + // .verdict + // .iter() + // .map(|bdd| { + // format!( + // "{}", + // // fts.get_label(&state.control), + // bdd_to_formula(&atoms, bdd) + // ) + // }) + // .collect::>() + // .join(", ") + }) + .collect::>() + .join(", ") + }, + |transition| { + TransitionAttributes::new(transition.action().as_deref().unwrap_or("τ")) + }, + ) + .unwrap(); + } + } + + // 5️⃣ Flatten beliefs. + println!("\n== Flatten Verdicts =="); + let new_vts = algorithms::join_verdicts(&vts, &determinized, &manager.one()); //&power_set.top()); + drop(vts); + let vts = new_vts; + print_vts_info(&vts); + + if let Some(output_path) = &arguments.output_path { + if arguments.output_state_spaces { + ts::output::dot::write_to_file( + &vts, + "Flattened Verdicts", + &output_path.join("5_flattened_beliefs.dot"), + |_, state| { + bdd_to_formula(&atoms, &state.verdict).to_string() + // state + // .verdict + // .iter() + // .map(|bdd| { + // format!( + // "{}", + // // fts.get_label(&state.control), + // bdd_to_formula(&atoms, bdd) + // ) + // }) + // .collect::>() + // .join(", ") + }, + |transition| { + TransitionAttributes::new(transition.action().as_deref().unwrap_or("τ")) + }, + ) + .unwrap(); + ts::output::dot::write_to_file( + &vts, + "Flattened Verdicts", + &output_path.join("5_flattened_beliefs_count.dot"), + |_, state| { + bdd_to_formula(&atoms, &state.verdict).to_string() + // state + // .verdict + // .iter() + // .map(|bdd| bdd.count_solutions().to_string()) + // .collect::>() + // .join(", ") + }, + |transition| { + TransitionAttributes::new(transition.action().as_deref().unwrap_or("τ")) + }, + ) + .unwrap(); + } + } + vts + } else { + println!("VTS is already deterministic!"); + vts.map_states(|id, state| { + let mut control = vts.make_sparse_state_set(); + control.insert(id); + VtsState { + control, + verdict: state.verdict.clone(), + } + }) + }; + + // 5️⃣ Minimize. + println!("\n== Minimize =="); + let start = Instant::now(); + let old_vts = vts; + print_vts_info(&old_vts); + type Minimize<'cx, Q, V, A> = algorithms::minimize::MinimizeFast<'cx, Q, V, A>; + // type Minimize<'cx, Q, V, A> = algorithms::Minimize<'cx, Q, V, A>; + let vts = Minimize::new(&old_vts) + .with_language_insensitive(arguments.minimization.relax_language) + .run(); + print_vts_info(&vts); + + steps.push(StepInfo::new( + start.elapsed().as_millis().try_into().unwrap(), + ts_info(&old_vts), + ts_info(&vts), + )); + + if let Some(output_path) = &arguments.output_path { + if arguments.output_state_spaces { + ts::output::dot::write_to_file( + &vts, + "Minimized", + &output_path.join("6_minimized.dot"), + |_, state| { + bdd_to_formula(&atoms, &state.verdict).to_string() + // state + // .verdict + // .iter() + // .map(|bdd| { + // format!( + // "{}", + // // fts.get_label(&state.control), + // bdd_to_formula(&atoms, bdd) + // ) + // }) + // .collect::>() + // .join(", ") + }, + |transition| { + TransitionAttributes::new(transition.action().as_deref().unwrap_or("τ")) + }, + ) + .unwrap(); + ts::output::dot::write_to_file( + &vts, + "Minimized", + &output_path.join("6_minimized_count.dot"), + |state_id, state| { + if !is_state_deterministic(&vts, state_id) { + "NON DET!!!".to_owned() + } else { + state.verdict.count_solutions().to_string() + } + // state + // .verdict + // .iter() + // .map(|bdd| bdd.count_solutions().to_string()) + // .collect::>() + // .join(", ") + }, + |transition| { + TransitionAttributes::new(transition.action().as_deref().unwrap_or("τ")) + }, + ) + .unwrap(); + } + + let mut data = HashMap::new(); + + data.insert( + "numConfigs".to_owned(), + (feature_model.count_solutions() as u64).into(), + ); + + data.insert("numActions".to_owned(), actions.len().into()); + + data.insert( + "minConfigs".to_owned(), + vts.states() + .map(|state| vts.get_label(&state).verdict.count_solutions() as u64) + .min() + .into(), + ); + + let report = Report { + steps, + data, + output: ts_info(&vts), + input: ts_info(&fts), + }; + + std::fs::write( + output_path.join("report.json"), + serde_json::to_string_pretty(&report).unwrap(), + ) + .unwrap(); + } + + let monitor = vts; + + if !arguments.simulate { + return; + } + + println!("\n== Simulation =="); + + let num_runs = 160_000; + let run_length = arguments.steps.unwrap_or(1_000); + + let mut monitor_last_step_sum = 0; + let mut config_sum = 0.0; + let mut num_runs_end = 0; + let mut run_length_end_sum = 0; + + let mut non_live_runs = 0; + + for run in 0..num_runs { + if run % 5_000 == 0 { + println!("Run {}.", run); + } + let config = feature_model.sample_solution().0; + // let mut enabled = HashSet::new(); + // for feature in &arguments.feature { + // config = config.and(&manager.var(features.get_index_of(feature).unwrap() as i32)); + // enabled.insert(feature); + // } + // for feature in &features { + // if !enabled.contains(feature) { + // config = config.and( + // &manager + // .var(features.get_index_of(feature).unwrap() as i32) + // .complement(), + // ); + // } + // } + + // println!("Enabled Features: {:?}", enabled); + // println!("Config: {}", bdd_to_formula(&atoms, &config)); + + let mut monitor_state = monitor.initial_states().next().unwrap(); + let mut sys_state = fts.initial_states().next().unwrap(); + + let mut monitor_last_step = 0; + let mut reached_final = false; + + let mut non_live = false; + + 'outer: for step in 0..run_length { + // println!("# Step: {step}"); + // println!( + // "Verdict: {}", + // bdd_to_formula(&atoms, &monitor.get_label(&monitor_state).verdict) + // ); + // println!( + // "Configurations: {}", + // monitor.get_label(&monitor_state).verdict.count_solutions() + // ); + + let enabled = fts + .outgoing(&sys_state) + .filter(|transition| !transition.action().guard.and(&config).is_zero()) + .collect::>(); + + let Some(transition) = enabled.choose(&mut rand::thread_rng()) else { + // println!("No outgoing transition! FTS is not live."); + non_live = true; + break; + }; + + if let Some(action) = &transition.action().action { + let is_observable = observables.contains(action); + // println!( + // "Action: {action} ({})", + // if is_observable { + // "observable" + // } else { + // "unobservable" + // } + // ); + + if is_observable { + // let mut found = false; + for monitor_transition in monitor.outgoing(&monitor_state) { + let monitor_label = monitor_transition.action(); + if monitor_label.as_ref() == Some(action) { + let target = monitor_transition.target(); + if target != monitor_state { + monitor_state = target; + monitor_last_step = step; + if monitor.outgoing(&monitor_state).next().is_none() { + // Monitor is in final state. Nothing will happen anymore. + reached_final = true; + break 'outer; + } + } + // found = true; + break; + } + } + // if !found { + // println!("Monitor transition not found!"); + // } + } + } + + sys_state = transition.target(); + } + + config_sum += monitor.get_label(&monitor_state).verdict.count_solutions(); + monitor_last_step_sum += monitor_last_step; + if reached_final { + num_runs_end += 1; + run_length_end_sum += monitor_last_step; + } + + if non_live { + non_live_runs += 1; + } + } + + // let config_count = feature_model.count_solutions(); + + println!("Quality: {}", config_sum / (num_runs as f64)); + + if let Some(output_path) = &arguments.output_path { + let quality_metrics = QualityMetrics { + num_runs, + non_live_runs: non_live_runs, + run_length: run_length as u64, + score: 1.0 + - (((config_sum) / (num_runs as f64) - 1.0) + / (feature_model.count_solutions() - 1.0)) + .max(0.0) + .min(1.0), + final_state: (monitor_last_step_sum as f64) / (num_runs as f64), + num_runs_end, + run_length_end: if num_runs_end > 0 { + (run_length_end_sum as f64) / (num_runs_end as f64) + } else { + 0.0 + }, + }; + std::fs::write( + output_path.join("quality-metrics.json"), + serde_json::to_string_pretty(&quality_metrics).unwrap(), + ) + .unwrap(); + } +} diff --git a/engine/crates/vts-synth/src/synthesis/mod.rs b/engine/crates/vts-synth/src/synthesis/mod.rs new file mode 100644 index 00000000..2394352c --- /dev/null +++ b/engine/crates/vts-synth/src/synthesis/mod.rs @@ -0,0 +1,3 @@ +//! Synthesis pipeline. + +pub mod confmon; diff --git a/engine/crates/vts-synth/src/ts.rs b/engine/crates/vts-synth/src/ts.rs new file mode 100644 index 00000000..cc10cbd8 --- /dev/null +++ b/engine/crates/vts-synth/src/ts.rs @@ -0,0 +1,819 @@ +//! Efficient representation of explicit-state transition systems. + +use std::{ + fmt::Debug, + hash::{BuildHasher, Hash, Hasher}, + ops::{Index, Range}, + sync::atomic::{self}, +}; + +use bit_set::BitSet; +use hashbrown::{hash_map::DefaultHashBuilder, raw::RawTable, HashMap, HashSet}; + +use self::traits::*; + +pub mod algorithms; +pub mod output; +pub mod traits; +pub mod transposed; +pub mod types; + +const TS_DATA_ID_SHIFT: u64 = 48; +const TS_DATA_ID_MASK: u64 = 0xFFFF; +const STATE_IDX_MAX: usize = ((1 << TS_DATA_ID_SHIFT) - 1) & (usize::MAX as u64) as usize; +const STATE_IDX_SHIFT: u64 = 0; +const STATE_IDX_MASK: u64 = !(TS_DATA_ID_MASK << TS_DATA_ID_SHIFT); + +/// Returns the ID for the next transition system. +fn next_ts_data_id() -> u16 { + static ID: atomic::AtomicU16 = atomic::AtomicU16::new(0); + ID.fetch_add(1, atomic::Ordering::Relaxed) +} + +/// Unique identifier of a state in a TS. +#[derive(Clone, Copy, PartialEq, Eq, Hash)] +pub struct StateId(u64); + +impl StateId { + pub const fn from_parts(ts_id: u16, idx: usize) -> Self { + debug_assert!(idx <= STATE_IDX_MAX as usize); + Self(((ts_id as u64) << TS_DATA_ID_SHIFT) | ((idx as u64) << STATE_IDX_SHIFT)) + } + + /// The index of the state in the state vector. + pub fn idx(self) -> usize { + ((self.0 >> STATE_IDX_SHIFT) & STATE_IDX_MASK) as usize + } + + /// The id of the transition system the state belongs to. + fn ts_id(self) -> u16 { + ((self.0 >> TS_DATA_ID_SHIFT) & TS_DATA_ID_MASK) as u16 + } +} + +impl Debug for StateId { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("StateId") + .field("ts_id", &self.ts_id()) + .field("idx", &self.idx()) + .finish() + } +} + +/// Unique identifier of a transition in a TS. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub struct TransitionId(pub(crate) usize); + +/// State with additional data stored in a TS. +#[derive(Debug, Clone)] +struct StateEntry { + /// The actual data of the state. + state: S, + /// Range into the outgoing transitions vector. + outgoing: Range, + /// Range into the reverse edges vector. + reverse: Range, +} + +impl StateEntry { + /// Creates a new state entry. + fn new(state: S) -> Self { + Self { + state, + outgoing: 0..0, + reverse: 0..0, + } + } +} + +#[derive(Debug, Clone)] +pub struct StateLabeling(Vec); + +impl std::ops::Index for StateLabeling { + type Output = L; + + fn index(&self, index: StateId) -> &Self::Output { + &self.0[index.idx()] + } +} + +impl std::ops::Index<&StateId> for StateLabeling { + type Output = L; + + fn index(&self, index: &StateId) -> &Self::Output { + &self.0[index.idx()] + } +} + +impl std::ops::IndexMut for StateLabeling { + fn index_mut(&mut self, index: StateId) -> &mut Self::Output { + &mut self.0[index.idx()] + } +} + +impl std::ops::IndexMut<&StateId> for StateLabeling { + fn index_mut(&mut self, index: &StateId) -> &mut Self::Output { + &mut self.0[index.idx()] + } +} + +/// A transition of a TS. +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct Transition { + /// The source state of the transition. + source: StateId, + /// The label of the transition. + label: L, + /// The target state of the transition. + target: StateId, +} + +impl Transition { + /// The source state of the transition. + pub fn source(&self) -> StateId { + self.source + } + + /// The label of the transition. + pub fn action(&self) -> &L { + &self.label + } + + /// The target state of the transition. + pub fn target(&self) -> StateId { + self.target + } +} + +/// The actual data of a TS. +#[derive(Debug, Clone)] +struct TsData { + /// The id of the TS. + ts_id: u16, + /// The states of the TS. + states: Vec>, + /// The initial states of the TS. + initial_states: HashSet, + /// The transitions. + transitions: Vec>, + /// The outgoing transition of the TS. + outgoing: Vec, + /// The reverse edges of the TS. + reverse: Vec, +} + +impl TsData { + fn new() -> Self { + Self { + ts_id: next_ts_data_id(), + states: Vec::new(), + initial_states: HashSet::new(), + transitions: Vec::new(), + outgoing: Vec::new(), + reverse: Vec::new(), + } + } + + /// Turns `self` into [`Ts`] by establishing the necessary invariants. + /// + /// **Complexity:** `O(n log n)` where `n` is the number of edges. + fn into_ts(mut self) -> Ts { + // 1️⃣ Sort the incoming and outgoing edges. + self.outgoing.sort_by(|x, y| { + self.transitions[x.0] + .source + .idx() + .cmp(&self.transitions[y.0].source.idx()) + }); + self.reverse.sort_by(|x, y| { + self.transitions[x.0] + .target + .idx() + .cmp(&self.transitions[y.0].target.idx()) + }); + + // println!("{:?}", self.outgoing); + // println!("{:?}", self.reverse); + + // 2️⃣ Update/initializes the ranges stored with the states. + let mut last_outgoing = 0; + let mut last_reverse = 0; + for (id, state) in self.states.iter_mut().enumerate() { + // Update outgoing range. + let start_outgoing = last_outgoing; + while last_outgoing < self.outgoing.len() + && self.transitions[self.outgoing[last_outgoing].0] + .source + .idx() + == id + { + last_outgoing += 1; + } + state.outgoing = start_outgoing..last_outgoing; + // Update incoming range. + let start_reverse = last_reverse; + while last_reverse < self.reverse.len() + && self.transitions[self.reverse[last_reverse].0].target.idx() == id + { + last_reverse += 1; + } + state.reverse = start_reverse..last_reverse; + // Sanity check the updated ranges. + debug_assert!(self.outgoing[state.outgoing.clone()].iter().all(|edge| self + .transitions[edge.0] + .source() + .idx() + == id)); + debug_assert!(self.reverse[state.reverse.clone()] + .iter() + .all(|edge| self.transitions[edge.0].target().idx() == id)); + } + + // Check that all edges have been considered. + assert_eq!(last_outgoing, self.outgoing.len()); + assert_eq!(last_reverse, self.reverse.len()); + + let ts = Ts { + data: self, + empty_state_set: im::HashSet::new(), + }; + + ts.assert_invariants(); + + ts + } +} + +impl Index for TsData { + type Output = StateEntry; + + fn index(&self, index: StateId) -> &Self::Output { + debug_assert!( + index.ts_id() == self.ts_id, + "State does not belong to this TS (state TS = {}, actual TS = {}).", + index.ts_id(), + self.ts_id, + ); + &self.states[index.idx()] + } +} + +/// An explicit-state TS. +#[derive(Debug, Clone)] +pub struct Ts { + /// The data of the TS. + data: TsData, + /// Empty sparse state set. + /// + /// This is needed because we want all sparse state sets with the same states to have + /// the same hash. This means that the same hasher has to be used, i.e., they all have + /// to be constructed from the same empty [`im::HashSet`]. + empty_state_set: im::HashSet, +} + +impl Ts { + /// The outgoing transitions of a state. + pub fn outgoing(&self, state: &StateId) -> impl '_ + Iterator> { + self.data.outgoing[self.data[*state].outgoing.clone()] + .iter() + .map(|transition| &self.data.transitions[transition.0]) + } + + /// The incoming transitions of a satte. + pub fn incoming(&self, state: &StateId) -> impl '_ + Iterator> { + self.data.reverse[self.data[*state].reverse.clone()] + .iter() + .map(|transition| &self.data.transitions[transition.0]) + } + + /// The incoming transitions of a satte. + pub fn incoming_ids(&self, state: &StateId) -> impl '_ + Iterator { + self.data.reverse[self.data[*state].reverse.clone()] + .iter() + .map(|transition| *transition) + } + + pub fn get_transition(&self, id: TransitionId) -> &Transition { + &self.data.transitions[id.0] + } + + /// Applies a function to the labels of the TS. + pub fn map_labels(&self, mut fun: F) -> Ts + where + F: FnMut(&L) -> U, + S: Clone, + { + Ts { + data: TsData { + ts_id: self.data.ts_id, + states: self.data.states.clone(), + initial_states: self.data.initial_states.clone(), + transitions: self + .data + .transitions + .iter() + .map(|transition| Transition { + source: transition.source, + label: fun(&transition.label), + target: transition.target, + }) + .collect(), + outgoing: self.data.outgoing.clone(), + reverse: self.data.reverse.clone(), + }, + empty_state_set: im::HashSet::new(), + } + } + + /// Applies a function to the states of the TS. + pub fn map_states(&self, mut fun: F) -> Ts + where + F: FnMut(StateId, &S) -> U, + L: Clone, + { + Ts { + data: TsData { + ts_id: self.data.ts_id, + states: self + .data + .states + .iter() + .enumerate() + .map(|(idx, entry)| StateEntry { + state: fun(StateId::from_parts(self.data.ts_id, idx), &entry.state), + outgoing: entry.outgoing.clone(), + reverse: entry.reverse.clone(), + }) + .collect(), + initial_states: self.data.initial_states.clone(), + transitions: self.data.transitions.clone(), + outgoing: self.data.outgoing.clone(), + reverse: self.data.reverse.clone(), + }, + empty_state_set: im::HashSet::new(), + } + } + + pub fn create_state_labeling( + &self, + mut default: impl FnMut(StateId) -> L2, + ) -> StateLabeling { + let mut labels = Vec::with_capacity(self.num_states()); + for id in self.states() { + labels.push(default(id)); + } + StateLabeling(labels) + } + + pub fn create_default_state_labeling(&self) -> StateLabeling + where + L2: Default, + { + self.create_state_labeling(|_| L2::default()) + } + + pub fn assert_invariants(&self) { + for state in self.states() { + for transition in self.outgoing(&state) { + assert_eq!(transition.source(), state); + } + for transition in self.incoming(&state) { + assert_eq!(transition.target(), state); + } + } + } +} + +impl BaseTs for Ts { + type StateId = StateId; + + type State = S; + + fn get_label(&self, id: &Self::StateId) -> &Self::State { + &self.data[*id].state + } +} + +impl States for Ts { + type StatesIter<'iter> = impl 'iter + Iterator + where + Self: 'iter; + + fn states(&self) -> Self::StatesIter<'_> { + (0..self.num_states()).map(|idx| StateId::from_parts(self.data.ts_id, idx)) + } + + fn num_states(&self) -> usize { + self.data.states.len() + } +} + +impl InitialStates for Ts { + type InitialStatesIter<'iter> = impl 'iter + Iterator + where + Self: 'iter; + + fn initial_states(&self) -> Self::InitialStatesIter<'_> { + self.data.initial_states.iter().cloned() + } + + fn is_initial(&self, state: &Self::StateId) -> bool { + self.data.initial_states.contains(state) + } + + fn num_initial_states(&self) -> usize { + self.data.initial_states.len() + } +} + +impl Transitions for Ts { + type Transition<'trans> = &'trans Transition + where + Self: 'trans; + + type TransitionsIter<'iter> = impl 'iter + Iterator> + where + Self: 'iter; + + fn transitions(&self) -> Self::TransitionsIter<'_> { + self.data.transitions.iter() + } + + fn num_transitions(&self) -> usize { + self.data.transitions.len() + } +} + +impl Successors for Ts { + type SuccessorsIter<'iter> = impl 'iter + Iterator + where + Self: 'iter; + + fn successors(&self, state: &Self::StateId) -> Self::SuccessorsIter<'_> { + self.data.outgoing[self.data[*state].outgoing.clone()] + .iter() + .map(|transition| self.data.transitions[transition.0].target()) + } +} + +impl Predecessors for Ts { + type PredecessorsIter<'iter> = impl 'iter + Iterator + where + Self: 'iter; + + fn predecessors(&self, state: &Self::StateId) -> Self::PredecessorsIter<'_> { + self.data.reverse[self.data[*state].reverse.clone()] + .iter() + .map(|transition| self.data.transitions[transition.0].source()) + } +} + +impl MakeDenseStateSet for Ts { + type DenseStateSet = DenseStateSet; + + fn make_dense_state_set(&self) -> Self::DenseStateSet { + DenseStateSet { + ts_id: self.data.ts_id, + set: BitSet::with_capacity(self.data.states.len()), + } + } +} + +impl MakeSparseStateSet for Ts { + type SparseStateSet = SparseStateSet; + + fn make_sparse_state_set(&self) -> Self::SparseStateSet { + SparseStateSet(self.empty_state_set.clone()) + } +} + +impl MakeDenseStateMap for Ts { + type DenseStateMap = DenseStateMap; + + fn make_dense_state_map(&self) -> Self::DenseStateMap { + let mut entries = Vec::with_capacity(self.data.states.len()); + for _ in 0..self.data.states.len() { + entries.push(DenseMapEntry::Vacant); + } + DenseStateMap { + ts_id: self.data.ts_id, + entries, + } + } +} + +impl MakeSparseStateMap for Ts { + type SparseStateMap = SparseStateMap; + + fn make_sparse_state_map(&self) -> Self::SparseStateMap { + SparseStateMap(HashMap::new()) + } +} + +/// A dense set of states of a TS. +#[derive(Debug, Clone, Hash, PartialEq, Eq)] +pub struct DenseStateSet { + ts_id: u16, + set: BitSet, +} + +impl StateSet for DenseStateSet { + #[inline(always)] + fn insert(&mut self, state: StateId) -> bool { + debug_assert!(self.ts_id == state.ts_id()); + self.set.insert(state.idx()) + } + + #[inline(always)] + fn remove(&mut self, state: &StateId) -> bool { + debug_assert!(self.ts_id == state.ts_id()); + self.set.remove(state.idx()) + } + + #[inline(always)] + fn contains(&self, state: &StateId) -> bool { + debug_assert!(self.ts_id == state.ts_id()); + self.set.contains(state.idx()) + } + + #[inline(always)] + fn clear(&mut self) { + self.set.clear() + } + + type Iter<'iter> = impl 'iter + Iterator + where + Self: 'iter; + + fn iter(&self) -> Self::Iter<'_> { + self.set + .iter() + .map(|idx| StateId::from_parts(self.ts_id, idx)) + } +} + +/// A sparse set of states of a TS. +#[derive(Debug, Clone, Hash, PartialEq, Eq)] +pub struct SparseStateSet(im::HashSet); + +impl StateSet for SparseStateSet { + #[inline(always)] + fn insert(&mut self, state: StateId) -> bool { + self.0.insert(state).is_none() + } + + fn remove(&mut self, state: &StateId) -> bool { + self.0.remove(state).is_some() + } + + fn contains(&self, state: &StateId) -> bool { + self.0.contains(state) + } + + fn clear(&mut self) { + self.0.clear() + } + + type Iter<'iter> = impl 'iter + Iterator + where + Self: 'iter; + + fn iter(&self) -> Self::Iter<'_> { + self.0.iter().cloned() + } +} + +/// An entry of a dense state map. +#[derive(Debug, Clone)] +enum DenseMapEntry { + Occupied(V), + Vacant, +} + +/// A dense map from states to values of type `V`. +#[derive(Debug, Clone)] +pub struct DenseStateMap { + ts_id: u16, + entries: Vec>, +} + +impl StateMap for DenseStateMap { + fn insert(&mut self, state: StateId, value: V) { + debug_assert!(state.ts_id() == self.ts_id); + self.entries[state.idx()] = DenseMapEntry::Occupied(value); + } + + fn remove(&mut self, state: &StateId) -> Option { + debug_assert!(state.ts_id() == self.ts_id); + let mut value = DenseMapEntry::Vacant; + std::mem::swap(&mut value, &mut self.entries[state.idx()]); + match value { + DenseMapEntry::Occupied(value) => Some(value), + DenseMapEntry::Vacant => None, + } + } + + fn get(&self, state: &StateId) -> Option<&V> { + debug_assert!(state.ts_id() == self.ts_id); + match &self.entries[state.idx()] { + DenseMapEntry::Occupied(value) => Some(value), + DenseMapEntry::Vacant => None, + } + } + + fn contains(&self, state: &StateId) -> bool { + debug_assert!(state.ts_id() == self.ts_id); + matches!(self.entries[state.idx()], DenseMapEntry::Occupied(_)) + } + + fn get_mut(&mut self, state: &StateId) -> Option<&mut V> { + match &mut self.entries[state.idx()] { + DenseMapEntry::Occupied(value) => Some(value), + DenseMapEntry::Vacant => None, + } + } +} + +/// A sparse map from states to values of type `V`. +#[derive(Debug, Clone)] +pub struct SparseStateMap(HashMap); + +impl StateMap for SparseStateMap { + fn insert(&mut self, state: StateId, value: V) { + self.0.insert(state, value); + } + + fn remove(&mut self, state: &StateId) -> Option { + self.0.remove(state) + } + + fn get(&self, state: &StateId) -> Option<&V> { + self.0.get(state) + } + + fn contains(&self, state: &StateId) -> bool { + self.0.contains_key(state) + } + + fn get_mut(&mut self, state: &StateId) -> Option<&mut V> { + self.0.get_mut(state) + } +} + +/// Computes the hash of a value. +fn hash_value(value: &V, builder: &HB) -> u64 { + let mut hasher = builder.build_hasher(); + value.hash(&mut hasher); + hasher.finish() +} + +/// A builder for transition systems. +#[derive(Clone)] +pub struct TsBuilder { + /// The data of the transition. + data: TsData, + /// Table used for deduplication of states. + state_table: RawTable, + /// Table used for deduplication of transitions. + transition_table: RawTable, + /// The hash builder for deduplication. + hash_builder: DefaultHashBuilder, + + insert_called: usize, +} + +impl Debug for TsBuilder { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("TsBuilder") + .field("data", &self.data) + .finish_non_exhaustive() + } +} + +impl TsBuilder { + /// Creates a new builder. + pub fn new() -> Self { + Self { + data: TsData::new(), + state_table: RawTable::new(), + transition_table: RawTable::new(), + hash_builder: DefaultHashBuilder::default(), + insert_called: 0, + } + } + + /// Mark a state as initial state. + pub fn mark_initial(&mut self, state: StateId) { + self.data.initial_states.insert(state); + } + + /// Looks up the id of the state in the table. + pub fn lookup_state(&self, state: &S) -> Option + where + S: Eq + Hash, + { + let hash = hash_value(state, &self.hash_builder); + self.state_table + .get(hash, |id| &self.data[*id].state == state) + .cloned() + } + + /// Inserts a state into the transition system. + pub fn insert_state(&mut self, state: S) -> StateId + where + S: Eq + Hash, + { + let hash = hash_value(&state, &self.hash_builder); + let id = self + .state_table + .get(hash, |id| self.data[*id].state == state) + .cloned(); + + match id { + Some(id) => id, + None => { + let id = StateId::from_parts(self.data.ts_id, self.data.states.len()); + self.data.states.push(StateEntry::new(state)); + self.state_table.insert(hash, id, |id| { + hash_value(&self.data[*id].state, &self.hash_builder) + }); + id + } + } + } + + /// Inserts a transition into the transition system. + pub fn insert_transition(&mut self, source: StateId, label: L, target: StateId) + where + L: Eq + Hash, + { + let transition = Transition { + source, + label, + target, + }; + self.insert_called += 1; + let hash = hash_value(&transition, &self.hash_builder); + let id = self + .transition_table + .get(hash, |id| self.data.transitions[id.0] == transition) + .cloned(); + if id.is_none() { + let id = TransitionId(self.data.transitions.len()); + self.data.transitions.push(transition); + self.data.outgoing.push(id); + self.data.reverse.push(id); + self.transition_table.insert(hash, id, |id| { + hash_value(&self.data.transitions[id.0], &self.hash_builder) + }); + } + } + + /// Builds the TS. + pub fn build(self) -> Ts { + println!("Insert transition called {} times.", self.insert_called); + self.data.into_ts() + } +} + +impl BaseTs for TsBuilder { + type StateId = StateId; + + type State = S; + + fn get_label(&self, id: &Self::StateId) -> &Self::State { + &self.data[*id].state + } +} + +impl States for TsBuilder { + type StatesIter<'iter> = impl 'iter + Iterator + where + Self: 'iter; + + fn states(&self) -> Self::StatesIter<'_> { + (0..self.num_states()).map(|idx| StateId::from_parts(self.data.ts_id, idx)) + } + + fn num_states(&self) -> usize { + self.data.states.len() + } +} + +impl InitialStates for TsBuilder { + type InitialStatesIter<'iter> = impl 'iter + Iterator + where + Self: 'iter; + + fn initial_states(&self) -> Self::InitialStatesIter<'_> { + self.data.initial_states.iter().cloned() + } + + fn is_initial(&self, state: &Self::StateId) -> bool { + self.data.initial_states.contains(state) + } + + fn num_initial_states(&self) -> usize { + self.data.initial_states.len() + } +} diff --git a/engine/crates/vts-synth/src/ts/algorithms.rs b/engine/crates/vts-synth/src/ts/algorithms.rs new file mode 100644 index 00000000..3a4e03b2 --- /dev/null +++ b/engine/crates/vts-synth/src/ts/algorithms.rs @@ -0,0 +1,39 @@ +//! General algorithms for transition systems. + +use super::{traits::*, transposed::Transposed}; + +pub mod bfs; +pub mod dfs; + +/// Computes the _strongly-connected components_ (SCCs) of a TS. +/// +/// Implements [Kosaraju's algorithm](https://en.wikipedia.org/wiki/Kosaraju%27s_algorithm). +pub fn scc( + ts: TS, +) -> impl Iterator> { + let mut stack = Vec::new(); + + let mut dfs = dfs::Dfs::empty(ts); + for state in ts.states() { + if dfs.push(state) { + for state in dfs.iter_post_order() { + stack.push(state); + } + } + } + + let mut dfs = dfs::Dfs::empty(Transposed::new(ts)); + + std::iter::from_fn(move || { + while let Some(state) = stack.pop() { + if dfs.push(state.clone()) { + let mut scc = vec![state]; + for state in dfs.iter_pre_order() { + scc.push(state); + } + return Some(scc); + } + } + None + }) +} diff --git a/engine/crates/vts-synth/src/ts/algorithms/bfs.rs b/engine/crates/vts-synth/src/ts/algorithms/bfs.rs new file mode 100644 index 00000000..1feffe47 --- /dev/null +++ b/engine/crates/vts-synth/src/ts/algorithms/bfs.rs @@ -0,0 +1,97 @@ +//! Breadth-first search. +//! +//! 🛠️ Can we share code between DFS and BFS? + +use std::collections::VecDeque; + +use crate::ts::traits::*; + +/// An TS supporting breadth-first search. +pub trait SupportsBfs: TsRef + MakeDenseStateSet + Successors {} + +impl SupportsBfs for TS {} + +/// State a breadth-first search. +pub struct Bfs { + ts: TS, + visited: TS::DenseStateSet, + queue: VecDeque, +} + +impl Bfs { + /// Initiates a new breadth-first search starting from the initial states. + pub fn new(ts: TS) -> Self + where + TS: InitialStates, + { + Self::new_with(ts, ts.initial_states()) + } + + /// Initiates a new breadth-first search starting from the provided states. + pub fn new_with(ts: TS, start: I) -> Self + where + I: IntoIterator, + { + Self { + ts, + visited: ts.make_dense_state_set(), + queue: start.into_iter().collect(), + } + } + + /// Initiates a new breadth-first search without any states. + pub fn empty(ts: TS) -> Self { + Self::new_with(ts, []) + } + + /// The TS over which the search is performed. + pub fn ts(&self) -> &TS { + &self.ts + } + + /// Pushes a state on the BFS queue. + pub fn push(&mut self, state: TS::StateId) -> bool { + if self.visited.insert(state.clone()) { + self.queue.push_back(state); + true + } else { + false + } + } + + /// Indicates whether the given state has been visited. + pub fn has_been_visited(&self, state: &TS::StateId) -> bool { + self.visited.contains(&state) + } + + /// Indicates whether the given state is queued. + pub fn is_queued(&self, state: &TS::StateId) -> bool { + self.queue.iter().any(|item| item == state) + } + + /// Indicates whether the given state has been finalized. + pub fn has_been_finalized(&self, state: &TS::StateId) -> bool { + self.has_been_visited(state) && !self.is_queued(state) + } + + /// Indicates whether the queue is empty. + pub fn is_queue_empty(&self) -> bool { + self.queue.is_empty() + } +} + +impl Iterator for Bfs { + type Item = TS::StateId; + + fn next(&mut self) -> Option { + if let Some(state) = self.queue.pop_front() { + let ts = self.ts; + for successor in ts.successors(&state) { + self.push(successor); + } + Some(state) + } else { + None + } + } +} diff --git a/engine/crates/vts-synth/src/ts/algorithms/dfs.rs b/engine/crates/vts-synth/src/ts/algorithms/dfs.rs new file mode 100644 index 00000000..e6cf8222 --- /dev/null +++ b/engine/crates/vts-synth/src/ts/algorithms/dfs.rs @@ -0,0 +1,150 @@ +//! Depth-first search. + +use crate::ts::traits::*; + +/// An TS supporting depth-first search. +pub trait SupportsDfs: TsRef + MakeDenseStateSet + Successors {} + +impl SupportsDfs for TS {} + +/// An item on the stack of a depth-first search. +#[derive(Debug, Clone)] +pub enum StackItem { + /// Visit the given state. + Visit(S), + /// Finalize the given state. + Finalize(S), +} + +impl StackItem { + /// The transition system state of the stack item. + pub fn state(&self) -> &S { + match self { + StackItem::Visit(state) | StackItem::Finalize(state) => state, + } + } +} + +/// State a depth-first search. +pub struct Dfs { + ts: TS, + visited: TS::DenseStateSet, + stack: Vec>, +} + +impl Dfs { + /// Initiates a new depth-first search starting from the initial states. + pub fn new(ts: TS) -> Self + where + TS: InitialStates, + { + Self::new_with(ts, ts.initial_states()) + } + + /// Initiates a new depth-first search starting from the provided states. + pub fn new_with(ts: TS, start: I) -> Self + where + I: IntoIterator, + { + Self { + ts, + visited: ts.make_dense_state_set(), + stack: start.into_iter().map(StackItem::Visit).collect(), + } + } + + /// Initiates a new depth-first search without any states. + pub fn empty(ts: TS) -> Self { + Self::new_with(ts, []) + } + + /// The TS over which the search is performed. + pub fn ts(&self) -> &TS { + &self.ts + } + + /// Pushes a state on the DFS stack. + pub fn push(&mut self, state: TS::StateId) -> bool { + if self.visited.contains(&state) { + false + } else { + self.stack.push(StackItem::Visit(state)); + true + } + } + + /// Indicates whether the given state has been visited. + pub fn has_been_visited(&self, state: &TS::StateId) -> bool { + self.visited.contains(&state) + } + + /// Indicates whether the given state is on the stack. + pub fn is_on_stack(&self, state: &TS::StateId) -> bool { + self.stack.iter().any(|item| item.state() == state) + } + + /// Indicates whether the stack is empty. + pub fn is_stack_empty(&self) -> bool { + self.stack.is_empty() + } + + /// Consumes a stack item and advances the search. + pub fn consume(&mut self) -> Option> { + if let Some(item) = self.stack.pop() { + if let StackItem::Visit(state) = &item { + if self.visited.insert(state.clone()) { + self.stack.push(StackItem::Finalize(state.clone())); + let ts = self.ts; + for successor in ts.successors(state) { + self.push(successor); + } + } + } + Some(item) + } else { + None + } + } + + /// Iterator over states in pre-order. + pub fn iter_pre_order(&mut self) -> PreOrderIter<'_, TS> { + PreOrderIter(self) + } + + /// Iterator over states in pre-order. + pub fn iter_post_order(&mut self) -> PostOrderIter<'_, TS> { + PostOrderIter(self) + } +} + +/// Iterator over states in DFS pre-order. +pub struct PreOrderIter<'dfs, TS: SupportsDfs>(&'dfs mut Dfs); + +impl<'dfs, TS: SupportsDfs> Iterator for PreOrderIter<'dfs, TS> { + type Item = TS::StateId; + + fn next(&mut self) -> Option { + while let Some(item) = self.0.consume() { + if let StackItem::Visit(state) = item { + return Some(state); + } + } + None + } +} + +/// Iterator over states in DFS post-order. +pub struct PostOrderIter<'dfs, TS: SupportsDfs>(&'dfs mut Dfs); + +impl<'dfs, TS: SupportsDfs> Iterator for PostOrderIter<'dfs, TS> { + type Item = TS::StateId; + + fn next(&mut self) -> Option { + while let Some(item) = self.0.consume() { + if let StackItem::Finalize(state) = item { + return Some(state); + } + } + None + } +} diff --git a/engine/crates/vts-synth/src/ts/output.rs b/engine/crates/vts-synth/src/ts/output.rs new file mode 100644 index 00000000..2cc4f6de --- /dev/null +++ b/engine/crates/vts-synth/src/ts/output.rs @@ -0,0 +1,4 @@ +//! Output formats for transition systems. + +pub mod dot; +pub mod pseuco; diff --git a/engine/crates/vts-synth/src/ts/output/dot.rs b/engine/crates/vts-synth/src/ts/output/dot.rs new file mode 100644 index 00000000..3932d303 --- /dev/null +++ b/engine/crates/vts-synth/src/ts/output/dot.rs @@ -0,0 +1,111 @@ +//! Output in [Graphviz's](https://graphviz.org) `dot` format. + +use std::{ + fs, + hash::Hash, + io::{self, Write}, + path::Path, +}; + +use hashbrown::HashMap; + +use crate::ts::{traits::*, StateId, Transition, Ts}; + +pub fn dump_ts( + ts: &Ts, + title: &str, + mut out: W, + state_label: impl Fn(StateId, &S) -> String, + transition_attrs: impl Fn(&Transition) -> TransitionAttributes, +) -> Result<(), io::Error> +where + S: Clone + Eq + Hash, + L: Clone + Eq + Hash, + W: Write, +{ + writeln!(out, "digraph Lts {{")?; + writeln!(out, " label=\"{}\"", title)?; + let mut state_ids = HashMap::new(); + for state in ts.states() { + let state_id = format!("s{}", state_ids.len()); + let is_initial = ts.is_initial(&state); + let shape = if is_initial { + ",shape=rect" + } else { + ",shape=ellipse" + }; + writeln!( + out, + " {}[label=\"{}\" {}]", + state_id, + state_label(state, ts.get_label(&state)), + shape + )?; + state_ids.insert(state, state_id); + } + for transition in ts.transitions() { + let source_id = state_ids.get(&transition.source).unwrap(); + let target_id = state_ids.get(&transition.target).unwrap(); + writeln!( + out, + " {} -> {} [{}]", + source_id, + target_id, + transition_attrs(transition).to_string() + )?; + } + // for source in ts.states() { + // for target in ts.predecessors(&source) { + // let source_id = state_ids.get(&source).unwrap(); + // let target_id = state_ids.get(&target).unwrap(); + // writeln!(out, " {} -> {} [color=red]", source_id, target_id,)?; + // } + // } + writeln!(out, "}}")?; + Ok(()) +} + +pub struct TransitionAttributes(HashMap); + +impl TransitionAttributes { + pub fn new(label: &str) -> Self { + Self(HashMap::from([( + "label".to_owned(), + format!("\"{}\"", label), + )])) + } + + pub fn with_color(mut self, color: String) -> Self { + self.0.insert("color".to_owned(), color); + self + } + + pub fn to_string(&self) -> String { + self.0 + .iter() + .map(|(key, value)| format!("{}={}", key, value)) + .collect::>() + .join(",") + } +} + +pub fn write_to_file( + lts: &Ts, + title: &str, + path: &Path, + state_label: impl Fn(StateId, &S) -> String, + transition_attrs: impl Fn(&Transition) -> TransitionAttributes, +) -> Result<(), io::Error> +where + S: Clone + Eq + Hash, + L: Clone + Eq + Hash, +{ + let out = fs::File::create(path)?; + dump_ts( + lts, + title, + io::BufWriter::new(out), + state_label, + transition_attrs, + ) +} diff --git a/engine/crates/vts-synth/src/ts/output/pseuco.rs b/engine/crates/vts-synth/src/ts/output/pseuco.rs new file mode 100644 index 00000000..d5fc787a --- /dev/null +++ b/engine/crates/vts-synth/src/ts/output/pseuco.rs @@ -0,0 +1,156 @@ +//! Output in [Pseuco's](https://pseuco.com) LTS JSON format. + +use std::{collections::HashMap, fs, hash::Hash, io, path::Path}; + +use serde::{de, Deserialize, Serialize}; + +use crate::ts::{ + traits::{BaseTs, InitialStates, States}, + Ts, +}; + +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct State { + #[serde(default, skip_serializing_if = "Vec::is_empty")] + pub transitions: Vec, + #[serde(skip_serializing_if = "Option::is_none")] + pub error: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub highlighted: Option, +} +#[derive(Serialize, Deserialize, Debug, Clone)] +#[serde(rename_all = "camelCase")] +pub struct Transition { + pub label: String, + #[serde(skip_serializing_if = "Option::is_none")] + pub weak: Option, + pub details_label: DetailsLabel, + pub target: String, +} + +#[derive(Debug, Clone)] +pub struct DetailsLabel(pub Option); + +impl Serialize for DetailsLabel { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + if let Some(label) = &self.0 { + serializer.serialize_str(label) + } else { + serializer.serialize_bool(false) + } + } +} + +impl<'de> Deserialize<'de> for DetailsLabel { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + struct Visitor; + + impl<'de> de::Visitor<'de> for Visitor { + type Value = DetailsLabel; + + fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { + formatter.write_str("Expected a string or `false`.") + } + + fn visit_bool(self, v: bool) -> Result + where + E: de::Error, + { + if !v { + Ok(DetailsLabel(None)) + } else { + Err(de::Error::invalid_type(de::Unexpected::Bool(v), &self)) + } + } + + fn visit_str(self, v: &str) -> Result + where + E: de::Error, + { + Ok(DetailsLabel(Some(v.to_owned()))) + } + } + + deserializer.deserialize_any(Visitor) + } +} + +#[derive(Serialize, Deserialize, Debug, Clone)] +#[serde(rename_all = "camelCase")] +pub struct PseucoLts { + initial_state: String, + states: HashMap, +} + +pub fn to_pseuco_lts( + ts: &Ts, + state_label: impl Fn(&S) -> String, + transition_label: impl Fn(&L) -> String, +) -> PseucoLts +where + S: Clone + Eq + Hash, + L: Clone + Eq + Hash, +{ + let mut state_ids = HashMap::new(); + for state in ts.states() { + let state_id = format!( + "{} ({})", + state_label(ts.get_label(&state)), + state_ids.len() + ); + state_ids.insert(state, state_id); + } + + let initial_state = ts + .initial_states() + .map(|initial_state| state_ids.get(&initial_state)) + .next() + .unwrap() + .unwrap() + .clone(); + + PseucoLts { + initial_state, + states: ts + .states() + .map(|state| { + ( + state_ids.get(&state).unwrap().clone(), + State { + transitions: ts + .outgoing(&state) + .map(|transition| Transition { + label: transition_label(&transition.label), + weak: Some(false), + details_label: DetailsLabel(None), + target: state_ids.get(&transition.target).unwrap().clone(), + }) + .collect(), + error: None, + highlighted: None, + }, + ) + }) + .collect(), + } +} + +pub fn write_to_file( + lts: &Ts, + path: &Path, + state_label: impl Fn(&S) -> String, + transition_label: impl Fn(&L) -> String, +) -> Result<(), io::Error> +where + S: Clone + Eq + Hash, + L: Clone + Eq + Hash, +{ + let src = serde_json::to_string(&to_pseuco_lts(lts, state_label, transition_label)).unwrap(); + fs::write(path, &src) +} diff --git a/engine/crates/vts-synth/src/ts/traits.rs b/engine/crates/vts-synth/src/ts/traits.rs new file mode 100644 index 00000000..e62c75f8 --- /dev/null +++ b/engine/crates/vts-synth/src/ts/traits.rs @@ -0,0 +1,315 @@ +//! Abstractions for explicit-state transition systems. + +use std::{fmt::Debug, hash::Hash}; + +/// Base trait for transition systems. +pub trait BaseTs { + /// Type for unique identifiers of the states of the TS. + type StateId: Debug + Clone + Eq + Hash; + + /// State type of the TS. + type State; + + /// Retrieves the state with the given id. + fn get_label(&self, id: &Self::StateId) -> &Self::State; +} + +impl<'ts, TS: BaseTs> BaseTs for &'ts TS { + type StateId = TS::StateId; + + type State = TS::State; + + #[inline(always)] + fn get_label(&self, id: &Self::StateId) -> &Self::State { + (*self).get_label(id) + } +} + +/// A copyable reference to a TS. +pub trait TsRef: BaseTs + Copy {} + +impl TsRef for TS {} + +/// A TS supporting iteration over all states. +pub trait States: BaseTs { + /// Iterator type over all states of the TS. + type StatesIter<'iter>: 'iter + Iterator + where + Self: 'iter; + + /// Iterator over all states of the TS. + fn states(&self) -> Self::StatesIter<'_>; + + /// The number of states of the TS. + fn num_states(&self) -> usize { + self.states().count() + } +} + +impl<'ts, TS: States> States for &'ts TS { + type StatesIter<'iter> = TS::StatesIter<'iter> where Self: 'iter; + + #[inline(always)] + fn states(&self) -> Self::StatesIter<'_> { + (*self).states() + } + + #[inline(always)] + fn num_states(&self) -> usize { + (*self).num_states() + } +} + +/// A TS with _initial states_. +pub trait InitialStates: BaseTs { + /// Iterator type over the initial states of the TS. + type InitialStatesIter<'iter>: 'iter + Iterator + where + Self: 'iter; + + /// Iterator over the initial states of the TS. + fn initial_states(&self) -> Self::InitialStatesIter<'_>; + + /// Checks whether the state is an initial state. + fn is_initial(&self, state: &Self::StateId) -> bool; + + /// The number of initial states of the TS. + fn num_initial_states(&self) -> usize { + self.initial_states().count() + } +} + +impl<'ts, TS: InitialStates> InitialStates for &'ts TS { + type InitialStatesIter<'iter> = TS::InitialStatesIter<'iter> + where + Self: 'iter; + + #[inline(always)] + fn initial_states(&self) -> Self::InitialStatesIter<'_> { + (*self).initial_states() + } + + #[inline(always)] + fn is_initial(&self, state: &Self::StateId) -> bool { + (*self).is_initial(state) + } + + #[inline(always)] + fn num_initial_states(&self) -> usize { + (*self).num_initial_states() + } +} + +/// A TS supporting iteration over all transitions. +pub trait Transitions: BaseTs { + type Transition<'trans> + where + Self: 'trans; + + /// Iterator type over all transitions of the TS. + type TransitionsIter<'iter>: 'iter + Iterator> + where + Self: 'iter; + + /// Iterator over all transitions of the TS. + fn transitions(&self) -> Self::TransitionsIter<'_>; + + /// The number of transitions of the TS. + fn num_transitions(&self) -> usize { + self.transitions().count() + } +} + +impl<'ts, TS: Transitions> Transitions for &'ts TS { + type Transition<'trans> = TS::Transition<'trans> + where + Self: 'trans; + + type TransitionsIter<'iter> = TS::TransitionsIter<'iter> + where + Self: 'iter; + + #[inline(always)] + fn transitions(&self) -> Self::TransitionsIter<'_> { + (*self).transitions() + } + + #[inline(always)] + fn num_transitions(&self) -> usize { + (*self).num_transitions() + } +} + +/// A TS supporting iteration over the _successor states_ of a state. +pub trait Successors: BaseTs { + /// Iterator type over the successor states of a state. + type SuccessorsIter<'iter>: 'iter + Iterator + where + Self: 'iter; + + /// Iterator over the successor states of a state. + fn successors(&self, state: &Self::StateId) -> Self::SuccessorsIter<'_>; +} + +impl<'ts, TS: Successors> Successors for &'ts TS { + type SuccessorsIter<'iter> = TS::SuccessorsIter<'iter> + where + Self: 'iter; + + #[inline(always)] + fn successors(&self, state: &Self::StateId) -> Self::SuccessorsIter<'_> { + (*self).successors(state) + } +} + +/// A TS supporting iteration over the _predecessor states_ of a state. +pub trait Predecessors: BaseTs { + /// Iterator type over the predecessor states of a state. + type PredecessorsIter<'iter>: 'iter + Iterator + where + Self: 'iter; + + /// Iterator over the predecessor states of a state. + fn predecessors(&self, state: &Self::StateId) -> Self::PredecessorsIter<'_>; +} + +impl<'ts, TS: Predecessors> Predecessors for &'ts TS { + type PredecessorsIter<'iter> = TS::PredecessorsIter<'iter> + where + Self: 'iter; + + #[inline(always)] + fn predecessors(&self, state: &Self::StateId) -> Self::PredecessorsIter<'_> { + (*self).predecessors(state) + } +} + +/// A set of states of a TS. +pub trait StateSet { + /// Inserts a state into the set. + /// + /// Returns `true` if the state has been inserted, i.e., was not already in the + /// set. + fn insert(&mut self, state: S) -> bool; + + /// Removes a state from the set. + /// + /// Returns `true` if the state has been removed, i.e., was contained in the set. + fn remove(&mut self, state: &S) -> bool; + + /// Checks whether a state is contained in the set. + fn contains(&self, state: &S) -> bool; + + /// Clears the set. + fn clear(&mut self); + + type Iter<'iter>: 'iter + Iterator + where + Self: 'iter; + + fn iter(&self) -> Self::Iter<'_>; +} + +/// A TS supporting _dense state sets_. +pub trait MakeDenseStateSet: BaseTs { + /// Type of dense state sets. + type DenseStateSet: StateSet; + + /// Creates an empty dense state set. + fn make_dense_state_set(&self) -> Self::DenseStateSet; +} + +impl<'ts, TS: MakeDenseStateSet> MakeDenseStateSet for &'ts TS { + type DenseStateSet = TS::DenseStateSet; + + fn make_dense_state_set(&self) -> Self::DenseStateSet { + (*self).make_dense_state_set() + } +} + +/// A TS supporting _sparse state sets_. +pub trait MakeSparseStateSet: BaseTs { + /// Type of sparse state sets. + type SparseStateSet: StateSet; + + /// Crates an empty sparse state set. + fn make_sparse_state_set(&self) -> Self::SparseStateSet; +} + +impl<'ts, TS: MakeSparseStateSet> MakeSparseStateSet for &'ts TS { + type SparseStateSet = TS::SparseStateSet; + + fn make_sparse_state_set(&self) -> Self::SparseStateSet { + (*self).make_sparse_state_set() + } +} + +/// A map from states of a TS to other values. +pub trait StateMap { + /// Inserts a value for a state. + fn insert(&mut self, state: S, value: V); + + fn insert_default(&mut self, state: S) + where + V: Default, + { + self.insert(state, V::default()) + } + + /// Removes the value for a state. + fn remove(&mut self, state: &S) -> Option; + + /// Returns the value for a state. + fn get(&self, state: &S) -> Option<&V>; + + /// Returns the value for a state. + fn get_mut(&mut self, state: &S) -> Option<&mut V>; + + fn get_mut_or_default(&mut self, state: &S) -> &mut V + where + S: Clone, + V: Default, + { + if !self.contains(state) { + self.insert(state.clone(), V::default()); + } + self.get_mut(state).unwrap() + } + + /// Checks whether the map contains a value for a state. + fn contains(&self, state: &S) -> bool; +} + +/// A TS supporting _dense state maps_. +pub trait MakeDenseStateMap: BaseTs { + /// Type of dense state maps. + type DenseStateMap: StateMap; + + /// Creates an empty dense state map. + fn make_dense_state_map(&self) -> Self::DenseStateMap; +} + +impl<'ts, T, TS: MakeDenseStateMap> MakeDenseStateMap for &'ts TS { + type DenseStateMap = TS::DenseStateMap; + + fn make_dense_state_map(&self) -> Self::DenseStateMap { + (*self).make_dense_state_map() + } +} + +/// A TS supporting _sparse state maps_. +pub trait MakeSparseStateMap: BaseTs { + /// Type of sparse state maps. + type SparseStateMap: StateMap; + + /// Creates an empty sparse state map. + fn make_sparse_state_map(&self) -> Self::SparseStateMap; +} + +impl<'ts, T, TS: MakeSparseStateMap> MakeSparseStateMap for &'ts TS { + type SparseStateMap = TS::SparseStateMap; + + fn make_sparse_state_map(&self) -> Self::SparseStateMap { + (*self).make_sparse_state_map() + } +} diff --git a/engine/crates/vts-synth/src/ts/transposed.rs b/engine/crates/vts-synth/src/ts/transposed.rs new file mode 100644 index 00000000..80eb44d3 --- /dev/null +++ b/engine/crates/vts-synth/src/ts/transposed.rs @@ -0,0 +1,95 @@ +//! Provides a type [`Transposed`] inverting all transitions of a TS. + +use super::traits::*; + +/// A transposed TS. +#[derive(Debug, Clone, Copy)] +pub struct Transposed(TS); + +impl Transposed { + /// Creates a new transposed TS. + pub fn new(ts: TS) -> Self { + Self(ts) + } + + /// Returns the original inner TS. + pub fn into_inner(self) -> TS { + self.0 + } +} + +impl BaseTs for Transposed { + type StateId = TS::StateId; + + type State = TS::State; + + fn get_label(&self, id: &Self::StateId) -> &Self::State { + self.0.get_label(id) + } +} + +impl States for Transposed { + type StatesIter<'iter> = TS::StatesIter<'iter> + where + Self: 'iter; + + fn states(&self) -> Self::StatesIter<'_> { + self.0.states() + } + + fn num_states(&self) -> usize { + self.0.num_states() + } +} + +impl Successors for Transposed { + type SuccessorsIter<'iter> = TS::PredecessorsIter<'iter> + where + Self: 'iter; + + fn successors(&self, state: &Self::StateId) -> Self::SuccessorsIter<'_> { + self.0.predecessors(state) + } +} + +impl Predecessors for Transposed { + type PredecessorsIter<'iter> = TS::SuccessorsIter<'iter> + where + Self: 'iter; + + fn predecessors(&self, state: &Self::StateId) -> Self::PredecessorsIter<'_> { + self.0.successors(state) + } +} + +impl MakeDenseStateSet for Transposed { + type DenseStateSet = TS::DenseStateSet; + + fn make_dense_state_set(&self) -> Self::DenseStateSet { + self.0.make_dense_state_set() + } +} + +impl MakeSparseStateSet for Transposed { + type SparseStateSet = TS::SparseStateSet; + + fn make_sparse_state_set(&self) -> Self::SparseStateSet { + self.0.make_sparse_state_set() + } +} + +impl, T> MakeDenseStateMap for Transposed { + type DenseStateMap = TS::DenseStateMap; + + fn make_dense_state_map(&self) -> Self::DenseStateMap { + self.0.make_dense_state_map() + } +} + +impl, T> MakeSparseStateMap for Transposed { + type SparseStateMap = TS::SparseStateMap; + + fn make_sparse_state_map(&self) -> Self::SparseStateMap { + self.0.make_sparse_state_map() + } +} diff --git a/engine/crates/vts-synth/src/ts/types.rs b/engine/crates/vts-synth/src/ts/types.rs new file mode 100644 index 00000000..ad963be1 --- /dev/null +++ b/engine/crates/vts-synth/src/ts/types.rs @@ -0,0 +1,41 @@ +//! Specialized types of transition systems. + +use super::Ts; + +/// A label of a guarded transition system. +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct VatsLabel { + /// The action of the label. + pub action: A, + /// The guard of the label. + pub guard: G, +} + +impl VatsLabel { + /// Creates a new GTS label. + pub fn new(action: A, guard: G) -> Self { + Self { action, guard } + } +} + +/// A guarded transition system. +pub type Vats = Ts>; + +/// A state of a belief transition system. +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct VtsState { + /// The control state of the state. + pub control: Q, + /// The belief state of the state. + pub verdict: B, +} + +impl VtsState { + /// Creates a new VTS state. + pub fn new(control: Q, verdict: B) -> Self { + Self { control, verdict } + } +} + +/// A belief transition system. +pub type Vts = Ts, L>; diff --git a/engine/crates/vts-synth/src/ts_traits.rs b/engine/crates/vts-synth/src/ts_traits.rs new file mode 100644 index 00000000..97b62fd5 --- /dev/null +++ b/engine/crates/vts-synth/src/ts_traits.rs @@ -0,0 +1,165 @@ +use std::hash::Hash; + +/// Base trait for transition systems. +pub trait BaseTs { + /// Uniquely identifies a state of the transition system. + type StateId: Copy + Eq + Ord + Hash; + + /// Uniquely identifies a transition of the transition system. + type TransitionId: Copy + Eq + Ord + Hash; +} + +impl<'ts, TS: BaseTs> BaseTs for &'ts TS { + type StateId = TS::StateId; + + type TransitionId = TS::TransitionId; +} + +/// A copyable reference to a TS. +pub trait TsRef: BaseTs + Copy {} + +impl TsRef for TS {} + +/// A TS supporting iteration oval all states. +pub trait States: BaseTs { + /// Iterator type over all states of the TS. + type StatesIter<'iter>: 'iter + Iterator + where + Self: 'iter; + + /// Iterator over all states of the TS. + fn states(&self) -> Self::StatesIter<'_>; + + /// The number of states of the TS. + fn num_states(&self) -> usize { + self.states().count() + } +} + +impl<'ts, TS: States> States for &'ts TS { + type StatesIter<'iter> = TS::StatesIter<'iter> where Self: 'iter; + + #[inline(always)] + fn states(&self) -> Self::StatesIter<'_> { + (*self).states() + } + + #[inline(always)] + fn num_states(&self) -> usize { + (*self).num_states() + } +} + +/// A TS with _initial states_. +pub trait InitialStates: BaseTs { + /// Iterator type over the initial states of the TS. + type InitialStatesIter<'iter>: 'iter + Iterator + where + Self: 'iter; + + /// Iterator over the initial states of the TS. + fn initial_states(&self) -> Self::InitialStatesIter<'_>; + + /// Checks whether the state is an initial state. + fn is_initial(&self, state: &Self::StateId) -> bool; + + /// The number of initial states of the TS. + fn num_initial_states(&self) -> usize { + self.initial_states().count() + } +} + +impl<'ts, TS: InitialStates> InitialStates for &'ts TS { + type InitialStatesIter<'iter> = TS::InitialStatesIter<'iter> + where + Self: 'iter; + + #[inline(always)] + fn initial_states(&self) -> Self::InitialStatesIter<'_> { + (*self).initial_states() + } + + #[inline(always)] + fn is_initial(&self, state: &Self::StateId) -> bool { + (*self).is_initial(state) + } + + #[inline(always)] + fn num_initial_states(&self) -> usize { + (*self).num_initial_states() + } +} + +/// A TS supporting iteration over the _successor states_ of a state. +pub trait Successors: BaseTs { + /// Iterator type over the successor states of a state. + type SuccessorsIter<'iter>: 'iter + Iterator + where + Self: 'iter; + + /// Iterator over the successor states of a state. + fn successors(&self, state: &Self::StateId) -> Self::SuccessorsIter<'_>; +} + +impl<'ts, TS: Successors> Successors for &'ts TS { + type SuccessorsIter<'iter> = TS::SuccessorsIter<'iter> + where + Self: 'iter; + + #[inline(always)] + fn successors(&self, state: &Self::StateId) -> Self::SuccessorsIter<'_> { + (*self).successors(state) + } +} + +/// A TS supporting iteration over the _predecessor states_ of a state. +pub trait Predecessors: BaseTs { + /// Iterator type over the predecessor states of a state. + type PredecessorsIter<'iter>: 'iter + Iterator + where + Self: 'iter; + + /// Iterator over the predecessor states of a state. + fn predecessors(&self, state: &Self::StateId) -> Self::PredecessorsIter<'_>; +} + +impl<'ts, TS: Predecessors> Predecessors for &'ts TS { + type PredecessorsIter<'iter> = TS::PredecessorsIter<'iter> + where + Self: 'iter; + + #[inline(always)] + fn predecessors(&self, state: &Self::StateId) -> Self::PredecessorsIter<'_> { + (*self).predecessors(state) + } +} + +/// A TS supporting iteration oval all transitions. +pub trait Transitions: BaseTs { + /// Iterator type over all transitions of the TS. + type TransitionsIter<'iter>: 'iter + Iterator + where + Self: 'iter; + + /// Iterator over all transitions of the TS. + fn transitions(&self) -> Self::TransitionsIter<'_>; + + /// The number of transitions of the TS. + fn num_transitions(&self) -> usize { + self.transitions().count() + } +} + +impl<'ts, TS: Transitions> Transitions for &'ts TS { + type TransitionsIter<'iter> = TS::TransitionsIter<'iter> where Self: 'iter; + + #[inline(always)] + fn transitions(&self) -> Self::TransitionsIter<'_> { + (*self).transitions() + } + + #[inline(always)] + fn num_transitions(&self) -> usize { + (*self).num_transitions() + } +} diff --git a/engine/rust-toolchain b/engine/rust-toolchain index 870bbe4e..ef46bf2d 100644 --- a/engine/rust-toolchain +++ b/engine/rust-toolchain @@ -1 +1 @@ -stable \ No newline at end of file +nightly-2024-06-25 \ No newline at end of file