1#[cfg(feature = "prepare")]
2use crate::prepare::ArgumentType;
3use crate::{IntegerRelation, Value, ValueType, relations::RelationMode};
4use serde::{Deserialize, Deserializer, Serialize};
5
6#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Serialize)]
8#[serde(transparent)]
9pub struct FiniteFloat(f64);
10impl Eq for FiniteFloat {}
11impl FiniteFloat {
12 pub fn new(value: f64) -> Result<Self, &'static str> {
13 if !value.is_finite() {
14 return Err("float values must be finite");
15 }
16 Ok(Self(if value == 0.0 { 0.0 } else { value }))
17 }
18 pub fn get(self) -> f64 {
19 self.0
20 }
21}
22impl<'de> Deserialize<'de> for FiniteFloat {
23 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
24 Self::new(f64::deserialize(deserializer)?).map_err(serde::de::Error::custom)
25 }
26}
27impl std::fmt::Display for FiniteFloat {
28 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
29 write!(f, "{:?}", self.0)
30 }
31}
32
33#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize)]
35pub struct FloatTolerance {
36 absolute: FiniteFloat,
37 relative: FiniteFloat,
38}
39impl FloatTolerance {
40 pub fn new(absolute: f64, relative: f64) -> Result<Self, &'static str> {
41 if absolute < 0.0 || relative < 0.0 {
42 return Err("float tolerances must be nonnegative");
43 }
44 Ok(Self {
45 absolute: FiniteFloat::new(absolute)?,
46 relative: FiniteFloat::new(relative)?,
47 })
48 }
49 pub fn absolute(self) -> f64 {
50 self.absolute.get()
51 }
52 pub fn relative(self) -> f64 {
53 self.relative.get()
54 }
55 pub fn equal(self, left: FiniteFloat, right: FiniteFloat) -> bool {
56 let (a, b) = (left.get(), right.get());
57 if a == b {
58 return true;
59 }
60 let distance = (a - b).abs();
61 if distance.is_finite() && distance <= self.absolute() {
62 return true;
63 }
64 if self.relative() == 0.0 {
65 return false;
66 }
67 let scale = a.abs().max(b.abs());
68 let relative_distance = if distance.is_finite() {
69 distance / scale
70 } else {
71 (a * 0.5 - b * 0.5).abs() / (scale * 0.5)
72 };
73 relative_distance <= self.relative()
74 }
75 pub(crate) fn holds(
76 self,
77 relation: IntegerRelation,
78 left: FiniteFloat,
79 right: FiniteFloat,
80 ) -> bool {
81 use IntegerRelation::*;
82 let equal = self.equal(left, right);
83 match relation {
84 Equal => equal,
85 NotEqual => !equal,
86 Less => left < right && !equal,
87 LessEqual => left < right || equal,
88 Greater => left > right && !equal,
89 GreaterEqual => left > right || equal,
90 }
91 }
92 pub(crate) fn contains(
93 self,
94 value: FiniteFloat,
95 lower: FiniteFloat,
96 upper: FiniteFloat,
97 ) -> bool {
98 lower <= upper
99 && self.holds(IntegerRelation::LessEqual, lower, value)
100 && self.holds(IntegerRelation::LessEqual, value, upper)
101 }
102}
103impl Default for FloatTolerance {
104 fn default() -> Self {
105 Self::new(1e-9, 1e-9).expect("finite nonnegative defaults")
106 }
107}
108impl<'de> Deserialize<'de> for FloatTolerance {
109 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
110 #[derive(Deserialize)]
111 #[serde(deny_unknown_fields)]
112 struct Settings {
113 absolute: FiniteFloat,
114 relative: FiniteFloat,
115 }
116 let settings = Settings::deserialize(deserializer)?;
117 Self::new(settings.absolute.get(), settings.relative.get())
118 .map_err(serde::de::Error::custom)
119 }
120}
121
122#[derive(Clone, Debug, PartialEq, Eq, Serialize)]
124#[serde(transparent)]
125pub struct FloatSet(Vec<(FiniteFloat, FiniteFloat)>);
126impl FloatSet {
127 pub fn from_ranges(mut ranges: Vec<(FiniteFloat, FiniteFloat)>) -> Self {
128 ranges.retain(|(lower, upper)| lower <= upper);
129 ranges.sort_by(|a, b| a.0.partial_cmp(&b.0).expect("finite endpoints"));
130 let mut union: Vec<(FiniteFloat, FiniteFloat)> = Vec::new();
131 for (lower, upper) in ranges {
132 if let Some(last) = union.last_mut()
133 && lower <= last.1
134 {
135 if upper > last.1 {
136 last.1 = upper;
137 }
138 } else {
139 union.push((lower, upper));
140 }
141 }
142 Self(union)
143 }
144 pub fn ranges(&self) -> &[(FiniteFloat, FiniteFloat)] {
145 &self.0
146 }
147}
148impl<'de> Deserialize<'de> for FloatSet {
149 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
150 let ranges = Vec::<(FiniteFloat, FiniteFloat)>::deserialize(deserializer)?;
151 if ranges.iter().any(|(lower, upper)| lower > upper)
152 || ranges.windows(2).any(|pair| pair[0].1 >= pair[1].0)
153 {
154 return Err(serde::de::Error::custom(
155 "float set ranges must be ordered, nonoverlapping and not reversed",
156 ));
157 }
158 Ok(Self(ranges))
159 }
160}
161impl std::fmt::Display for FloatSet {
162 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
163 f.write_str("{")?;
164 for (index, (lower, upper)) in self.0.iter().enumerate() {
165 if index != 0 {
166 f.write_str(", ")?;
167 }
168 if lower == upper {
169 write!(f, "{lower}")?;
170 } else {
171 write!(f, "{lower}..{upper}")?;
172 }
173 }
174 f.write_str("}")
175 }
176}
177
178#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
179#[serde(rename_all = "snake_case")]
180pub enum FloatOperation {
181 Absolute,
182 Add,
183 Multiply,
184 Divide,
185 Minimum,
186 Maximum,
187 SquareRoot,
188 Power,
189 FixedBasePower,
190 Exponential,
191 NaturalLog,
192 Log10,
193 Log2,
194 Sine,
195 Cosine,
196 Tangent,
197 ArcSine,
198 ArcCosine,
199 ArcTangent,
200 HyperbolicSine,
201 HyperbolicCosine,
202 HyperbolicTangent,
203 InverseHyperbolicSine,
204 InverseHyperbolicCosine,
205 InverseHyperbolicTangent,
206}
207impl FloatOperation {
208 pub(crate) fn input_count(self) -> usize {
209 match self {
210 Self::Add
211 | Self::Multiply
212 | Self::Divide
213 | Self::Minimum
214 | Self::Maximum
215 | Self::Power
216 | Self::FixedBasePower => 2,
217 _ => 1,
218 }
219 }
220 pub(crate) fn calculate(self, inputs: &[FiniteFloat]) -> Result<FiniteFloat, &'static str> {
221 let x = inputs[0].get();
222 let result = match self {
223 Self::Absolute => x.abs(),
224 Self::Add => x + inputs[1].get(),
225 Self::Multiply => x * inputs[1].get(),
226 Self::Divide => {
227 let divisor = inputs[1].get();
228 if divisor == 0.0 {
229 return Err("division by zero in float arithmetic");
230 }
231 x / divisor
232 }
233 Self::Minimum => x.min(inputs[1].get()),
234 Self::Maximum => x.max(inputs[1].get()),
235 Self::SquareRoot => {
236 if x < 0.0 {
237 return Err("sqrt requires a nonnegative argument");
238 }
239 libm::sqrt(x)
240 }
241 Self::Power | Self::FixedBasePower => {
242 let exponent = inputs[1].get();
243 if self == Self::FixedBasePower && x <= 0.0 {
244 return Err("fixed-base power requires a positive base");
245 }
246 if x < 0.0 && exponent.trunc() != exponent {
247 return Err("negative float power base requires an integral exponent");
248 }
249 if x == 0.0 && exponent < 0.0 {
250 return Err("zero float power base requires a nonnegative exponent");
251 }
252 libm::pow(x, exponent)
253 }
254 Self::Exponential => libm::exp(x),
255 Self::NaturalLog | Self::Log10 | Self::Log2 => {
256 if x <= 0.0 {
257 return Err("logarithm requires a positive argument");
258 }
259 match self {
260 Self::NaturalLog => libm::log(x),
261 Self::Log10 => libm::log10(x),
262 _ => libm::log2(x),
263 }
264 }
265 Self::Sine => libm::sin(x),
266 Self::Cosine => libm::cos(x),
267 Self::Tangent => libm::tan(x),
268 Self::ArcSine | Self::ArcCosine => {
269 if !(-1.0..=1.0).contains(&x) {
270 return Err("inverse sine/cosine requires an argument in -1..1");
271 }
272 if self == Self::ArcSine {
273 libm::asin(x)
274 } else {
275 libm::acos(x)
276 }
277 }
278 Self::ArcTangent => libm::atan(x),
279 Self::HyperbolicSine => libm::sinh(x),
280 Self::HyperbolicCosine => libm::cosh(x),
281 Self::HyperbolicTangent => libm::tanh(x),
282 Self::InverseHyperbolicSine => libm::asinh(x),
283 Self::InverseHyperbolicCosine => {
284 if x < 1.0 {
285 return Err("acosh requires an argument at least one");
286 }
287 libm::acosh(x)
288 }
289 Self::InverseHyperbolicTangent => {
290 if x <= -1.0 || x >= 1.0 {
291 return Err("atanh requires an argument strictly between -1 and 1");
292 }
293 libm::atanh(x)
294 }
295 };
296 finite_result(result)
297 }
298}
299#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
301#[serde(rename_all = "snake_case")]
302pub enum FloatExtremum {
303 Minimum,
304 Maximum,
305}
306impl FloatExtremum {
307 pub(crate) fn calculate(self, inputs: &[FiniteFloat]) -> FiniteFloat {
308 inputs
309 .iter()
310 .copied()
311 .reduce(|left, right| match self {
312 Self::Minimum if right < left => right,
313 Self::Maximum if right > left => right,
314 _ => left,
315 })
316 .expect("validated nonempty float extremum")
317 }
318}
319
320pub(crate) fn validate_integer_exponent(exponent: i64) -> Result<(), &'static str> {
321 if !(0..=2147483646).contains(&exponent) {
322 return Err("Gecode float power exponent must be in 0..2147483646");
323 }
324 Ok(())
325}
326
327pub(crate) fn integer_power(
328 base: FiniteFloat,
329 mut exponent: i64,
330) -> Result<FiniteFloat, &'static str> {
331 if base.get() == 0.0 && exponent == 0 {
332 return Err("Gecode integer power excludes zero to the zero power");
333 }
334 let mut factor = base;
335 let mut result = FiniteFloat::new(1.0)?;
336 while exponent > 0 {
337 if exponent % 2 == 1 {
338 result = finite_result(result.get() * factor.get())?;
339 }
340 exponent /= 2;
341 if exponent > 0 {
342 factor = finite_result(factor.get() * factor.get())?;
343 }
344 }
345 Ok(result)
346}
347
348#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
349#[serde(rename_all = "snake_case")]
350pub enum FloatConversion {
351 IntegerToFloat,
352 Ceiling,
353 Floor,
354 Round,
355}
356impl FloatConversion {
357 pub(crate) fn input_type(self) -> ValueType {
358 if self == Self::IntegerToFloat {
359 ValueType::Integer
360 } else {
361 ValueType::Float
362 }
363 }
364 pub(crate) fn output_type(self) -> ValueType {
365 if self == Self::IntegerToFloat {
366 ValueType::Float
367 } else {
368 ValueType::Integer
369 }
370 }
371 pub(crate) fn calculate(self, input: &Value) -> Result<Value, &'static str> {
372 if self == Self::IntegerToFloat {
373 return Ok(Value::Float(FiniteFloat::new(input.integer() as f64)?));
374 }
375 let x = input.float().get();
376 let integer = match self {
377 Self::Ceiling => x.ceil(),
378 Self::Floor => x.floor(),
379 Self::Round => x.round(),
380 Self::IntegerToFloat => unreachable!(),
381 };
382 if !(-9223372036854775808.0..9223372036854775808.0).contains(&integer) {
383 return Err("float conversion result is outside i64 range");
384 }
385 Ok(Value::Integer(integer as i64))
386 }
387}
388#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
389#[serde(rename_all = "snake_case")]
390pub enum FloatMembership {
391 Interval,
392 Domain,
393 Set,
394}
395#[derive(Clone, Copy, Debug, PartialEq, Eq)]
396pub(crate) enum FloatName {
397 Relation {
398 relation: IntegerRelation,
399 linear: bool,
400 mode: RelationMode,
401 },
402 CodedImplication,
403 Arithmetic(FloatOperation),
404 IntegerPower,
405 ArrayExtremum {
406 extremum: FloatExtremum,
407 result_first: bool,
408 },
409 Conversion(FloatConversion),
410 Membership(FloatMembership),
411}
412impl FloatName {
413 pub fn from_name(name: &str) -> Option<Self> {
414 use IntegerRelation::*;
415 use RelationMode::*;
416 Some(match name {
417 "float_eq" | "gecode_float_eq" | "fzn_float_eq" => Self::Relation {
418 relation: Equal,
419 linear: false,
420 mode: Plain,
421 },
422 "float_eq_reif" | "gecode_float_eq_reif" | "fzn_float_eq_reif" => Self::Relation {
423 relation: Equal,
424 linear: false,
425 mode: Reified,
426 },
427 "float_eq_imp" => Self::Relation {
428 relation: Equal,
429 linear: false,
430 mode: Implied,
431 },
432 "float_ne" | "gecode_float_ne" | "fzn_float_ne" => Self::Relation {
433 relation: NotEqual,
434 linear: false,
435 mode: Plain,
436 },
437 "float_ne_reif" => Self::Relation {
438 relation: NotEqual,
439 linear: false,
440 mode: Reified,
441 },
442 "float_ne_imp" => Self::Relation {
443 relation: NotEqual,
444 linear: false,
445 mode: Implied,
446 },
447 "float_le" | "gecode_float_le" | "fzn_float_le" => Self::Relation {
448 relation: LessEqual,
449 linear: false,
450 mode: Plain,
451 },
452 "float_le_reif" | "gecode_float_le_reif" | "fzn_float_le_reif" => Self::Relation {
453 relation: LessEqual,
454 linear: false,
455 mode: Reified,
456 },
457 "float_le_imp" => Self::Relation {
458 relation: LessEqual,
459 linear: false,
460 mode: Implied,
461 },
462 "float_lt" | "gecode_float_lt" | "fzn_float_lt" => Self::Relation {
463 relation: Less,
464 linear: false,
465 mode: Plain,
466 },
467 "float_lt_reif" | "gecode_float_lt_reif" | "fzn_float_lt_reif" => Self::Relation {
468 relation: Less,
469 linear: false,
470 mode: Reified,
471 },
472 "float_lt_imp" => Self::Relation {
473 relation: Less,
474 linear: false,
475 mode: Implied,
476 },
477 "float_lin_eq" | "gecode_float_lin_eq" | "fzn_float_lin_eq" => Self::Relation {
478 relation: Equal,
479 linear: true,
480 mode: Plain,
481 },
482 "float_lin_eq_reif" | "gecode_float_lin_eq_reif" | "fzn_float_lin_eq_reif" => {
483 Self::Relation {
484 relation: Equal,
485 linear: true,
486 mode: Reified,
487 }
488 }
489 "float_lin_eq_imp" => Self::Relation {
490 relation: Equal,
491 linear: true,
492 mode: Implied,
493 },
494 "float_lin_ne" => Self::Relation {
495 relation: NotEqual,
496 linear: true,
497 mode: Plain,
498 },
499 "float_lin_ne_reif" => Self::Relation {
500 relation: NotEqual,
501 linear: true,
502 mode: Reified,
503 },
504 "float_lin_ne_imp" => Self::Relation {
505 relation: NotEqual,
506 linear: true,
507 mode: Implied,
508 },
509 "float_lin_le" | "gecode_float_lin_le" | "fzn_float_lin_le" => Self::Relation {
510 relation: LessEqual,
511 linear: true,
512 mode: Plain,
513 },
514 "float_lin_le_reif" | "gecode_float_lin_le_reif" | "fzn_float_lin_le_reif" => {
515 Self::Relation {
516 relation: LessEqual,
517 linear: true,
518 mode: Reified,
519 }
520 }
521 "float_lin_le_imp" => Self::Relation {
522 relation: LessEqual,
523 linear: true,
524 mode: Implied,
525 },
526 "float_lin_lt" | "gecode_float_lin_lt" | "fzn_float_lin_lt" => Self::Relation {
527 relation: Less,
528 linear: true,
529 mode: Plain,
530 },
531 "float_lin_lt_reif" | "gecode_float_lin_lt_reif" | "fzn_float_lin_lt_reif" => {
532 Self::Relation {
533 relation: Less,
534 linear: true,
535 mode: Reified,
536 }
537 }
538 "float_lin_lt_imp" => Self::Relation {
539 relation: Less,
540 linear: true,
541 mode: Implied,
542 },
543 "float_abs" | "gecode_float_abs" | "fzn_float_abs" => {
544 Self::Arithmetic(FloatOperation::Absolute)
545 }
546 "float_plus" | "gecode_float_plus" | "fzn_float_plus" => {
547 Self::Arithmetic(FloatOperation::Add)
548 }
549 "float_times" | "gecode_float_times" | "fzn_float_times" => {
550 Self::Arithmetic(FloatOperation::Multiply)
551 }
552 "float_div" | "gecode_float_div" | "fzn_float_div" => {
553 Self::Arithmetic(FloatOperation::Divide)
554 }
555 "float_min" | "gecode_float_min" | "fzn_float_min" => {
556 Self::Arithmetic(FloatOperation::Minimum)
557 }
558 "float_max" | "gecode_float_max" | "fzn_float_max" => {
559 Self::Arithmetic(FloatOperation::Maximum)
560 }
561 "float_sqrt" | "gecode_float_sqrt" | "fzn_float_sqrt" => {
562 Self::Arithmetic(FloatOperation::SquareRoot)
563 }
564 "float_acos" | "gecode_float_acos" | "fzn_float_acos" => {
565 Self::Arithmetic(FloatOperation::ArcCosine)
566 }
567 "float_asin" | "gecode_float_asin" | "fzn_float_asin" => {
568 Self::Arithmetic(FloatOperation::ArcSine)
569 }
570 "float_atan" | "gecode_float_atan" | "fzn_float_atan" => {
571 Self::Arithmetic(FloatOperation::ArcTangent)
572 }
573 "float_cos" | "gecode_float_cos" | "fzn_float_cos" => {
574 Self::Arithmetic(FloatOperation::Cosine)
575 }
576 "float_exp" | "gecode_float_exp" | "fzn_float_exp" => {
577 Self::Arithmetic(FloatOperation::Exponential)
578 }
579 "float_ln" | "gecode_float_ln" | "fzn_float_ln" => {
580 Self::Arithmetic(FloatOperation::NaturalLog)
581 }
582 "float_log10" | "gecode_float_log10" | "fzn_float_log10" => {
583 Self::Arithmetic(FloatOperation::Log10)
584 }
585 "float_log2" | "gecode_float_log2" | "fzn_float_log2" => {
586 Self::Arithmetic(FloatOperation::Log2)
587 }
588 "float_sin" | "gecode_float_sin" | "fzn_float_sin" => {
589 Self::Arithmetic(FloatOperation::Sine)
590 }
591 "float_tan" | "gecode_float_tan" | "fzn_float_tan" => {
592 Self::Arithmetic(FloatOperation::Tangent)
593 }
594 "float_acosh" => Self::Arithmetic(FloatOperation::InverseHyperbolicCosine),
595 "float_asinh" => Self::Arithmetic(FloatOperation::InverseHyperbolicSine),
596 "float_atanh" => Self::Arithmetic(FloatOperation::InverseHyperbolicTangent),
597 "float_cosh" => Self::Arithmetic(FloatOperation::HyperbolicCosine),
598 "float_sinh" => Self::Arithmetic(FloatOperation::HyperbolicSine),
599 "float_tanh" => Self::Arithmetic(FloatOperation::HyperbolicTangent),
600 "float_pow" => Self::Arithmetic(FloatOperation::Power),
601 "gecode_float_pow" | "gecode_gecode_float_pow" | "fzn_gecode_float_pow" => {
602 Self::IntegerPower
603 }
604 "gecode_float_pow_base"
605 | "gecode_gecode_float_pow_base"
606 | "fzn_gecode_float_pow_base" => Self::Arithmetic(FloatOperation::FixedBasePower),
607 "array_float_minimum" => Self::ArrayExtremum {
608 extremum: FloatExtremum::Minimum,
609 result_first: true,
610 },
611 "gecode_float_array_min"
612 | "gecode_gecode_float_array_min"
613 | "fzn_gecode_float_array_min" => Self::ArrayExtremum {
614 extremum: FloatExtremum::Minimum,
615 result_first: false,
616 },
617 "array_float_maximum" => Self::ArrayExtremum {
618 extremum: FloatExtremum::Maximum,
619 result_first: true,
620 },
621 "gecode_float_array_max"
622 | "gecode_gecode_float_array_max"
623 | "fzn_gecode_float_array_max" => Self::ArrayExtremum {
624 extremum: FloatExtremum::Maximum,
625 result_first: false,
626 },
627 "int2float" | "gecode_int2float" | "fzn_int2float" => {
628 Self::Conversion(FloatConversion::IntegerToFloat)
629 }
630 "float_ceil" => Self::Conversion(FloatConversion::Ceiling),
631 "float_floor" => Self::Conversion(FloatConversion::Floor),
632 "float_round" => Self::Conversion(FloatConversion::Round),
633 "float_in" => Self::Membership(FloatMembership::Interval),
634 "float_dom" => Self::Membership(FloatMembership::Domain),
635 "float_set_in" => Self::Membership(FloatMembership::Set),
636 "gecode_float_rel_imp_fzn"
637 | "gecode_gecode_float_rel_imp_fzn"
638 | "fzn_gecode_float_rel_imp_fzn" => Self::CodedImplication,
639 _ => return None,
640 })
641 }
642 #[cfg(feature = "prepare")]
643 pub fn signature(self) -> Vec<ArgumentType> {
644 let scalar = |kind, is_var| ArgumentType {
645 kind,
646 is_var,
647 is_array: false,
648 };
649 let array = |kind, is_var| ArgumentType {
650 kind,
651 is_var,
652 is_array: true,
653 };
654 match self {
655 Self::Relation { linear, mode, .. } => {
656 let mut args = if linear {
657 vec![
658 array(ValueType::Float, false),
659 array(ValueType::Float, true),
660 scalar(ValueType::Float, false),
661 ]
662 } else {
663 vec![scalar(ValueType::Float, true); 2]
664 };
665 if mode != RelationMode::Plain {
666 args.push(scalar(ValueType::Boolean, true));
667 }
668 args
669 }
670 Self::CodedImplication => vec![
671 scalar(ValueType::Float, true),
672 scalar(ValueType::Integer, false),
673 scalar(ValueType::Float, true),
674 scalar(ValueType::Boolean, true),
675 ],
676 Self::Arithmetic(operation) => {
677 let mut args = vec![scalar(ValueType::Float, true); operation.input_count() + 1];
678 if operation == FloatOperation::FixedBasePower {
679 args[0].is_var = false;
680 }
681 args
682 }
683 Self::IntegerPower => vec![
684 scalar(ValueType::Float, true),
685 scalar(ValueType::Integer, false),
686 scalar(ValueType::Float, true),
687 ],
688 Self::ArrayExtremum { result_first, .. } => {
689 let result = scalar(ValueType::Float, true);
690 let inputs = array(ValueType::Float, true);
691 if result_first {
692 vec![result, inputs]
693 } else {
694 vec![inputs, result]
695 }
696 }
697 Self::Conversion(operation) => vec![
698 scalar(operation.input_type(), true),
699 scalar(operation.output_type(), true),
700 ],
701 Self::Membership(FloatMembership::Interval) => vec![
702 scalar(ValueType::Float, true),
703 scalar(ValueType::Float, false),
704 scalar(ValueType::Float, false),
705 ],
706 Self::Membership(FloatMembership::Domain) => vec![
707 scalar(ValueType::Float, true),
708 array(ValueType::Float, false),
709 ],
710 Self::Membership(FloatMembership::Set) => vec![
711 scalar(ValueType::Float, true),
712 scalar(ValueType::FloatSet, false),
713 ],
714 }
715 }
716}
717pub(crate) fn finite_result(value: f64) -> Result<FiniteFloat, &'static str> {
718 FiniteFloat::new(value).map_err(|_| "nonfinite result in float arithmetic")
719}
720pub(crate) fn sum(
721 terms: impl IntoIterator<Item = (FiniteFloat, FiniteFloat)>,
722) -> Result<FiniteFloat, &'static str> {
723 let mut sum = 0.0;
724 for (coefficient, value) in terms {
725 let product = finite_result(coefficient.get() * value.get())?;
726 sum = finite_result(sum + product.get())?.get();
727 }
728 finite_result(sum)
729}