mirror of
https://github.com/ellmau/adf-obdd.git
synced 2025-12-19 09:29:36 +01:00
Add naive af support to webserver (#195)
* Update flake * Remove cargo-kcov * Make AdfParser publically accessible * Fix mongodb version to 6 * Add naive AF support in Web * Add missing doc strings * Remove unused import * Remove TODO comment * Update DevSkim Action * Upgrade flake * Apply clippy suggestions --------- Co-authored-by: monsterkrampe <monsterkrampe@users.noreply.github.com>
This commit is contained in:
parent
c9631346ab
commit
ff12d4fede
6
.github/workflows/devskim.yml
vendored
6
.github/workflows/devskim.yml
vendored
@ -16,19 +16,19 @@ on:
|
|||||||
jobs:
|
jobs:
|
||||||
lint:
|
lint:
|
||||||
name: DevSkim
|
name: DevSkim
|
||||||
runs-on: ubuntu-20.04
|
runs-on: ubuntu-latest
|
||||||
permissions:
|
permissions:
|
||||||
actions: read
|
actions: read
|
||||||
contents: read
|
contents: read
|
||||||
security-events: write
|
security-events: write
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Run DevSkim scanner
|
- name: Run DevSkim scanner
|
||||||
uses: microsoft/DevSkim-Action@v1
|
uses: microsoft/DevSkim-Action@v1
|
||||||
|
|
||||||
- name: Upload DevSkim scan results to GitHub Security tab
|
- name: Upload DevSkim scan results to GitHub Security tab
|
||||||
uses: github/codeql-action/upload-sarif@v2
|
uses: github/codeql-action/upload-sarif@v3
|
||||||
with:
|
with:
|
||||||
sarif_file: devskim-results.sarif
|
sarif_file: devskim-results.sarif
|
||||||
|
|||||||
@ -204,14 +204,14 @@ impl App {
|
|||||||
Some("nai") => {
|
Some("nai") => {
|
||||||
let naive_adf = adf.hybrid_step_opt(false);
|
let naive_adf = adf.hybrid_step_opt(false);
|
||||||
for ac_counts in naive_adf.formulacounts(false) {
|
for ac_counts in naive_adf.formulacounts(false) {
|
||||||
print!("{:?} ", ac_counts);
|
print!("{ac_counts:?} ");
|
||||||
}
|
}
|
||||||
println!();
|
println!();
|
||||||
}
|
}
|
||||||
Some("mem") => {
|
Some("mem") => {
|
||||||
let naive_adf = adf.hybrid_step_opt(false);
|
let naive_adf = adf.hybrid_step_opt(false);
|
||||||
for ac_counts in naive_adf.formulacounts(true) {
|
for ac_counts in naive_adf.formulacounts(true) {
|
||||||
print!("{:?}", ac_counts);
|
print!("{ac_counts:?}");
|
||||||
}
|
}
|
||||||
println!();
|
println!();
|
||||||
}
|
}
|
||||||
@ -383,13 +383,13 @@ impl App {
|
|||||||
match self.counter.as_deref() {
|
match self.counter.as_deref() {
|
||||||
Some("nai") => {
|
Some("nai") => {
|
||||||
for ac_counts in adf.formulacounts(false) {
|
for ac_counts in adf.formulacounts(false) {
|
||||||
print!("{:?} ", ac_counts);
|
print!("{ac_counts:?} ");
|
||||||
}
|
}
|
||||||
println!();
|
println!();
|
||||||
}
|
}
|
||||||
Some("mem") => {
|
Some("mem") => {
|
||||||
for ac_counts in adf.formulacounts(true) {
|
for ac_counts in adf.formulacounts(true) {
|
||||||
print!("{:?}", ac_counts);
|
print!("{ac_counts:?}");
|
||||||
}
|
}
|
||||||
println!();
|
println!();
|
||||||
}
|
}
|
||||||
|
|||||||
14
flake.lock
generated
14
flake.lock
generated
@ -38,16 +38,16 @@
|
|||||||
},
|
},
|
||||||
"nixpkgs": {
|
"nixpkgs": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1741724370,
|
"lastModified": 1750969886,
|
||||||
"narHash": "sha256-WsD+8uodhl58jzKKcPH4jH9dLTLFWZpVmGq4W1XDVF4=",
|
"narHash": "sha256-zW/OFnotiz/ndPFdebpo3X0CrbVNf22n4DjN2vxlb58=",
|
||||||
"owner": "NixOS",
|
"owner": "NixOS",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"rev": "95600680c021743fd87b3e2fe13be7c290e1cac4",
|
"rev": "a676066377a2fe7457369dd37c31fd2263b662f4",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
"owner": "NixOS",
|
"owner": "NixOS",
|
||||||
"ref": "nixos-24.11",
|
"ref": "nixos-25.05",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
@ -66,11 +66,11 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1741833135,
|
"lastModified": 1751251399,
|
||||||
"narHash": "sha256-HUtFcF4NLwvu7CAowWgqCHXVkNj0EOc/W6Ism4biV6I=",
|
"narHash": "sha256-y+viCuy/eKKpkX1K2gDvXIJI/yzvy6zA3HObapz9XZ0=",
|
||||||
"owner": "oxalica",
|
"owner": "oxalica",
|
||||||
"repo": "rust-overlay",
|
"repo": "rust-overlay",
|
||||||
"rev": "f3cd1e0feb994188fe3ad9a5c3ab021ed433b8c8",
|
"rev": "b22d5ee8c60ed1291521f2dde48784edd6bf695b",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
|
|||||||
@ -2,7 +2,7 @@ rec {
|
|||||||
description = "adf-bdd, Abstract Dialectical Frameworks solved by Binary Decision Diagrams; developed in Dresden";
|
description = "adf-bdd, Abstract Dialectical Frameworks solved by Binary Decision Diagrams; developed in Dresden";
|
||||||
|
|
||||||
inputs = {
|
inputs = {
|
||||||
nixpkgs.url = "github:NixOS/nixpkgs/nixos-24.11";
|
nixpkgs.url = "github:NixOS/nixpkgs/nixos-25.05";
|
||||||
rust-overlay = {
|
rust-overlay = {
|
||||||
url = "github:oxalica/rust-overlay";
|
url = "github:oxalica/rust-overlay";
|
||||||
inputs = {
|
inputs = {
|
||||||
@ -81,7 +81,7 @@ rec {
|
|||||||
pkgs.cargo-audit
|
pkgs.cargo-audit
|
||||||
pkgs.cargo-license
|
pkgs.cargo-license
|
||||||
]
|
]
|
||||||
++ (notOn ["aarch64-darwin" "x86_64-darwin"] [pkgs.kcov pkgs.cargo-kcov pkgs.gnuplot pkgs.valgrind])
|
++ (notOn ["aarch64-darwin" "x86_64-darwin"] [pkgs.kcov pkgs.gnuplot pkgs.valgrind])
|
||||||
++ (notOn ["aarch64-linux" "aarch64-darwin" "i686-linux"] [pkgs.cargo-tarpaulin]);
|
++ (notOn ["aarch64-linux" "aarch64-darwin" "i686-linux"] [pkgs.cargo-tarpaulin]);
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@ -40,6 +40,7 @@ function AdfNewForm({ fetchProblems }: { fetchProblems: () => void; }) {
|
|||||||
const [code, setCode] = useState(PLACEHOLDER);
|
const [code, setCode] = useState(PLACEHOLDER);
|
||||||
const [filename, setFilename] = useState('');
|
const [filename, setFilename] = useState('');
|
||||||
const [parsing, setParsing] = useState<Parsing>('Naive');
|
const [parsing, setParsing] = useState<Parsing>('Naive');
|
||||||
|
const [isAf, setIsAf] = useState(false);
|
||||||
const [name, setName] = useState('');
|
const [name, setName] = useState('');
|
||||||
const fileRef = useRef<HTMLInputElement>(null);
|
const fileRef = useRef<HTMLInputElement>(null);
|
||||||
|
|
||||||
@ -59,6 +60,7 @@ function AdfNewForm({ fetchProblems }: { fetchProblems: () => void; }) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
formData.append('parsing', parsing);
|
formData.append('parsing', parsing);
|
||||||
|
formData.append('is_af', isAf);
|
||||||
formData.append('name', name);
|
formData.append('name', name);
|
||||||
|
|
||||||
fetch(`${process.env.NODE_ENV === 'development' ? '//localhost:8080' : ''}/adf/add`, {
|
fetch(`${process.env.NODE_ENV === 'development' ? '//localhost:8080' : ''}/adf/add`, {
|
||||||
@ -119,10 +121,13 @@ function AdfNewForm({ fetchProblems }: { fetchProblems: () => void; }) {
|
|||||||
label="Put your code here:"
|
label="Put your code here:"
|
||||||
helperText={(
|
helperText={(
|
||||||
<>
|
<>
|
||||||
For more info on the syntax, have a
|
For more info on the ADF syntax, have a
|
||||||
look
|
look
|
||||||
{' '}
|
{' '}
|
||||||
<Link href="https://github.com/ellmau/adf-obdd" target="_blank" rel="noopener noreferrer">here</Link>
|
<Link href="https://github.com/ellmau/adf-obdd" target="_blank" rel="noopener noreferrer">here</Link>
|
||||||
|
. For the AF syntax, we currently only allow the ICCMA competition format, see for example
|
||||||
|
{' '}
|
||||||
|
<Link href="https://argumentationcompetition.org/2025/rules.html" target="_blank" rel="noopener noreferrer">here</Link>
|
||||||
.
|
.
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
@ -137,6 +142,20 @@ function AdfNewForm({ fetchProblems }: { fetchProblems: () => void; }) {
|
|||||||
|
|
||||||
<Container sx={{ marginTop: 2 }}>
|
<Container sx={{ marginTop: 2 }}>
|
||||||
<Stack direction="row" justifyContent="center" spacing={2}>
|
<Stack direction="row" justifyContent="center" spacing={2}>
|
||||||
|
<FormControl>
|
||||||
|
<FormLabel id="isAf-radio-group">ADF or AF?</FormLabel>
|
||||||
|
<RadioGroup
|
||||||
|
row
|
||||||
|
aria-labelledby="isAf-radio-group"
|
||||||
|
name="isAf"
|
||||||
|
value={isAf}
|
||||||
|
onChange={(e) => setIsAf(((e.target as HTMLInputElement).value))}
|
||||||
|
>
|
||||||
|
<FormControlLabel value={false} control={<Radio />} label="ADF" />
|
||||||
|
<FormControlLabel value={true} control={<Radio />} label="AF" />
|
||||||
|
</RadioGroup>
|
||||||
|
<span style={{ fontSize: "0.7em" }}>AFs are converted to ADFs internally.</span>
|
||||||
|
</FormControl>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<FormLabel id="parsing-radio-group">Parsing Strategy</FormLabel>
|
<FormLabel id="parsing-radio-group">Parsing Strategy</FormLabel>
|
||||||
<RadioGroup
|
<RadioGroup
|
||||||
|
|||||||
@ -21,3 +21,17 @@ The `Parsing Strategy` determines the internal implementation used for these. `N
|
|||||||
You will get a view on the BDD in the detail view after you added the problem.
|
You will get a view on the BDD in the detail view after you added the problem.
|
||||||
|
|
||||||
You can optionally set a name for you ADF problem. Otherwise a random name will be chosen. At the moment the name cannot be changed later (but you could remove and re-add the problem).
|
You can optionally set a name for you ADF problem. Otherwise a random name will be chosen. At the moment the name cannot be changed later (but you could remove and re-add the problem).
|
||||||
|
|
||||||
|
We also support adding AFs in the ICCMA competition format. They are converted to ADFs internally in the obvious way.
|
||||||
|
For example you can try the following code and change the option below from ADF to AF.
|
||||||
|
|
||||||
|
```
|
||||||
|
p af 5
|
||||||
|
# this is a comment
|
||||||
|
1 2
|
||||||
|
2 4
|
||||||
|
4 5
|
||||||
|
5 4
|
||||||
|
5 5
|
||||||
|
```
|
||||||
|
|
||||||
|
|||||||
@ -275,8 +275,8 @@ mod test {
|
|||||||
let term: Term = Term::from(value);
|
let term: Term = Term::from(value);
|
||||||
let var = Var::from(value);
|
let var = Var::from(value);
|
||||||
// display
|
// display
|
||||||
assert_eq!(format!("{}", term), format!("Term({})", value));
|
assert_eq!(format!("{term}"), format!("Term({})", value));
|
||||||
assert_eq!(format!("{}", var), format!("Var({})", value));
|
assert_eq!(format!("{var}"), format!("Var({})", value));
|
||||||
//deref
|
//deref
|
||||||
assert_eq!(value, *term);
|
assert_eq!(value, *term);
|
||||||
true
|
true
|
||||||
|
|||||||
@ -690,7 +690,7 @@ mod test {
|
|||||||
let a1 = bdd.and(v1, v2);
|
let a1 = bdd.and(v1, v2);
|
||||||
let _a2 = bdd.or(a1, v3);
|
let _a2 = bdd.or(a1, v3);
|
||||||
|
|
||||||
assert_eq!(format!("{}", bdd), " \n0 BddNode: Var(18446744073709551614), lo: Term(0), hi: Term(0)\n1 BddNode: Var(18446744073709551615), lo: Term(1), hi: Term(1)\n2 BddNode: Var(0), lo: Term(0), hi: Term(1)\n3 BddNode: Var(1), lo: Term(0), hi: Term(1)\n4 BddNode: Var(2), lo: Term(0), hi: Term(1)\n5 BddNode: Var(0), lo: Term(0), hi: Term(3)\n6 BddNode: Var(1), lo: Term(4), hi: Term(1)\n7 BddNode: Var(0), lo: Term(4), hi: Term(6)\n");
|
assert_eq!(format!("{bdd}"), " \n0 BddNode: Var(18446744073709551614), lo: Term(0), hi: Term(0)\n1 BddNode: Var(18446744073709551615), lo: Term(1), hi: Term(1)\n2 BddNode: Var(0), lo: Term(0), hi: Term(1)\n3 BddNode: Var(1), lo: Term(0), hi: Term(1)\n4 BddNode: Var(2), lo: Term(0), hi: Term(1)\n5 BddNode: Var(0), lo: Term(0), hi: Term(3)\n6 BddNode: Var(1), lo: Term(4), hi: Term(1)\n7 BddNode: Var(0), lo: Term(4), hi: Term(6)\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|||||||
@ -20,28 +20,28 @@ use crate::datatypes::adf::VarContainer;
|
|||||||
|
|
||||||
/// A representation of a formula, still using the strings from the input.
|
/// A representation of a formula, still using the strings from the input.
|
||||||
#[derive(Clone, PartialEq, Eq)]
|
#[derive(Clone, PartialEq, Eq)]
|
||||||
pub enum Formula<'a> {
|
pub enum Formula {
|
||||||
/// `c(f)` in the input format.
|
/// `c(f)` in the input format.
|
||||||
Bot,
|
Bot,
|
||||||
/// `c(v)` in the input format.
|
/// `c(v)` in the input format.
|
||||||
Top,
|
Top,
|
||||||
/// Some atomic variable in the input format.
|
/// Some atomic variable in the input format.
|
||||||
Atom(&'a str),
|
Atom(String),
|
||||||
/// Negation of a subformula.
|
/// Negation of a subformula.
|
||||||
Not(Box<Formula<'a>>),
|
Not(Box<Formula>),
|
||||||
/// Conjunction of two subformulae.
|
/// Conjunction of two subformulae.
|
||||||
And(Box<Formula<'a>>, Box<Formula<'a>>),
|
And(Box<Formula>, Box<Formula>),
|
||||||
/// Disjunction of two subformulae.
|
/// Disjunction of two subformulae.
|
||||||
Or(Box<Formula<'a>>, Box<Formula<'a>>),
|
Or(Box<Formula>, Box<Formula>),
|
||||||
/// Implication of two subformulae.
|
/// Implication of two subformulae.
|
||||||
Imp(Box<Formula<'a>>, Box<Formula<'a>>),
|
Imp(Box<Formula>, Box<Formula>),
|
||||||
/// Exclusive-Or of two subformulae.
|
/// Exclusive-Or of two subformulae.
|
||||||
Xor(Box<Formula<'a>>, Box<Formula<'a>>),
|
Xor(Box<Formula>, Box<Formula>),
|
||||||
/// If and only if connective between two formulae.
|
/// If and only if connective between two formulae.
|
||||||
Iff(Box<Formula<'a>>, Box<Formula<'a>>),
|
Iff(Box<Formula>, Box<Formula>),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Formula<'_> {
|
impl Formula {
|
||||||
pub(crate) fn to_boolean_expr(
|
pub(crate) fn to_boolean_expr(
|
||||||
&self,
|
&self,
|
||||||
) -> biodivine_lib_bdd::boolean_expression::BooleanExpression {
|
) -> biodivine_lib_bdd::boolean_expression::BooleanExpression {
|
||||||
@ -90,29 +90,29 @@ impl Formula<'_> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::fmt::Debug for Formula<'_> {
|
impl std::fmt::Debug for Formula {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
match self {
|
match self {
|
||||||
Formula::Atom(a) => {
|
Formula::Atom(a) => {
|
||||||
write!(f, "{}", a)?;
|
write!(f, "{a}")?;
|
||||||
}
|
}
|
||||||
Formula::Not(n) => {
|
Formula::Not(n) => {
|
||||||
write!(f, "not({:?})", n)?;
|
write!(f, "not({n:?})")?;
|
||||||
}
|
}
|
||||||
Formula::And(f1, f2) => {
|
Formula::And(f1, f2) => {
|
||||||
write!(f, "and({:?},{:?})", f1, f2)?;
|
write!(f, "and({f1:?},{f2:?})")?;
|
||||||
}
|
}
|
||||||
Formula::Or(f1, f2) => {
|
Formula::Or(f1, f2) => {
|
||||||
write!(f, "or({:?},{:?})", f1, f2)?;
|
write!(f, "or({f1:?},{f2:?})")?;
|
||||||
}
|
}
|
||||||
Formula::Imp(f1, f2) => {
|
Formula::Imp(f1, f2) => {
|
||||||
write!(f, "imp({:?},{:?})", f1, f2)?;
|
write!(f, "imp({f1:?},{f2:?})")?;
|
||||||
}
|
}
|
||||||
Formula::Xor(f1, f2) => {
|
Formula::Xor(f1, f2) => {
|
||||||
write!(f, "xor({:?},{:?})", f1, f2)?;
|
write!(f, "xor({f1:?},{f2:?})")?;
|
||||||
}
|
}
|
||||||
Formula::Iff(f1, f2) => {
|
Formula::Iff(f1, f2) => {
|
||||||
write!(f, "iff({:?},{:?})", f1, f2)?;
|
write!(f, "iff({f1:?},{f2:?})")?;
|
||||||
}
|
}
|
||||||
Formula::Bot => {
|
Formula::Bot => {
|
||||||
write!(f, "Const(B)")?;
|
write!(f, "Const(B)")?;
|
||||||
@ -132,14 +132,18 @@ impl std::fmt::Debug for Formula<'_> {
|
|||||||
///
|
///
|
||||||
/// Note that the parser can be utilised by an [ADF][`crate::adf::Adf`] to initialise it with minimal overhead.
|
/// Note that the parser can be utilised by an [ADF][`crate::adf::Adf`] to initialise it with minimal overhead.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct AdfParser<'a> {
|
pub struct AdfParser {
|
||||||
namelist: Arc<RwLock<Vec<String>>>,
|
/// A name for each statement (identified by index in vector)
|
||||||
dict: Arc<RwLock<HashMap<String, usize>>>,
|
pub namelist: Arc<RwLock<Vec<String>>>,
|
||||||
formulae: RefCell<Vec<Formula<'a>>>,
|
/// Inverse mapping from name to index of statement in vector above
|
||||||
formulaname: RefCell<Vec<String>>,
|
pub dict: Arc<RwLock<HashMap<String, usize>>>,
|
||||||
|
/// The formula (acceptance condition) for each statement identified by its index
|
||||||
|
pub formulae: RefCell<Vec<Formula>>,
|
||||||
|
/// The formula for each statement identified by its index
|
||||||
|
pub formulaname: RefCell<Vec<String>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for AdfParser<'_> {
|
impl Default for AdfParser {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
AdfParser {
|
AdfParser {
|
||||||
namelist: Arc::new(RwLock::new(Vec::new())),
|
namelist: Arc::new(RwLock::new(Vec::new())),
|
||||||
@ -150,10 +154,7 @@ impl Default for AdfParser<'_> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, 'b> AdfParser<'b>
|
impl<'a> AdfParser {
|
||||||
where
|
|
||||||
'a: 'b,
|
|
||||||
{
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
fn parse_statements(&'a self) -> impl FnMut(&'a str) -> IResult<&'a str, ()> {
|
fn parse_statements(&'a self) -> impl FnMut(&'a str) -> IResult<&'a str, ()> {
|
||||||
move |input| {
|
move |input| {
|
||||||
@ -212,9 +213,9 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> AdfParser<'a> {
|
impl AdfParser {
|
||||||
/// Creates a new parser, utilising the already existing [VarContainer]
|
/// Creates a new parser, utilising the already existing [VarContainer]
|
||||||
pub fn with_var_container(var_container: VarContainer) -> AdfParser<'a> {
|
pub fn with_var_container(var_container: VarContainer) -> AdfParser {
|
||||||
AdfParser {
|
AdfParser {
|
||||||
namelist: var_container.names(),
|
namelist: var_container.names(),
|
||||||
dict: var_container.mappings(),
|
dict: var_container.mappings(),
|
||||||
@ -224,7 +225,7 @@ impl<'a> AdfParser<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AdfParser<'_> {
|
impl AdfParser {
|
||||||
/// after an update to the namelist, all indizes are updated
|
/// after an update to the namelist, all indizes are updated
|
||||||
fn regenerate_indizes(&self) {
|
fn regenerate_indizes(&self) {
|
||||||
self.namelist
|
self.namelist
|
||||||
@ -284,7 +285,7 @@ impl AdfParser<'_> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn atomic_term(input: &str) -> IResult<&str, Formula> {
|
fn atomic_term(input: &str) -> IResult<&str, Formula> {
|
||||||
AdfParser::atomic(input).map(|(input, result)| (input, Formula::Atom(result)))
|
AdfParser::atomic(input).map(|(input, result)| (input, Formula::Atom(result.to_string())))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn formula(input: &str) -> IResult<&str, Formula> {
|
fn formula(input: &str) -> IResult<&str, Formula> {
|
||||||
@ -480,7 +481,7 @@ mod test {
|
|||||||
let (_remain, result) = AdfParser::formula(input).unwrap();
|
let (_remain, result) = AdfParser::formula(input).unwrap();
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
format!("{:?}", result),
|
format!("{result:?}"),
|
||||||
"and(or(not(a),iff( iff left ,b)),xor(imp(c,d),e))"
|
"and(or(not(a),iff( iff left ,b)),xor(imp(c,d),e))"
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -504,7 +505,10 @@ mod test {
|
|||||||
assert_eq!(parser.dict_value("b"), Some(2usize));
|
assert_eq!(parser.dict_value("b"), Some(2usize));
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
format!("{:?}", parser.ac_at(1).unwrap()),
|
format!("{:?}", parser.ac_at(1).unwrap()),
|
||||||
format!("{:?}", Formula::Not(Box::new(Formula::Atom("a"))))
|
format!(
|
||||||
|
"{:?}",
|
||||||
|
Formula::Not(Box::new(Formula::Atom("a".to_string())))
|
||||||
|
)
|
||||||
);
|
);
|
||||||
assert_eq!(parser.formula_count(), 3);
|
assert_eq!(parser.formula_count(), 3);
|
||||||
assert_eq!(parser.formula_order(), vec![0, 2, 1]);
|
assert_eq!(parser.formula_order(), vec![0, 2, 1]);
|
||||||
|
|||||||
@ -3,7 +3,7 @@ version: '3.1'
|
|||||||
services:
|
services:
|
||||||
|
|
||||||
mongo:
|
mongo:
|
||||||
image: mongo
|
image: mongo:6
|
||||||
restart: always
|
restart: always
|
||||||
ports:
|
ports:
|
||||||
- 27017:27017
|
- 27017:27017
|
||||||
|
|||||||
@ -1,3 +1,4 @@
|
|||||||
|
use std::cell::RefCell;
|
||||||
use std::collections::{HashMap, HashSet};
|
use std::collections::{HashMap, HashSet};
|
||||||
use std::sync::{Arc, RwLock};
|
use std::sync::{Arc, RwLock};
|
||||||
#[cfg(feature = "mock_long_computations")]
|
#[cfg(feature = "mock_long_computations")]
|
||||||
@ -21,7 +22,7 @@ use serde::{Deserialize, Serialize};
|
|||||||
use adf_bdd::adf::Adf;
|
use adf_bdd::adf::Adf;
|
||||||
use adf_bdd::adfbiodivine::Adf as BdAdf;
|
use adf_bdd::adfbiodivine::Adf as BdAdf;
|
||||||
use adf_bdd::obdd::Bdd;
|
use adf_bdd::obdd::Bdd;
|
||||||
use adf_bdd::parser::AdfParser;
|
use adf_bdd::parser::{AdfParser, Formula};
|
||||||
|
|
||||||
use crate::config::{AppState, RunningInfo, Task, ADF_COLL, COMPUTE_TIME, DB_NAME, USER_COLL};
|
use crate::config::{AppState, RunningInfo, Task, ADF_COLL, COMPUTE_TIME, DB_NAME, USER_COLL};
|
||||||
use crate::user::{username_exists, User};
|
use crate::user::{username_exists, User};
|
||||||
@ -205,6 +206,8 @@ pub(crate) struct AdfProblem {
|
|||||||
pub(crate) username: String,
|
pub(crate) username: String,
|
||||||
pub(crate) code: String,
|
pub(crate) code: String,
|
||||||
pub(crate) parsing_used: Parsing,
|
pub(crate) parsing_used: Parsing,
|
||||||
|
#[serde(default)]
|
||||||
|
pub(crate) is_af: bool,
|
||||||
pub(crate) adf: SimplifiedAdfOpt,
|
pub(crate) adf: SimplifiedAdfOpt,
|
||||||
pub(crate) acs_per_strategy: AcsPerStrategy,
|
pub(crate) acs_per_strategy: AcsPerStrategy,
|
||||||
}
|
}
|
||||||
@ -215,6 +218,7 @@ struct AddAdfProblemBodyMultipart {
|
|||||||
code: Option<Text<String>>, // Either Code or File is set
|
code: Option<Text<String>>, // Either Code or File is set
|
||||||
file: Option<TempFile>, // Either Code or File is set
|
file: Option<TempFile>, // Either Code or File is set
|
||||||
parsing: Text<Parsing>,
|
parsing: Text<Parsing>,
|
||||||
|
is_af: Text<bool>, // if its not an AF then it is an ADF
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
@ -222,6 +226,7 @@ struct AddAdfProblemBodyPlain {
|
|||||||
name: String,
|
name: String,
|
||||||
code: String,
|
code: String,
|
||||||
parsing: Parsing,
|
parsing: Parsing,
|
||||||
|
is_af: bool, // if its not an AF then it is an ADF
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TryFrom<AddAdfProblemBodyMultipart> for AddAdfProblemBodyPlain {
|
impl TryFrom<AddAdfProblemBodyMultipart> for AddAdfProblemBodyPlain {
|
||||||
@ -237,6 +242,7 @@ impl TryFrom<AddAdfProblemBodyMultipart> for AddAdfProblemBodyPlain {
|
|||||||
.and_then(|code| (!code.is_empty()).then_some(code))
|
.and_then(|code| (!code.is_empty()).then_some(code))
|
||||||
.ok_or("Either a file or the code has to be provided.")?,
|
.ok_or("Either a file or the code has to be provided.")?,
|
||||||
parsing: source.parsing.into_inner(),
|
parsing: source.parsing.into_inner(),
|
||||||
|
is_af: source.is_af.into_inner(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -259,6 +265,7 @@ struct AdfProblemInfo {
|
|||||||
name: String,
|
name: String,
|
||||||
code: String,
|
code: String,
|
||||||
parsing_used: Parsing,
|
parsing_used: Parsing,
|
||||||
|
is_af: bool,
|
||||||
acs_per_strategy: AcsPerStrategy,
|
acs_per_strategy: AcsPerStrategy,
|
||||||
running_tasks: Vec<Task>,
|
running_tasks: Vec<Task>,
|
||||||
}
|
}
|
||||||
@ -269,6 +276,7 @@ impl AdfProblemInfo {
|
|||||||
name: adf.name.clone(),
|
name: adf.name.clone(),
|
||||||
code: adf.code,
|
code: adf.code,
|
||||||
parsing_used: adf.parsing_used,
|
parsing_used: adf.parsing_used,
|
||||||
|
is_af: adf.is_af,
|
||||||
acs_per_strategy: adf.acs_per_strategy,
|
acs_per_strategy: adf.acs_per_strategy,
|
||||||
running_tasks: tasks
|
running_tasks: tasks
|
||||||
.iter()
|
.iter()
|
||||||
@ -280,6 +288,87 @@ impl AdfProblemInfo {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct AF(Vec<Vec<usize>>);
|
||||||
|
|
||||||
|
impl From<AF> for AdfParser {
|
||||||
|
fn from(source: AF) -> Self {
|
||||||
|
let names: Vec<String> = (0..source.0.len())
|
||||||
|
.map(|val| (val + 1).to_string())
|
||||||
|
.collect();
|
||||||
|
let dict: HashMap<String, usize> = names
|
||||||
|
.iter()
|
||||||
|
.enumerate()
|
||||||
|
.map(|(i, val)| (val.clone(), i))
|
||||||
|
.collect();
|
||||||
|
let formulae: Vec<Formula> = source
|
||||||
|
.0
|
||||||
|
.into_iter()
|
||||||
|
.map(|attackers| {
|
||||||
|
attackers.into_iter().fold(Formula::Top, |acc, attacker| {
|
||||||
|
Formula::And(
|
||||||
|
Box::new(acc),
|
||||||
|
Box::new(Formula::Not(Box::new(Formula::Atom(
|
||||||
|
(attacker + 1).to_string(),
|
||||||
|
)))),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
let formulanames = names.clone();
|
||||||
|
|
||||||
|
Self {
|
||||||
|
namelist: Arc::new(RwLock::new(names)),
|
||||||
|
dict: Arc::new(RwLock::new(dict)),
|
||||||
|
formulae: RefCell::new(formulae),
|
||||||
|
formulaname: RefCell::new(formulanames),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_af(code: String) -> Result<AdfParser, &'static str> {
|
||||||
|
let mut lines = code.lines();
|
||||||
|
|
||||||
|
let Some(first_line) = lines.next() else {
|
||||||
|
return Err("There must be at least one line in the AF input.");
|
||||||
|
};
|
||||||
|
|
||||||
|
let first_line: Vec<_> = first_line.split(" ").collect();
|
||||||
|
if first_line[0] != "p" || first_line[1] != "af" {
|
||||||
|
return Err("Expected first line to be of the form: p af <n>");
|
||||||
|
}
|
||||||
|
|
||||||
|
let Ok(num_arguments) = first_line[2].parse::<usize>() else {
|
||||||
|
return Err("Could not convert number of arguments to u32; expected first line to be of the form: p af <n>");
|
||||||
|
};
|
||||||
|
|
||||||
|
let attacks_opt: Option<Vec<(usize, usize)>> = lines
|
||||||
|
.filter(|line| !line.starts_with('#') && !line.is_empty())
|
||||||
|
.map(|line| {
|
||||||
|
let mut line = line.split(" ");
|
||||||
|
let a = line.next()?;
|
||||||
|
let b = line.next()?;
|
||||||
|
if line.next().is_some() {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some((a.parse::<usize>().ok()?, b.parse::<usize>().ok()?))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
let Some(attacks) = attacks_opt else {
|
||||||
|
return Err("Line must be of the form: n m");
|
||||||
|
};
|
||||||
|
|
||||||
|
// index in outer vector represents attacked element
|
||||||
|
let mut is_attacked_by: Vec<Vec<usize>> = vec![vec![]; num_arguments];
|
||||||
|
for (a, b) in attacks {
|
||||||
|
is_attacked_by[b - 1].push(a - 1); // we normalize names to be zero-indexed
|
||||||
|
}
|
||||||
|
|
||||||
|
let hacked_adf_parser = AdfParser::from(AF(is_attacked_by));
|
||||||
|
|
||||||
|
Ok(hacked_adf_parser)
|
||||||
|
}
|
||||||
|
|
||||||
#[post("/add")]
|
#[post("/add")]
|
||||||
async fn add_adf_problem(
|
async fn add_adf_problem(
|
||||||
req: HttpRequest,
|
req: HttpRequest,
|
||||||
@ -381,6 +470,7 @@ async fn add_adf_problem(
|
|||||||
username: username.clone(),
|
username: username.clone(),
|
||||||
code: adf_problem_input.code.clone(),
|
code: adf_problem_input.code.clone(),
|
||||||
parsing_used: adf_problem_input.parsing,
|
parsing_used: adf_problem_input.parsing,
|
||||||
|
is_af: adf_problem_input.is_af,
|
||||||
adf: SimplifiedAdfOpt::None,
|
adf: SimplifiedAdfOpt::None,
|
||||||
acs_per_strategy: AcsPerStrategy::default(),
|
acs_per_strategy: AcsPerStrategy::default(),
|
||||||
};
|
};
|
||||||
@ -413,9 +503,20 @@ async fn add_adf_problem(
|
|||||||
#[cfg(feature = "mock_long_computations")]
|
#[cfg(feature = "mock_long_computations")]
|
||||||
std::thread::sleep(Duration::from_secs(20));
|
std::thread::sleep(Duration::from_secs(20));
|
||||||
|
|
||||||
let parser = AdfParser::default();
|
let (parser, parse_result) = {
|
||||||
let parse_result = parser.parse()(&adf_problem_input.code)
|
if adf_problem_input.is_af {
|
||||||
.map_err(|_| "ADF could not be parsed, double check your input!");
|
parse_af(adf_problem_input.code)
|
||||||
|
.map(|p| (p, Ok(())))
|
||||||
|
.unwrap_or_else(|e| (AdfParser::default(), Err(e)))
|
||||||
|
} else {
|
||||||
|
let parser = AdfParser::default();
|
||||||
|
let parse_result = parser.parse()(&adf_problem_input.code)
|
||||||
|
.map(|_| ())
|
||||||
|
.map_err(|_| "ADF could not be parsed, double check your input!");
|
||||||
|
|
||||||
|
(parser, parse_result)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
let result = parse_result.map(|_| {
|
let result = parse_result.map(|_| {
|
||||||
let lib_adf = match adf_problem_input.parsing {
|
let lib_adf = match adf_problem_input.parsing {
|
||||||
@ -500,7 +601,7 @@ async fn solve_adf_problem(
|
|||||||
.collection(ADF_COLL);
|
.collection(ADF_COLL);
|
||||||
|
|
||||||
let username = match identity.map(|id| id.id()) {
|
let username = match identity.map(|id| id.id()) {
|
||||||
None => {
|
Option::None => {
|
||||||
return HttpResponse::Unauthorized().body("You need to login to add an ADF problem.")
|
return HttpResponse::Unauthorized().body("You need to login to add an ADF problem.")
|
||||||
}
|
}
|
||||||
Some(Err(err)) => return HttpResponse::InternalServerError().body(err.to_string()),
|
Some(Err(err)) => return HttpResponse::InternalServerError().body(err.to_string()),
|
||||||
@ -512,7 +613,7 @@ async fn solve_adf_problem(
|
|||||||
.await
|
.await
|
||||||
{
|
{
|
||||||
Err(err) => return HttpResponse::InternalServerError().body(err.to_string()),
|
Err(err) => return HttpResponse::InternalServerError().body(err.to_string()),
|
||||||
Ok(None) => {
|
Ok(Option::None) => {
|
||||||
return HttpResponse::NotFound()
|
return HttpResponse::NotFound()
|
||||||
.body(format!("ADF problem with name {problem_name} not found."))
|
.body(format!("ADF problem with name {problem_name} not found."))
|
||||||
}
|
}
|
||||||
@ -644,7 +745,7 @@ async fn get_adf_problem(
|
|||||||
.collection(ADF_COLL);
|
.collection(ADF_COLL);
|
||||||
|
|
||||||
let username = match identity.map(|id| id.id()) {
|
let username = match identity.map(|id| id.id()) {
|
||||||
None => {
|
Option::None => {
|
||||||
return HttpResponse::Unauthorized().body("You need to login to get an ADF problem.")
|
return HttpResponse::Unauthorized().body("You need to login to get an ADF problem.")
|
||||||
}
|
}
|
||||||
Some(Err(err)) => return HttpResponse::InternalServerError().body(err.to_string()),
|
Some(Err(err)) => return HttpResponse::InternalServerError().body(err.to_string()),
|
||||||
@ -656,7 +757,7 @@ async fn get_adf_problem(
|
|||||||
.await
|
.await
|
||||||
{
|
{
|
||||||
Err(err) => return HttpResponse::InternalServerError().body(err.to_string()),
|
Err(err) => return HttpResponse::InternalServerError().body(err.to_string()),
|
||||||
Ok(None) => {
|
Ok(Option::None) => {
|
||||||
return HttpResponse::NotFound()
|
return HttpResponse::NotFound()
|
||||||
.body(format!("ADF problem with name {problem_name} not found."))
|
.body(format!("ADF problem with name {problem_name} not found."))
|
||||||
}
|
}
|
||||||
@ -682,7 +783,7 @@ async fn delete_adf_problem(
|
|||||||
.collection(ADF_COLL);
|
.collection(ADF_COLL);
|
||||||
|
|
||||||
let username = match identity.map(|id| id.id()) {
|
let username = match identity.map(|id| id.id()) {
|
||||||
None => {
|
Option::None => {
|
||||||
return HttpResponse::Unauthorized().body("You need to login to get an ADF problem.")
|
return HttpResponse::Unauthorized().body("You need to login to get an ADF problem.")
|
||||||
}
|
}
|
||||||
Some(Err(err)) => return HttpResponse::InternalServerError().body(err.to_string()),
|
Some(Err(err)) => return HttpResponse::InternalServerError().body(err.to_string()),
|
||||||
@ -717,7 +818,7 @@ async fn get_adf_problems_for_user(
|
|||||||
.collection(ADF_COLL);
|
.collection(ADF_COLL);
|
||||||
|
|
||||||
let username = match identity.map(|id| id.id()) {
|
let username = match identity.map(|id| id.id()) {
|
||||||
None => {
|
Option::None => {
|
||||||
return HttpResponse::Unauthorized().body("You need to login to get an ADF problem.")
|
return HttpResponse::Unauthorized().body("You need to login to get an ADF problem.")
|
||||||
}
|
}
|
||||||
Some(Err(err)) => return HttpResponse::InternalServerError().body(err.to_string()),
|
Some(Err(err)) => return HttpResponse::InternalServerError().body(err.to_string()),
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user