Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
79 changes: 79 additions & 0 deletions src/cli.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
//! This module defines the command line interface (CLI) for the application.

use clap::{Parser, ValueEnum};
use std::path::PathBuf;
use crate::solver;
use crate::nfa;

#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, ValueEnum)]
pub enum OutputFormat {
Plain,
Tex,
Csv,
}

#[derive(Parser, Debug)]
#[command(version, about, long_about = None)]
pub struct Args {
#[arg(value_name = "AUTOMATON_FILE", help = "Path to the input")]
pub filename: String,

#[arg(
short = 'f',
long = "from",
value_enum,
default_value = "tikz",
help = "The input format"
)]
pub input_format: nfa::InputFormat,

#[arg(
short = 'v',
long = "verbose",
action = clap::ArgAction::Count,
help = "Increase verbosity level"
)]
pub verbosity: u8,

#[arg(
long,
short = 'l',
value_name = "LOG_FILE",
help = "Optional path to the log file. Defaults to stdout if not specified."
)]
pub log_output: Option<PathBuf>,

#[arg(
value_enum,
short = 't',
long = "to",
default_value = "plain",
help = "The output format"
)]
pub output_format: OutputFormat,

#[arg(
short = 'o',
long = "output",
value_name = "OUTPUT_FILE",
help = "Where to write the strategy; defaults to stdout."
)]
pub output_path: Option<PathBuf>,

#[arg(
short,
long,
value_enum,
default_value = "input",
help = "The state reordering type."
)]
pub state_ordering: nfa::StateOrdering,

#[arg(
long,
value_enum,
default_value = "strategy",
help = "Solver output specification."
)]
pub solver_output: solver::SolverOutput,
}
38 changes: 38 additions & 0 deletions src/logging.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@

//! This module provides functionality for setting up logging

use env_logger::Builder;
use log::LevelFilter;
use std::fs::File;
use std::io::Write;
use std::path::PathBuf;

/// Sets up the logger based on verbosity and optional log file path.
pub fn setup_logger(verbosity: u8, log_output: Option<PathBuf>) {
let mut builder = Builder::from_default_env();
builder.format_timestamp(None);

builder.filter_level(match verbosity {
0 => LevelFilter::Warn,
1 => LevelFilter::Info,
2 => LevelFilter::Debug,
_ => LevelFilter::Trace,
});

if let Some(log_path) = log_output {
match File::create(&log_path) {
Ok(file) => {
let writer = Box::new(file) as Box<dyn Write + Send>;
builder.target(env_logger::Target::Pipe(writer));
}
Err(_) => {
eprintln!("Could not create log file at {}. Defaulting to stderr.", log_path.display());
builder.target(env_logger::Target::Stderr);
}
}
} else {
builder.target(env_logger::Target::Stderr);
}

builder.init();
}
86 changes: 12 additions & 74 deletions src/main.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
use clap::{Parser, ValueEnum};
use clap::Parser;
use std::fs::File;
use std::io;
use std::io::Write;
use std::path::PathBuf;
mod coef;
mod downset;
mod flow;
Expand All @@ -15,85 +14,24 @@ mod semigroup;
mod solution;
mod solver;
mod strategy;
use log::LevelFilter;
use log::info;
mod logging;
mod cli;

#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, ValueEnum)]
enum OutputFormat {
Plain,
Tex,
Csv,
}

#[derive(clap::Parser, Debug)]
#[command(version, about, long_about = None)]
struct Args {
#[arg(value_name = "AUTOMATON_FILE", help = "path to the input")]
filename: String,

#[arg(
short = 'f',
long = "from",
value_enum,
default_value = "tikz",
help = "The input format"
)]
input_format: nfa::InputFormat,

#[arg(
value_enum,
short = 't',
long = "to",
default_value = "plain",
help = "The output format"
)]
output_format: OutputFormat,

/// path to write the strategy
#[arg(
short = 'o',
long = "output",
value_name = "OUTPUT_FILE",
help = "where to write the strategy; defaults to stdout."
)]
output_path: Option<PathBuf>,

#[arg(
short,
long,
value_enum,
default_value = "input",
help = format!("The state reordering type: preserves input order, sorts alphabetically or topologically.")
)]
state_ordering: nfa::StateOrdering,

#[arg(
long,
value_enum,
default_value = "strategy",
help = format!("The solver output. Either yes/no and a winning strategy (the faster). Or the full maximal winning strategy.")
)]
solver_output: solver::SolverOutput,
}

fn main() {
#[cfg(debug_assertions)]
env_logger::Builder::new()
.filter_level(LevelFilter::Debug)
.init();

#[cfg(not(debug_assertions))]
env_logger::Builder::new()
.filter_level(LevelFilter::Info)
.init();

// parse CLI arguments
let args = Args::parse();
let args = cli::Args::parse();

// set up logging
logging::setup_logger(args.verbosity, args.log_output);

// parse the input file
let nfa = nfa::Nfa::load_from_file(&args.filename, &args.input_format, &args.state_ordering);

// print the input automaton
println!("{}", nfa);
info!("{}", nfa);

// compute the solution
let solution = solver::solve(&nfa, &args.solver_output);
Expand Down Expand Up @@ -135,20 +73,20 @@ fn main() {

// prepare output string
let output = match args.output_format {
OutputFormat::Tex => {
cli::OutputFormat::Tex => {
let is_tikz = args.input_format == nfa::InputFormat::Tikz;
let latex_content =
solution.as_latex(if is_tikz { Some(&args.filename) } else { None });
latex_content.to_string()
}
OutputFormat::Plain => {
cli::OutputFormat::Plain => {
format!(
"States: {}\n {}",
nfa.states_str(),
solution.winning_strategy
)
}
OutputFormat::Csv => {
cli::OutputFormat::Csv => {
format!(
"Σ, {}\n{}\n",
nfa.states().join(","),
Expand Down
7 changes: 3 additions & 4 deletions src/semigroup.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ use rayon::prelude::*;
use std::collections::HashSet; // for distinct method
use std::collections::VecDeque;
use std::fmt;
use std::io::{self, Write};

pub struct FlowSemigroup {
//invariant: all flows have the same dimension
Expand Down Expand Up @@ -194,8 +193,8 @@ impl FlowSemigroup {
let mut changed = false;
while !to_process_mult.is_empty() {
let flow = to_process_mult.pop_front().unwrap();
print!(".");
io::stdout().flush().unwrap();
//print!(".");
//io::stdout().flush().unwrap();
debug!("\nClose by product processing flow\n{}\n", flow);
/*if Self::is_covered(&flow, &processed) {
//debug!("Skipped inqueue\n{}", flow);
Expand Down Expand Up @@ -250,7 +249,7 @@ impl FlowSemigroup {
while !to_process_iter.is_empty() {
let flow = to_process_iter.pop_front().unwrap();
debug_assert!(flow.is_idempotent());
print!(".");
//print!(".");
debug!("\nClose by product processing flow\n{}\n", flow);
let iteration = flow.iteration();
if !Self::is_covered(&iteration, &self.flows) {
Expand Down
5 changes: 2 additions & 3 deletions src/strategy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ use crate::downset::DownSet;
use crate::graph::Graph;
use crate::ideal::Ideal;
use crate::nfa;
use std::io::{self, Write};

use std::collections::HashMap;
use std::fmt;
Expand Down Expand Up @@ -38,8 +37,8 @@ impl Strategy {
) -> bool {
let mut result = false;
for (a, downset) in self.0.iter_mut() {
print!(".");
io::stdout().flush().unwrap();
// print!(".");
//io::stdout().flush().unwrap();
let edges = edges_per_letter.get(a).unwrap();
let safe_pre_image = safe.safe_pre_image(edges, maximal_finite_value);
result |= downset.restrict_to(&safe_pre_image);
Expand Down