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
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
//! Fine-tuning parameter types.
//!
//! For options that take an integer value, should this value be negative, the
//! default will be used, if any.

// Idx and Real can be 32 or 64 bits. Make sure to suppress warnings when
// casts turn out to be trivial.
#![allow(trivial_numeric_casts)]

use crate::m;
use crate::Idx;

mod private {
    pub trait Sealed {}
}

/// Trait implemented by METIS' options.
///
/// See [`crate::Graph::set_options`] for an example.  It is also used in
/// [`crate::Mesh::set_options`].
pub trait Opt: private::Sealed {
    /// Index of the option in the array from [`crate::Graph::set_options`] and
    /// [`crate::Mesh::set_options`].
    const INDEX: usize;

    /// Convert the value into metis' format, for use with
    /// [`crate::Graph::set_options`] and [`crate::Mesh::set_options`].
    fn value(self) -> Idx;
}

/// Specifies the partitioning method.
pub enum PType {
    /// Multilevel recursive bisection.
    Rb,

    /// Multilevel k-way partitioning.
    Kway,
}

impl private::Sealed for PType {}
impl Opt for PType {
    const INDEX: usize = m::moptions_et_METIS_OPTION_PTYPE as usize;

    fn value(self) -> Idx {
        match self {
            PType::Rb => m::mptype_et_METIS_PTYPE_RB as Idx,
            PType::Kway => m::mptype_et_METIS_PTYPE_KWAY as Idx,
        }
    }
}

/// Specifies the type of objective.
pub enum ObjType {
    /// Edge-cut minimization.
    Cut,

    /// Total communication volume minimization.
    Vol,
}

impl private::Sealed for ObjType {}
impl Opt for ObjType {
    const INDEX: usize = m::moptions_et_METIS_OPTION_OBJTYPE as usize;

    fn value(self) -> Idx {
        match self {
            ObjType::Cut => m::mobjtype_et_METIS_OBJTYPE_CUT as Idx,
            ObjType::Vol => m::mobjtype_et_METIS_OBJTYPE_VOL as Idx,
        }
    }
}

/// Specifies the matching scheme to be used during coarsening.
pub enum CType {
    /// Random matching.
    Rm,

    /// Sorted heavy-edge matching.
    Shem,
}

impl private::Sealed for CType {}
impl Opt for CType {
    const INDEX: usize = m::moptions_et_METIS_OPTION_CTYPE as usize;

    fn value(self) -> Idx {
        match self {
            CType::Rm => m::mctype_et_METIS_CTYPE_RM as Idx,
            CType::Shem => m::mctype_et_METIS_CTYPE_SHEM as Idx,
        }
    }
}

/// Determines the algorithm used during initial partitioning.
pub enum IpType {
    /// Grows a bisection using a greedy strategy.
    Grow,

    /// Compute a bisection at random followed by a refinement.
    Random,

    /// Derives a separator from an edge cut.
    Edge,

    /// Grow a bisection using a greedy node-based strategy.
    Node,
}

impl private::Sealed for IpType {}
impl Opt for IpType {
    const INDEX: usize = m::moptions_et_METIS_OPTION_IPTYPE as usize;

    fn value(self) -> Idx {
        match self {
            IpType::Grow => m::miptype_et_METIS_IPTYPE_GROW as Idx,
            IpType::Random => m::miptype_et_METIS_IPTYPE_RANDOM as Idx,
            IpType::Edge => m::miptype_et_METIS_IPTYPE_EDGE as Idx,
            IpType::Node => m::miptype_et_METIS_IPTYPE_NODE as Idx,
        }
    }
}

/// Determines the algorithm used for refinement.
pub enum RType {
    /// FM-based cut refinement.
    Fm,

    /// Greedy-based cut and volume refinement.
    Greedy,

    /// Two-sided FM refinement.
    Sep2Sided,

    /// One-sided FM refinement.
    Sep1Sided,
}

impl private::Sealed for RType {}
impl Opt for RType {
    const INDEX: usize = m::moptions_et_METIS_OPTION_RTYPE as usize;

    fn value(self) -> Idx {
        match self {
            RType::Fm => m::mrtype_et_METIS_RTYPE_FM as Idx,
            RType::Greedy => m::mrtype_et_METIS_RTYPE_GREEDY as Idx,
            RType::Sep2Sided => m::mrtype_et_METIS_RTYPE_SEP2SIDED as Idx,
            RType::Sep1Sided => m::mrtype_et_METIS_RTYPE_SEP1SIDED as Idx,
        }
    }
}

/// Specifies the number of different partitions that it will compute. The
/// final partition is the one that achieves the best edge cut or
/// communication volume. Default is 1.
pub struct NCuts(pub Idx);

impl private::Sealed for NCuts {}
impl Opt for NCuts {
    const INDEX: usize = m::moptions_et_METIS_OPTION_NCUTS as usize;

    fn value(self) -> Idx {
        self.0
    }
}

/// Specifies the number of different separators that it will compute at each
/// level of nested dissection.
///
/// The final separator that is used is the smallest one. Default is 1.
pub struct NSeps(pub Idx);

impl private::Sealed for NSeps {}
impl Opt for NSeps {
    const INDEX: usize = m::moptions_et_METIS_OPTION_NSEPS as usize;

    fn value(self) -> Idx {
        self.0
    }
}

/// Used to indicate which numbering scheme is used for the adjacency structure
/// of a graph or the element-node structure of a mesh.
#[allow(dead_code)]
pub(crate) enum Numbering {
    /// C-style numbering which is assumed to start from 0.
    C,

    /// Fortran-style numbering which is assumed to start from 1.
    Fortran,
}

impl private::Sealed for Numbering {}
impl Opt for Numbering {
    const INDEX: usize = m::moptions_et_METIS_OPTION_NUMBERING as usize;

    fn value(self) -> Idx {
        match self {
            Numbering::C => 0,
            Numbering::Fortran => 1,
        }
    }
}

/// Specifies the number of iterations for the refinement algorithms at each
/// stage of the uncoarsening process.
///
/// Default is 10.
pub struct NIter(pub Idx);

impl private::Sealed for NIter {}
impl Opt for NIter {
    const INDEX: usize = m::moptions_et_METIS_OPTION_NITER as usize;

    fn value(self) -> Idx {
        self.0
    }
}

/// Specifies the seed for the random number generator.
pub struct Seed(pub Idx);

impl private::Sealed for Seed {}
impl Opt for Seed {
    const INDEX: usize = m::moptions_et_METIS_OPTION_SEED as usize;

    fn value(self) -> Idx {
        self.0
    }
}

/// Specifies that the partitioning routines should try to minimize the maximum
/// degree of the subdomain graph.
///
/// I.e., the graph in which each partition is a node, and edges connect
/// subdomains with a shared interface.
pub struct MinConn(pub bool);

impl private::Sealed for MinConn {}
impl Opt for MinConn {
    const INDEX: usize = m::moptions_et_METIS_OPTION_MINCONN as usize;

    fn value(self) -> Idx {
        self.0 as Idx
    }
}

/// Specifies that the coarsening will not perform any 2-hop matching when the
/// standards matching approach fails to sufficiently coarsen the graph.
///
/// The 2-hop matching is very effective for graphs with power-law degree
/// distributions.
pub struct No2Hop(pub bool);

impl private::Sealed for No2Hop {}
impl Opt for No2Hop {
    const INDEX: usize = m::moptions_et_METIS_OPTION_NO2HOP as usize;

    fn value(self) -> Idx {
        self.0 as Idx
    }
}

/// Specifies that the partitioning routines should try to produce partitions
/// that are contiguous.
///
/// Note that if the input graph is not connected this option is ignored.
pub struct Contig(pub bool);

impl private::Sealed for Contig {}
impl Opt for Contig {
    const INDEX: usize = m::moptions_et_METIS_OPTION_CONTIG as usize;

    fn value(self) -> Idx {
        self.0 as Idx
    }
}

/// Specifies that the graph should be compressed by combining vertices
/// that have identical adjacency lists.
pub struct Compress(pub bool);

impl private::Sealed for Compress {}
impl Opt for Compress {
    const INDEX: usize = m::moptions_et_METIS_OPTION_COMPRESS as usize;

    fn value(self) -> Idx {
        self.0 as Idx
    }
}

/// Specifies if the connected components of the graph should first be
/// identified and ordered separately.
pub struct CCOrder(pub bool);

impl private::Sealed for CCOrder {}
impl Opt for CCOrder {
    const INDEX: usize = m::moptions_et_METIS_OPTION_CCORDER as usize;

    fn value(self) -> Idx {
        self.0 as Idx
    }
}

/// Specifies the minimum degree of the vertices that will be ordered last.
///
/// If the specified value is `x > 0`, then any vertices with a degree greater
/// than `0.1*x*(average degree)` are removed from the graph, an ordering of the
/// rest of the vertices is computed, and an overall ordering is computed by
/// ordering the removed vertices at the end of the overall ordering.  For
/// example if `x == 40`, and the average degree is 5, then the algorithm will
/// remove all vertices with degree greater than 20. The vertices that are
/// removed are ordered last (i.e., they are automatically placed in the
/// top-level separator). Good values are often in the range of 60 to 200 (i.e.,
/// 6 to 20 times more than the average). Default value is 0, indicating that no
/// vertices are removed.
///
/// Used to control whether the ordering algorithm should remove any
/// vertices with high degree (i.e., dense columns). This is particularly
/// helpful for certain classes of LP matrices, in which there a few vertices
/// that are connected to many other vertices. By removing these vertices prior
/// to ordering, the quality and the amount of time required to do the ordering
/// improves.
pub struct PFactor(pub Idx);

impl private::Sealed for PFactor {}
impl Opt for PFactor {
    const INDEX: usize = m::moptions_et_METIS_OPTION_PFACTOR as usize;

    fn value(self) -> Idx {
        self.0
    }
}

/// Specifies the maximum allowed load imbalance among the partitions.
///
/// A value of `x` indicates that the allowed load imbalance is `(1 + x)/1000`.
/// The load imbalance for the `j`th constraint is defined to be
/// `max_i(w[j,i]/t[j,i])`, where `w[j,i]` is the fraction of the overall
/// weight of the `j`th constraint that is assigned to the`i`th partition and
/// `t[j,i]` is the desired target weight of the `j`th constraint for the `i`th
/// partition (i.e., that specified via `-tpwgts`). For `-ptype=rb`, the default
/// value is 1 (i.e., load imbalance of 1.001) and for `-ptype=kway`, the
/// default value is 30 (i.e., load imbalance of 1.03).
pub struct UFactor(pub Idx);

impl private::Sealed for UFactor {}
impl Opt for UFactor {
    const INDEX: usize = m::moptions_et_METIS_OPTION_UFACTOR as usize;

    fn value(self) -> Idx {
        self.0
    }
}

/// Specifies the amount of progress/debugging information will be printed
/// during the execution of the algorithms.
///
/// The default value is false for every field (no debugging/progress
/// information).
pub struct DbgLvl {
    /// Prints various diagnostic messages.
    pub info: bool,

    /// Performs timing analysis.
    pub time: bool,

    /// Displays various statistics during coarsening.
    pub coarsen: bool,

    /// Displays various statistics during refinement.
    pub refine: bool,

    /// Displays various statistics during initial partitioning.
    pub ipart: bool,

    /// Display detailed information about vertex moves during refinement.
    pub move_info: bool,

    /// Display detailed information about vertex separators.
    pub sep_info: bool,

    /// Display information related to the minimization of subdomain
    /// connectivity.
    pub conn_info: bool,

    /// Display information related to the elimination of connected components.
    pub contig_info: bool,
}

impl private::Sealed for DbgLvl {}
impl Opt for DbgLvl {
    const INDEX: usize = m::moptions_et_METIS_OPTION_DBGLVL as usize;

    fn value(self) -> Idx {
        let mut dbglvl = 0;
        if self.info {
            dbglvl |= 1;
        }
        if self.time {
            dbglvl |= 2;
        }
        if self.coarsen {
            dbglvl |= 4;
        }
        if self.refine {
            dbglvl |= 8;
        }
        if self.ipart {
            dbglvl |= 16;
        }
        if self.move_info {
            dbglvl |= 32;
        }
        if self.sep_info {
            dbglvl |= 64;
        }
        if self.conn_info {
            dbglvl |= 128;
        }
        if self.contig_info {
            dbglvl |= 256;
        }
        dbglvl
    }
}