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
//! Constraint definitions and propagation traits for CSP/COP modeling.
//!
//! References:
//! - Rossi, F., van Beek, P., & Walsh, T. (2006). *Handbook of Constraint Programming*. Elsevier.
//! - van Hoeve, W. J., & Katriel, I. (2006). *Global Constraints*. Handbook of Constraint Programming, Chapter 6.
pub use AllDifferent;
pub use ;
pub use ;
pub use ;
pub use Equal;
pub use LessThanOrEqual;
pub use NoOverlap;
pub use NotEqual;
pub use Optional;
pub use Precedence;
use crate;
use crateVariableId;
use HashMap;
use Debug;
/// Result of a domain propagation step executed by a constraint.
/// Core interface for constraints in `unifier`.
///
/// Each constraint defines its variable scope, satisfaction checking logic,
/// and filtering/propagation rule.
/// Evaluates a binary comparator over two assigned variables.
///
/// Returns `true` if either variable is unassigned (a partial assignment does not yet
/// violate a not-yet-fully-known comparison), matching the "partial assignment is not
/// violating" convention used by binary comparison constraints.
///
/// # Complexity
/// Time & Space: O(1).
pub
/// Returns the `(min, max)` bounds of `var`'s domain for propagation, distinguishing an
/// untracked variable from an already-empty domain.
///
/// Returns `Err(Success { changed: false })` if `var` is not present in `domains` (nothing to
/// prune), or `Err(Conflict)` if its domain is already empty.
///
/// # Complexity
/// Time & Space: O(1).
pub
/// Returns the `(min, max)` bounds of `var`'s domain, or `None` if `var` is untracked or its
/// domain is empty. Intended for propagation loops that skip rather than abort on a missing
/// operand (e.g. global constraints iterating over a task list).
///
/// # Complexity
/// Time & Space: O(1).
pub
/// Applies `narrow` to `var`'s domain, tracking whether it removed any values in `changed` and
/// returning `Some(Conflict)` if the domain became empty. Returns `None` if `var` is untracked
/// or narrowing did not exhaust the domain, so the caller can continue.
///
/// # Complexity
/// Time & Space: O(1) plus the cost of `narrow`.
pub
/// Converts a `u64` duration to `i64` for arithmetic with `i64`-typed domain values, saturating
/// to `i64::MAX` instead of wrapping/panicking for durations exceeding `i64::MAX` (unrealistic in
/// practice, but not excluded by the `u64` type).
///
/// # Complexity
/// Time & Space: O(1).
pub
/// Checks whether any time window `[a, b)` containing a subset of tasks demands more total
/// `demand * duration` ("energy") than a resource of `capacity` can provide across that window —
/// a generalization of pairwise mandatory-part reasoning to sets of three or more tasks whose
/// individual pairwise overlaps don't reveal an infeasibility that their *combined* demand does
/// (see `plan/11-search-heuristics-and-global-constraints.md`, part D, for a worked example: 3
/// tasks with duration 2 each and starts free in `[0,3]` overload a unary resource only as a
/// triple, not as any pair).
///
/// `tasks` gives each task's `(est, lct, energy)`: earliest start, latest completion
/// (`domain_max + duration`), and `demand * duration` (or just `duration` for a unary resource
/// with implicit demand 1, e.g. [`crate::constraint::NoOverlap`]). For every candidate window
/// `[a, b)` — `a`/`b` drawn from the tasks' own `est`/`lct` values, since a tighter window can
/// only ever be bounded by an actual task edge — sums the energy of every task fully confined to
/// it (`est(task) >= a && lct(task) <= b`) and compares against `capacity * (b - a)`. If it's
/// larger, the window cannot accommodate the confined tasks: `Conflict` regardless of what the
/// rest of the schedule looks like.
///
/// This is the sound *detection* half of energetic reasoning / edge-finding — it never reports
/// an overload that isn't real (soundness proof: every confined task's *entire* domain-feasible
/// range lies within `[a, b)` by construction, so its whole duration's energy consumption must
/// fall inside the window regardless of how it's actually scheduled; total energy exceeding
/// `capacity * window` is then a necessary condition for infeasibility). It intentionally skips
/// the harder *update* half (tightening `est` bounds for tasks that must run after an
/// overloaded-adjacent set) — left as future work, see `plan/11-...md` part D.
///
/// # Complexity
/// Time: O(N^3) (N candidate `a` thresholds x N candidate `b` thresholds x O(N) to sum energy
/// per window) — a straightforward, easily-verified enumeration rather than the O(N log N)
/// Theta-tree formulation the literature uses for the full algorithm. Fine at the task-list sizes
/// scheduling constraints see in this crate's benchmark corpus; a production-scale (100s of
/// tasks) implementation would want the Theta-tree instead.
/// Space: O(N).
///
/// # Reference
/// Erschler, J., & Lopez, P. (1990). *Energy-based approaches for task scheduling under time and
/// resource constraints*. Baptiste, P., Le Pape, C., & Nuijten, W. (2001). *Constraint-Based
/// Scheduling*. Springer (edge-finding and energetic reasoning for unary/cumulative resources).
pub