1 //! This crate contains an implementation of the Cassowary constraint solving algorithm, based upon the work by
2 //! G.J. Badros et al. in 2001. This algorithm is designed primarily for use constraining elements in user interfaces.
3 //! Constraints are linear combinations of the problem variables. The notable features of Cassowary that make it
4 //! ideal for user interfaces are that it is incremental (i.e. you can add and remove constraints at runtime
5 //! and it will perform the minimum work to update the result) and that the constraints can be violated if
6 //! necessary,
7 //! with the order in which they are violated specified by setting a "strength" for each constraint.
8 //! This allows the solution to gracefully degrade, which is useful for when a
9 //! user interface needs to compromise on its constraints in order to still be able to display something.
10 //!
11 //! ## Constraint syntax
12 //!
13 //! This crate aims to provide syntax for describing linear constraints as naturally as possible, within
14 //! the limitations of Rust's type system. Generally you can write constraints as you would naturally, however
15 //! the operator symbol (for greater-than, less-than, equals) is replaced with an instance of the
16 //! `WeightedRelation` enum wrapped in "pipe brackets".
17 //!
18 //! For example, for the constraint
19 //! `(a + b) * 2 + c >= d + 1` with strength `s`, the code to use is
20 //!
21 //! ```ignore
22 //! (a + b) * 2.0 + c |GE(s)| d + 1.0
23 //! ```
24 //!
25 //! # A simple example
26 //!
27 //! Imagine a layout consisting of two elements laid out horizontally. For small window widths the elements
28 //! should compress to fit, but if there is enough space they should display at their preferred widths. The
29 //! first element will align to the left, and the second to the right. For  this example we will ignore
30 //! vertical layout.
31 //!
32 //! First we need to include the relevant parts of `cassowary`:
33 //!
34 //! ```
35 //! use cassowary::{ Solver, Variable };
36 //! use cassowary::WeightedRelation::*;
37 //! use cassowary::strength::{ WEAK, MEDIUM, STRONG, REQUIRED };
38 //! ```
39 //!
40 //! And we'll construct some conveniences for pretty printing (which should hopefully be self-explanatory):
41 //!
42 //! ```ignore
43 //! use std::collections::HashMap;
44 //! let mut names = HashMap::new();
45 //! fn print_changes(names: &HashMap<Variable, &'static str>, changes: &[(Variable, f64)]) {
46 //!     println!("Changes:");
47 //!     for &(ref var, ref val) in changes {
48 //!         println!("{}: {}", names[var], val);
49 //!     }
50 //! }
51 //! ```
52 //!
53 //! Let's define the variables required - the left and right edges of the elements, and the width of the window.
54 //!
55 //! ```ignore
56 //! let window_width = Variable::new();
57 //! names.insert(window_width, "window_width");
58 //!
59 //! struct Element {
60 //!     left: Variable,
61 //!     right: Variable
62 //! }
63 //! let box1 = Element {
64 //!     left: Variable::new(),
65 //!     right: Variable::new()
66 //! };
67 //! names.insert(box1.left, "box1.left");
68 //! names.insert(box1.right, "box1.right");
69 //!
70 //! let box2 = Element {
71 //!     left: Variable::new(),
72 //!     right: Variable::new()
73 //! };
74 //! names.insert(box2.left, "box2.left");
75 //! names.insert(box2.right, "box2.right");
76 //! ```
77 //!
78 //! Now to set up the solver and constraints.
79 //!
80 //! ```ignore
81 //! let mut solver = Solver::new();
82 //! solver.add_constraints(&[window_width |GE(REQUIRED)| 0.0, // positive window width
83 //!                          box1.left |EQ(REQUIRED)| 0.0, // left align
84 //!                          box2.right |EQ(REQUIRED)| window_width, // right align
85 //!                          box2.left |GE(REQUIRED)| box1.right, // no overlap
86 //!                          // positive widths
87 //!                          box1.left |LE(REQUIRED)| box1.right,
88 //!                          box2.left |LE(REQUIRED)| box2.right,
89 //!                          // preferred widths:
90 //!                          box1.right - box1.left |EQ(WEAK)| 50.0,
91 //!                          box2.right - box2.left |EQ(WEAK)| 100.0]).unwrap();
92 //! ```
93 //!
94 //! The window width is currently free to take any positive value. Let's constrain it to a particular value.
95 //! Since for this example we will repeatedly change the window width, it is most efficient to use an
96 //! "edit variable", instead of repeatedly removing and adding constraints (note that for efficiency
97 //! reasons we cannot edit a normal constraint that has been added to the solver).
98 //!
99 //! ```ignore
100 //! solver.add_edit_variable(window_width, STRONG).unwrap();
101 //! solver.suggest_value(window_width, 300.0).unwrap();
102 //! ```
103 //!
104 //! This value of 300 is enough to fit both boxes in with room to spare, so let's check that this is the case.
105 //! We can fetch a list of changes to the values of variables in the solver. Using the pretty printer defined
106 //! earlier we can see what values our variables now hold.
107 //!
108 //! ```ignore
109 //! print_changes(&names, solver.fetch_changes());
110 //! ```
111 //!
112 //! This should print (in a possibly different order):
113 //!
114 //! ```ignore
115 //! Changes:
116 //! window_width: 300
117 //! box1.right: 50
118 //! box2.left: 200
119 //! box2.right: 300
120 //! ```
121 //!
122 //! Note that the value of `box1.left` is not mentioned. This is because `solver.fetch_changes` only lists
123 //! *changes* to variables, and since each variable starts in the solver with a value of zero, any values that
124 //! have not changed from zero will not be reported.
125 //!
126 //! Now let's try compressing the window so that the boxes can't take up their preferred widths.
127 //!
128 //! ```ignore
129 //! solver.suggest_value(window_width, 75.0);
130 //! print_changes(&names, solver.fetch_changes);
131 //! ```
132 //!
133 //! Now the solver can't satisfy all of the constraints. It will pick at least one of the weakest constraints to
134 //! violate. In this case it will be one or both of the preferred widths. For efficiency reasons this is picked
135 //! nondeterministically, so there are two possible results. This could be
136 //!
137 //! ```ignore
138 //! Changes:
139 //! window_width: 75
140 //! box1.right: 0
141 //! box2.left: 0
142 //! box2.right: 75
143 //! ```
144 //!
145 //! or
146 //!
147 //! ```ignore
148 //! Changes:
149 //! window_width: 75
150 //! box2.left: 50
151 //! box2.right: 75
152 //! ```
153 //!
154 //! Due to the nature of the algorithm, "in-between" solutions, although just as valid, are not picked.
155 //!
156 //! In a user interface this is not likely a result we would prefer. The solution is to add another constraint
157 //! to control the behaviour when the preferred widths cannot both be satisfied. In this example we are going
158 //! to constrain the boxes to try to maintain a ratio between their widths.
159 //!
160 //! ```
161 //! # use cassowary::{ Solver, Variable };
162 //! # use cassowary::WeightedRelation::*;
163 //! # use cassowary::strength::{ WEAK, MEDIUM, STRONG, REQUIRED };
164 //! #
165 //! # use std::collections::HashMap;
166 //! # let mut names = HashMap::new();
167 //! # fn print_changes(names: &HashMap<Variable, &'static str>, changes: &[(Variable, f64)]) {
168 //! #     println!("Changes:");
169 //! #     for &(ref var, ref val) in changes {
170 //! #         println!("{}: {}", names[var], val);
171 //! #     }
172 //! # }
173 //! #
174 //! # let window_width = Variable::new();
175 //! # names.insert(window_width, "window_width");
176 //! # struct Element {
177 //! #    left: Variable,
178 //! #    right: Variable
179 //! # }
180 //! # let box1 = Element {
181 //! #     left: Variable::new(),
182 //! #     right: Variable::new()
183 //! # };
184 //! # names.insert(box1.left, "box1.left");
185 //! # names.insert(box1.right, "box1.right");
186 //! # let box2 = Element {
187 //! #     left: Variable::new(),
188 //! #     right: Variable::new()
189 //! # };
190 //! # names.insert(box2.left, "box2.left");
191 //! # names.insert(box2.right, "box2.right");
192 //! # let mut solver = Solver::new();
193 //! # solver.add_constraints(&[window_width |GE(REQUIRED)| 0.0, // positive window width
194 //! #                          box1.left |EQ(REQUIRED)| 0.0, // left align
195 //! #                          box2.right |EQ(REQUIRED)| window_width, // right align
196 //! #                          box2.left |GE(REQUIRED)| box1.right, // no overlap
197 //! #                          // positive widths
198 //! #                          box1.left |LE(REQUIRED)| box1.right,
199 //! #                          box2.left |LE(REQUIRED)| box2.right,
200 //! #                          // preferred widths:
201 //! #                          box1.right - box1.left |EQ(WEAK)| 50.0,
202 //! #                          box2.right - box2.left |EQ(WEAK)| 100.0]).unwrap();
203 //! # solver.add_edit_variable(window_width, STRONG).unwrap();
204 //! # solver.suggest_value(window_width, 300.0).unwrap();
205 //! # print_changes(&names, solver.fetch_changes());
206 //! # solver.suggest_value(window_width, 75.0);
207 //! # print_changes(&names, solver.fetch_changes());
208 //! solver.add_constraint(
209 //!     (box1.right - box1.left) / 50.0 |EQ(MEDIUM)| (box2.right - box2.left) / 100.0
210 //!     ).unwrap();
211 //! print_changes(&names, solver.fetch_changes());
212 //! ```
213 //!
214 //! Now the result gives values that maintain the ratio between the sizes of the two boxes:
215 //!
216 //! ```ignore
217 //! Changes:
218 //! box1.right: 25
219 //! box2.left: 25
220 //! ```
221 //!
222 //! This example may have appeared somewhat contrived, but hopefully it shows the power of the cassowary
223 //! algorithm for laying out user interfaces.
224 //!
225 //! One thing that this example exposes is that this crate is a rather low level library. It does not have
226 //! any inherent knowledge of user interfaces, directions or boxes. Thus for use in a user interface this
227 //! crate should ideally be wrapped by a higher level API, which is outside the scope of this crate.
228 use std::sync::Arc;
229 use std::collections::HashMap;
230 use std::collections::hash_map::{Entry};
231 
232 mod solver_impl;
233 mod operators;
234 
235 static VARIABLE_ID: ::std::sync::atomic::AtomicUsize = ::std::sync::atomic::ATOMIC_USIZE_INIT;
236 
237 /// Identifies a variable for the constraint solver.
238 /// Each new variable is unique in the view of the solver, but copying or cloning the variable produces
239 /// a copy of the same variable.
240 #[derive(Copy, Clone, Hash, PartialEq, Eq, PartialOrd, Ord, Debug)]
241 pub struct Variable(usize);
242 
243 impl Variable {
244     /// Produces a new unique variable for use in constraint solving.
new() -> Variable245     pub fn new() -> Variable {
246         Variable(VARIABLE_ID.fetch_add(1, ::std::sync::atomic::Ordering::Relaxed))
247     }
248 }
249 
250 /// A variable and a coefficient to multiply that variable by. This is a sub-expression in
251 /// a constraint equation.
252 #[derive(Copy, Clone, Debug)]
253 pub struct Term {
254     pub variable: Variable,
255     pub coefficient: f64
256 }
257 
258 impl Term {
259     /// Construct a new Term from a variable and a coefficient.
new(variable: Variable, coefficient: f64) -> Term260     fn new(variable: Variable, coefficient: f64) -> Term {
261         Term {
262             variable: variable,
263             coefficient: coefficient
264         }
265     }
266 }
267 
268 /// An expression that can be the left hand or right hand side of a constraint equation.
269 /// It is a linear combination of variables, i.e. a sum of variables weighted by coefficients, plus an optional constant.
270 #[derive(Clone, Debug)]
271 pub struct Expression {
272     pub terms: Vec<Term>,
273     pub constant: f64
274 }
275 
276 impl Expression {
277     /// Constructs an expression of the form _n_, where n is a constant real number, not a variable.
from_constant(v: f64) -> Expression278     pub fn from_constant(v: f64) -> Expression {
279         Expression {
280             terms: Vec::new(),
281             constant: v
282         }
283     }
284     /// Constructs an expression from a single term. Forms an expression of the form _n x_
285     /// where n is the coefficient, and x is the variable.
from_term(term: Term) -> Expression286     pub fn from_term(term: Term) -> Expression {
287         Expression {
288             terms: vec![term],
289             constant: 0.0
290         }
291     }
292     /// General constructor. Each `Term` in `terms` is part of the sum forming the expression, as well as `constant`.
new(terms: Vec<Term>, constant: f64) -> Expression293     pub fn new(terms: Vec<Term>, constant: f64) -> Expression {
294         Expression {
295             terms: terms,
296             constant: constant
297         }
298     }
299     /// Mutates this expression by multiplying it by minus one.
negate(&mut self)300     pub fn negate(&mut self) {
301         self.constant = -self.constant;
302         for t in &mut self.terms {
303             *t = -*t;
304         }
305     }
306 }
307 
308 impl From<f64> for Expression {
from(v: f64) -> Expression309     fn from(v: f64) -> Expression {
310         Expression::from_constant(v)
311     }
312 }
313 
314 impl From<Variable> for Expression {
from(v: Variable) -> Expression315     fn from(v: Variable) -> Expression {
316         Expression::from_term(Term::new(v, 1.0))
317     }
318 }
319 
320 impl From<Term> for Expression {
from(t: Term) -> Expression321     fn from(t: Term) -> Expression {
322         Expression::from_term(t)
323     }
324 }
325 
326 /// Contains useful constants and functions for producing strengths for use in the constraint solver.
327 /// Each constraint added to the solver has an associated strength specifying the precedence the solver should
328 /// impose when choosing which constraints to enforce. It will try to enforce all constraints, but if that
329 /// is impossible the lowest strength constraints are the first to be violated.
330 ///
331 /// Strengths are simply real numbers. The strongest legal strength is 1,001,001,000.0. The weakest is 0.0.
332 /// For convenience constants are declared for commonly used strengths. These are `REQUIRED`, `STRONG`,
333 /// `MEDIUM` and `WEAK`. Feel free to multiply these by other values to get intermediate strengths.
334 /// Note that the solver will clip given strengths to the legal range.
335 ///
336 /// `REQUIRED` signifies a constraint that cannot be violated under any circumstance. Use this special strength
337 /// sparingly, as the solver will fail completely if it find that not all of the `REQUIRED` constraints
338 /// can be satisfied. The other strengths represent fallible constraints. These should be the most
339 /// commonly used strenghts for use cases where violating a constraint is acceptable or even desired.
340 ///
341 /// The solver will try to get as close to satisfying the constraints it violates as possible, strongest first.
342 /// This behaviour can be used (for example) to provide a "default" value for a variable should no other
343 /// stronger constraints be put upon it.
344 pub mod strength {
345     /// Create a constraint as a linear combination of STRONG, MEDIUM and WEAK strengths, corresponding to `a`
346     /// `b` and `c` respectively. The result is further multiplied by `w`.
create(a: f64, b: f64, c: f64, w: f64) -> f64347     pub fn create(a: f64, b: f64, c: f64, w: f64) -> f64 {
348         (a * w).max(0.0).min(1000.0) * 1_000_000.0 +
349             (b * w).max(0.0).min(1000.0) * 1000.0 +
350             (c * w).max(0.0).min(1000.0)
351     }
352     pub const REQUIRED: f64 = 1_001_001_000.0;
353     pub const STRONG: f64 = 1_000_000.0;
354     pub const MEDIUM: f64 = 1_000.0;
355     pub const WEAK: f64 = 1.0;
356 
357     /// Clips a strength value to the legal range
clip(s: f64) -> f64358     pub fn clip(s: f64) -> f64 {
359         s.min(REQUIRED).max(0.0)
360     }
361 }
362 
363 /// The possible relations that a constraint can specify.
364 #[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug)]
365 pub enum RelationalOperator {
366     /// `<=`
367     LessOrEqual,
368     /// `==`
369     Equal,
370     /// `>=`
371     GreaterOrEqual
372 }
373 
374 impl std::fmt::Display for RelationalOperator {
fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result375     fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
376         match *self {
377             RelationalOperator::LessOrEqual => write!(fmt, "<=") ?,
378             RelationalOperator::Equal => write!(fmt, "==") ?,
379             RelationalOperator::GreaterOrEqual => write!(fmt, ">=") ?,
380         };
381         Ok(())
382     }
383 }
384 
385 #[derive(Debug)]
386 struct ConstraintData {
387     expression: Expression,
388     strength: f64,
389     op: RelationalOperator
390 }
391 
392 /// A constraint, consisting of an equation governed by an expression and a relational operator,
393 /// and an associated strength.
394 #[derive(Clone, Debug)]
395 pub struct Constraint(Arc<ConstraintData>);
396 
397 impl Constraint {
398     /// Construct a new constraint from an expression, a relational operator and a strength.
399     /// This corresponds to the equation `e op 0.0`, e.g. `x + y >= 0.0`. For equations with a non-zero
400     /// right hand side, subtract it from the equation to give a zero right hand side.
new(e: Expression, op: RelationalOperator, strength: f64) -> Constraint401     pub fn new(e: Expression, op: RelationalOperator, strength: f64) -> Constraint {
402         Constraint(Arc::new(ConstraintData {
403             expression: e,
404             op: op,
405             strength: strength
406         }))
407     }
408     /// The expression of the left hand side of the constraint equation.
expr(&self) -> &Expression409     pub fn expr(&self) -> &Expression {
410         &self.0.expression
411     }
412     /// The relational operator governing the constraint.
op(&self) -> RelationalOperator413     pub fn op(&self) -> RelationalOperator {
414         self.0.op
415     }
416     /// The strength of the constraint that the solver will use.
strength(&self) -> f64417     pub fn strength(&self) -> f64 {
418         self.0.strength
419     }
420 }
421 
422 impl ::std::hash::Hash for Constraint {
hash<H: ::std::hash::Hasher>(&self, hasher: &mut H)423     fn hash<H: ::std::hash::Hasher>(&self, hasher: &mut H) {
424         use ::std::ops::Deref;
425         hasher.write_usize(self.0.deref() as *const _ as usize);
426     }
427 }
428 
429 impl PartialEq for Constraint {
eq(&self, other: &Constraint) -> bool430     fn eq(&self, other: &Constraint) -> bool {
431         use ::std::ops::Deref;
432         self.0.deref() as *const _ == other.0.deref() as *const _
433     }
434 }
435 
436 impl Eq for Constraint {}
437 
438 /// This is part of the syntactic sugar used for specifying constraints. This enum should be used as part of a
439 /// constraint expression. See the module documentation for more information.
440 pub enum WeightedRelation {
441     /// `==`
442     EQ(f64),
443     /// `<=`
444     LE(f64),
445     /// `>=`
446     GE(f64)
447 }
448 impl From<WeightedRelation> for (RelationalOperator, f64) {
from(r: WeightedRelation) -> (RelationalOperator, f64)449     fn from(r: WeightedRelation) -> (RelationalOperator, f64) {
450         use WeightedRelation::*;
451         match r {
452             EQ(s) => (RelationalOperator::Equal, s),
453             LE(s) => (RelationalOperator::LessOrEqual, s),
454             GE(s) => (RelationalOperator::GreaterOrEqual, s),
455         }
456     }
457 }
458 
459 /// This is an intermediate type used in the syntactic sugar for specifying constraints. You should not use it
460 /// directly.
461 pub struct PartialConstraint(Expression, WeightedRelation);
462 
463 #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
464 enum SymbolType {
465     Invalid,
466     External,
467     Slack,
468     Error,
469     Dummy
470 }
471 
472 #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
473 struct Symbol(usize, SymbolType);
474 
475 impl Symbol {
invalid() -> Symbol476     fn invalid() -> Symbol { Symbol(0, SymbolType::Invalid) }
type_(&self) -> SymbolType477     fn type_(&self) -> SymbolType { self.1 }
478 }
479 
480 #[derive(Clone)]
481 struct Row {
482     cells: HashMap<Symbol, f64>,
483     constant: f64
484 }
485 
near_zero(value: f64) -> bool486 fn near_zero(value: f64) -> bool {
487     const EPS: f64 = 1E-8;
488     if value < 0.0 {
489         -value < EPS
490     } else {
491         value < EPS
492     }
493 }
494 
495 impl Row {
new(constant: f64) -> Row496     fn new(constant: f64) -> Row {
497         Row {
498             cells: HashMap::new(),
499             constant: constant
500         }
501     }
add(&mut self, v: f64) -> f64502     fn add(&mut self, v: f64) -> f64 {
503         self.constant += v;
504         self.constant
505     }
insert_symbol(&mut self, s: Symbol, coefficient: f64)506     fn insert_symbol(&mut self, s: Symbol, coefficient: f64) {
507         match self.cells.entry(s) {
508             Entry::Vacant(entry) => if !near_zero(coefficient) {
509                 entry.insert(coefficient);
510             },
511             Entry::Occupied(mut entry) => {
512                 *entry.get_mut() += coefficient;
513                 if near_zero(*entry.get_mut()) {
514                     entry.remove();
515                 }
516             }
517         }
518     }
519 
insert_row(&mut self, other: &Row, coefficient: f64) -> bool520     fn insert_row(&mut self, other: &Row, coefficient: f64) -> bool {
521         let constant_diff = other.constant * coefficient;
522         self.constant += constant_diff;
523         for (s, v) in &other.cells {
524             self.insert_symbol(*s, v * coefficient);
525         }
526         constant_diff != 0.0
527     }
528 
remove(&mut self, s: Symbol)529     fn remove(&mut self, s: Symbol) {
530         self.cells.remove(&s);
531     }
532 
reverse_sign(&mut self)533     fn reverse_sign(&mut self) {
534         self.constant = -self.constant;
535         for (_, v) in &mut self.cells {
536             *v = -*v;
537         }
538     }
539 
solve_for_symbol(&mut self, s: Symbol)540     fn solve_for_symbol(&mut self, s: Symbol) {
541         let coeff = -1.0 / match self.cells.entry(s) {
542             Entry::Occupied(entry) => entry.remove(),
543             Entry::Vacant(_) => unreachable!()
544         };
545         self.constant *= coeff;
546         for (_, v) in &mut self.cells {
547             *v *= coeff;
548         }
549     }
550 
solve_for_symbols(&mut self, lhs: Symbol, rhs: Symbol)551     fn solve_for_symbols(&mut self, lhs: Symbol, rhs: Symbol) {
552         self.insert_symbol(lhs, -1.0);
553         self.solve_for_symbol(rhs);
554     }
555 
coefficient_for(&self, s: Symbol) -> f64556     fn coefficient_for(&self, s: Symbol) -> f64 {
557         self.cells.get(&s).cloned().unwrap_or(0.0)
558     }
559 
substitute(&mut self, s: Symbol, row: &Row) -> bool560     fn substitute(&mut self, s: Symbol, row: &Row) -> bool {
561         if let Some(coeff) = self.cells.remove(&s) {
562             self.insert_row(row, coeff)
563         } else {
564             false
565         }
566     }
567 }
568 
569 /// The possible error conditions that `Solver::add_constraint` can fail with.
570 #[derive(Debug, Copy, Clone)]
571 pub enum AddConstraintError {
572     /// The constraint specified has already been added to the solver.
573     DuplicateConstraint,
574     /// The constraint is required, but it is unsatisfiable in conjunction with the existing constraints.
575     UnsatisfiableConstraint,
576     /// The solver entered an invalid state. If this occurs please report the issue. This variant specifies
577     /// additional details as a string.
578     InternalSolverError(&'static str)
579 }
580 
581 /// The possible error conditions that `Solver::remove_constraint` can fail with.
582 #[derive(Debug, Copy, Clone)]
583 pub enum RemoveConstraintError {
584     /// The constraint specified was not already in the solver, so cannot be removed.
585     UnknownConstraint,
586     /// The solver entered an invalid state. If this occurs please report the issue. This variant specifies
587     /// additional details as a string.
588     InternalSolverError(&'static str)
589 }
590 
591 /// The possible error conditions that `Solver::add_edit_variable` can fail with.
592 #[derive(Debug, Copy, Clone)]
593 pub enum AddEditVariableError {
594     /// The specified variable is already marked as an edit variable in the solver.
595     DuplicateEditVariable,
596     /// The specified strength was `REQUIRED`. This is illegal for edit variable strengths.
597     BadRequiredStrength
598 }
599 
600 /// The possible error conditions that `Solver::remove_edit_variable` can fail with.
601 #[derive(Debug, Copy, Clone)]
602 pub enum RemoveEditVariableError {
603     /// The specified variable was not an edit variable in the solver, so cannot be removed.
604     UnknownEditVariable,
605     /// The solver entered an invalid state. If this occurs please report the issue. This variant specifies
606     /// additional details as a string.
607     InternalSolverError(&'static str)
608 }
609 
610 /// The possible error conditions that `Solver::suggest_value` can fail with.
611 #[derive(Debug, Copy, Clone)]
612 pub enum SuggestValueError {
613     /// The specified variable was not an edit variable in the solver, so cannot have its value suggested.
614     UnknownEditVariable,
615     /// The solver entered an invalid state. If this occurs please report the issue. This variant specifies
616     /// additional details as a string.
617     InternalSolverError(&'static str)
618 }
619 
620 #[derive(Debug, Copy, Clone)]
621 struct InternalSolverError(&'static str);
622 
623 pub use solver_impl::Solver;
624