1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
//! MacKay-Neal CLI subcommand
//!
//! This subcommand uses the MacKay-Neal pseudorandom construction to build
//! an LDPC parity check matrix. It runs the MacKay-Neal algorithm and,
//! if the construction is successful, prints to `stdout` the alist of the
//! parity check matrix. For more details about this construction, see
//! [`crate::mackay_neal`].
//!
//! # Examples
//! An r=1/2, n=16800 regular code with column weight 3 can be generated
//! with
//! ```shell
//! $ ldpc-toolbox mackay-neal 8100 16200 6 3 0 --uniform
//! ```
//! The `--uniform` parameter is useful when constructing regular codes
//! to prevent the construction from failing (see [`FillPolicy`]).
//!
//! A minimum graph girth can be enforced using the `--min_girth` and
//! `--girth_trials` parameters. For instance, a code of girth at least
//! 8 can be construced like so:
//! ```shell
//! $ ldpc-toolbox mackay-neal 8100 16200 6 3 0 --uniform \
//!       --min-girth 8 --girth-trials 1000
//! ```
//! This uses backtracking to try to find a construction that satisfies
//! the girth requirement.
//!
//! For high rate codes, the construction is less likely to suceed even if
//! backtracking is used. The `--search` parameter is useful to try several
//! seeds in parallel. It will print to `stderr` the seed that gave a successful
//! construction. For instance, an r=8/9 code with no 4-cycles can be
//! constructed with
//! ```shell
//! $ ldpc-toolbox mackay-neal 1800 16200 27 3 0 --uniform \
//!       --min-girth 6 --girth-trials 1000 --search
//! ```

use crate::cli::*;
use crate::mackay_neal::{Config, FillPolicy};
use clap::Parser;
use std::error::Error;

/// MacKay-Neal CLI arguments.
#[derive(Debug, Parser)]
#[command(about = "Generates LDPC codes using the MacKay-Neal algorithm")]
pub struct Args {
    /// Number of rows
    num_rows: usize,
    /// Number of columns
    num_columns: usize,
    /// Maximum row weight
    wr: usize,
    /// Column weight
    wc: usize,
    /// Seed
    seed: u64,
    /// Columns to backtrack
    #[structopt(long, default_value = "0")]
    backtrack_cols: usize,
    /// Backtrack attemps
    #[structopt(long, default_value = "0")]
    backtrack_trials: usize,
    /// Minimum girth
    #[structopt(long)]
    min_girth: Option<usize>,
    /// Girth trials
    #[structopt(long, default_value = "0")]
    girth_trials: usize,
    /// Use uniform fill policy
    #[structopt(long)]
    uniform: bool,
    /// Maximum seed trials
    #[structopt(long, default_value = "1000")]
    seed_trials: u64,
    /// Try several seeds in parallel
    #[structopt(long)]
    search: bool,
}

impl Args {
    fn config(&self) -> Config {
        Config {
            nrows: self.num_rows,
            ncols: self.num_columns,
            wr: self.wr,
            wc: self.wc,
            backtrack_cols: self.backtrack_cols,
            backtrack_trials: self.backtrack_trials,
            min_girth: self.min_girth,
            girth_trials: self.girth_trials,
            fill_policy: match self.uniform {
                true => FillPolicy::Uniform,
                false => FillPolicy::Random,
            },
        }
    }
}

impl Run for Args {
    fn run(&self) -> Result<(), Box<dyn Error>> {
        let conf = self.config();
        let h = if self.search {
            let (seed, hh) = conf
                .search(self.seed, self.seed_trials)
                .ok_or("no solution found")?;
            eprintln!("seed = {}", seed);
            hh
        } else {
            conf.run(self.seed)?
        };
        println!("{}", h.alist());
        Ok(())
    }
}