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
//! Sentinel-index encoding and fixed solver configuration shared across
//! every `klu_native` submodule — mirrors `vendor/suitesparse/KLU/Include/
//! klu_internal.h`'s `EMPTY`/`FLIP`/`UNFLIP` (identical, by inspection, to
//! `AMD/Include/amd_internal.h`'s and `BTF/Include/btf.h`'s own
//! `BTF_FLIP`/`BTF_UNFLIP`/`BTF_ISFLIPPED`) and `KLU/Source/klu_defaults.c`'s
//! `KLU_common` defaults.
//!
//! Ported deliberately literally, not "idiomatized" into an `Option`/`enum`
//! at this layer — `lpivot`'s diagonal-preference bookkeeping (`kernel.rs`,
//! landing in a later phase) reads `Pinv[diagrow] < 0` directly and recovers
//! `FLIP(Pinv[pivrow])` to reroute it, and BTF's `Q` uses the same encoding
//! for unmatched columns. This is exactly the kind of index bookkeeping
//! where `block_sparse.rs`'s `adj.row`/`adj.col` bug happened — silently
//! wrong only on inputs a casual test wouldn't cover. Conversion to
//! `Option<usize>`/checked `Vec<usize>` happens only at each phase's own
//! public API boundary, once the sentinel-form algorithm is done with it.
/// Sentinel for "not yet assigned" — matches every one of the three ported
/// packages' own `#define EMPTY (-1)` (confirmed identical across
/// `KLU/Include/klu_internal.h:77`, `AMD/Include/amd_internal.h:71,93`, and
/// implicitly in `BTF/Include/btf.h`'s `BTF_ISFLIPPED`/`BTF_UNFLIP`, which
/// treat `-1` the same way).
pub const EMPTY: i64 = -1;
/// "Negation about -1": marks an otherwise-non-negative index as "flipped"
/// (BTF uses this for unmatched columns; KLU's `lpivot` uses it to encode a
/// row's earlier diagonal preference once that row has been claimed by a
/// different column). `flip(flip(j)) == j` for all `j`, and
/// `flip(EMPTY) == EMPTY`. Matches `klu_internal.h:78`'s `FLIP` /
/// `amd_internal.h:72`'s `FLIP` / `btf.h`'s `BTF_FLIP` exactly (all three
/// upstream definitions are identical: `-(j)-2`).
pub const
/// `true` iff `i` is currently in "flipped" form (`i < EMPTY`). Matches
/// `klu_internal.h`'s implicit `UNFLIP` condition / `btf.h`'s
/// `BTF_ISFLIPPED` exactly.
pub const
/// Recovers the original non-negative index whether or not `i` is currently
/// flipped — a no-op if it wasn't. Matches `klu_internal.h:79`'s `UNFLIP` /
/// `amd_internal.h:73`'s `UNFLIP` / `btf.h`'s `BTF_UNFLIP` exactly.
pub const
/// Fixed solver configuration, mirroring the specific `KLU_common` fields
/// `sparse_klu.rs::new_common()` (via `klu_defaults`) leaves at their
/// defaults — confirmed directly from `KLU/Source/klu_defaults.c` — and that
/// this port's algorithm actually consumes. Kept as a struct (not bare
/// constants) so differential tests (`#[cfg(all(test, feature = "klu"))]`,
/// comparing against the vendored C oracle) can vary `tol`/`scale` without
/// touching any production code path, which always uses `Options::default()`.
///
/// Two `KLU_common` fields are deliberately **absent** here, not just
/// defaulted, because gridoxide's call pattern makes the alternative
/// branches they'd select genuinely unreachable (confirmed dead, not merely
/// unused):
/// - `ordering`: real KLU supports AMD (0), COLAMD (1), user-supplied P/Q
/// (2), or a user callback (3). `klu_defaults.c` defaults to `0` (AMD) and
/// `sparse_klu.rs` never overrides it, so this port only ever implements
/// the AMD path — no `ordering` field exists to accidentally select
/// another one.
/// - `maxwork`: bounds the work `btf_maxtrans` may spend before giving up
/// with a possibly-incomplete matching. Defaults to `0` ("no limit") and
/// is never overridden, so this port's BTF implementation always runs
/// `btf_maxtrans` to completion — the work-limit bookkeeping threaded
/// through the upstream C is dropped outright, not merely defaulted off.