1use std::cmp::Ordering;
16use std::fmt;
17use std::sync::Arc;
18
19use logicaffeine_base::algebraic::Algebraic;
20use logicaffeine_base::exact_real::ExactReal;
21use logicaffeine_base::limit_value::Converging;
22use logicaffeine_base::numeric::{
23 cmp_bigint_f64_exact, cmp_i64_f64_exact, cmp_rational_f64_exact, float_max, float_min,
24 rational_from_f64_exact, BigInt, Decimal, Rational,
25};
26use logicaffeine_base::ordinal::Ordinal;
27use logicaffeine_base::tower::{
28 self, DependsValue, ExactRef, HyperValue, NaturalValue, Op, Rounding, Shape, TowerValue,
29 ValueAlgebra,
30};
31
32pub use logicaffeine_base::tower::Rel;
34
35use crate::failure::{answered, index_out_of_bounds, DIVISION_BY_ZERO, MODULO_BY_ZERO};
36
37pub use logicaffeine_base::extension::Builtin as ExtensionBuiltin;
39
40pub fn logos_extension(builtin: ExtensionBuiltin, args: Vec<LogosValue>) -> LogosValue {
43 answered(logicaffeine_base::extension::call(builtin, args))
44}
45
46type Integer = BigInt;
48type Ratio = Rational;
49
50#[derive(Clone, Debug)]
52pub enum LogosValue {
53 Int(i64),
54 BigInt(Arc<BigInt>),
56 Rational(Arc<Rational>),
58 Float(f64),
59 Decimal(Arc<Decimal>),
60 Bool(bool),
61 Text(Arc<str>),
62 Nothing,
63 List(Arc<Vec<LogosValue>>),
64 Tuple(Arc<Vec<LogosValue>>),
65 Algebraic(Arc<Algebraic>),
66 ExactReal(Arc<ExactReal>),
67 Hyperreal(Arc<HyperValue>),
68 Hypernatural(Arc<NaturalValue>),
69 Ordinal(Arc<Ordinal>),
70 Depends(Arc<DependsValue<LogosValue>>),
71 Converging(Arc<Converging<LogosValue>>),
72 Other(Arc<dyn OtherValue>),
75}
76
77pub trait OtherValue: fmt::Debug + Send + Sync + 'static {
82 fn type_name(&self) -> &str;
84 fn display_string(&self) -> String;
86 fn as_any(&self) -> &dyn std::any::Any;
88 fn equals(&self, o: &dyn OtherValue) -> bool;
90 fn order(&self, o: &LogosValue) -> Option<Option<Ordering>>;
92 fn combine(&self, op: Op, o: &LogosValue, reversed: bool) -> Option<Result<LogosValue, String>>;
94}
95
96impl LogosValue {
97 pub fn from_bigint(b: BigInt) -> LogosValue {
99 match b.to_i64() {
100 Some(n) => LogosValue::Int(n),
101 None => LogosValue::BigInt(Arc::new(b)),
102 }
103 }
104
105 pub fn from_rational(r: Rational) -> LogosValue {
107 if r.is_integer() {
108 return LogosValue::from_bigint(r.to_bigint().expect("a whole rational is an integer"));
109 }
110 LogosValue::Rational(Arc::new(r))
111 }
112
113 pub fn text(t: &str) -> LogosValue {
115 LogosValue::Text(Arc::from(t))
116 }
117
118 pub fn list(items: Vec<LogosValue>) -> LogosValue {
120 LogosValue::List(Arc::new(items))
121 }
122
123 pub fn other(v: impl OtherValue) -> LogosValue {
125 LogosValue::Other(Arc::new(v))
126 }
127
128 pub fn other_as<T: OtherValue>(&self) -> Option<&T> {
130 match self {
131 LogosValue::Other(v) => v.as_any().downcast_ref::<T>(),
132 _ => None,
133 }
134 }
135
136 fn other_arith(&self, op: Op, o: &LogosValue) -> Option<Result<LogosValue, String>> {
138 if let LogosValue::Other(a) = self {
139 if let Some(r) = a.combine(op, o, false) {
140 return Some(r);
141 }
142 }
143 match o {
144 LogosValue::Other(b) => b.combine(op, self, true),
145 _ => None,
146 }
147 }
148
149 pub fn infinitesimal(name: &str, modulus: i64, allowed: &[i64]) -> Result<LogosValue, String> {
152 let allowed: Vec<u64> = allowed.iter().map(|r| *r as u64).collect();
153 tower::new_infinitesimal(name, modulus as u64, &allowed)
154 }
155
156 pub fn type_name(&self) -> &str {
158 match self {
159 LogosValue::Int(_) | LogosValue::BigInt(_) => "Int",
160 LogosValue::Rational(_) => "Rational",
161 LogosValue::Float(_) => "Float",
162 LogosValue::Decimal(_) => "Decimal",
163 LogosValue::Bool(_) => "Bool",
164 LogosValue::Text(_) => "Text",
165 LogosValue::Nothing => "Nothing",
166 LogosValue::List(_) => "List",
167 LogosValue::Tuple(_) => "Tuple",
168 LogosValue::Algebraic(_) => "Algebraic",
169 LogosValue::ExactReal(_) => "ExactReal",
170 LogosValue::Hyperreal(_) => "Hyperreal",
171 LogosValue::Hypernatural(_) => "Hypernatural",
172 LogosValue::Ordinal(_) => "Ordinal",
173 LogosValue::Depends(_) => "Depends",
174 LogosValue::Converging(_) => "Converging",
175 LogosValue::Other(v) => v.type_name(),
176 }
177 }
178
179 pub fn display_string(&self) -> String {
181 match self {
182 LogosValue::Int(n) => n.to_string(),
183 LogosValue::BigInt(b) => b.to_string(),
184 LogosValue::Rational(r) => r.to_string(),
185 LogosValue::Float(f) => crate::fmt::fmt_f64(*f),
186 LogosValue::Decimal(d) => d.to_string(),
187 LogosValue::Bool(b) => if *b { "true" } else { "false" }.to_string(),
188 LogosValue::Text(t) => t.to_string(),
189 LogosValue::Nothing => "nothing".to_string(),
190 LogosValue::List(items) => format!(
191 "[{}]",
192 items.iter().map(LogosValue::display_string).collect::<Vec<_>>().join(", ")
193 ),
194 LogosValue::Tuple(items) => format!(
195 "({})",
196 items.iter().map(LogosValue::display_string).collect::<Vec<_>>().join(", ")
197 ),
198 LogosValue::Other(v) => v.display_string(),
199 exact => tower::display(exact).expect("an exact value displays"),
200 }
201 }
202
203 pub fn decided_truth(&self) -> Result<bool, String> {
206 if let Some(why) = tower::undecided_branch(self) {
207 return Err(why);
208 }
209 Ok(self.is_truthy())
210 }
211
212 fn is_truthy(&self) -> bool {
213 match self {
214 LogosValue::Bool(b) => *b,
215 LogosValue::Int(n) => *n != 0,
216 LogosValue::Float(f) => *f != 0.0,
217 LogosValue::BigInt(b) => !b.is_zero(),
218 LogosValue::Rational(r) => !r.is_zero(),
219 LogosValue::Decimal(d) => !d.is_zero(),
220 LogosValue::Text(t) => !t.is_empty(),
221 LogosValue::List(items) => !items.is_empty(),
222 LogosValue::Nothing => false,
223 _ => true,
224 }
225 }
226
227 fn big(&self) -> Option<BigInt> {
228 match self {
229 LogosValue::Int(n) => Some(BigInt::from_i64(*n)),
230 LogosValue::BigInt(b) => Some((**b).clone()),
231 _ => None,
232 }
233 }
234
235 fn rat(&self) -> Option<Rational> {
236 match self {
237 LogosValue::Int(n) => Some(Rational::from_i64(*n)),
238 LogosValue::BigInt(b) => Some(Rational::from_bigint((**b).clone())),
239 LogosValue::Rational(r) => Some((**r).clone()),
240 LogosValue::Decimal(d) => Some(d.to_rational()),
241 _ => None,
242 }
243 }
244
245 fn dec(&self) -> Option<Decimal> {
246 match self {
247 LogosValue::Int(n) => Some(Decimal::from_i64(*n)),
248 LogosValue::BigInt(b) => Some(Decimal::from_bigint((**b).clone())),
249 LogosValue::Decimal(d) => Some((**d).clone()),
250 _ => None,
251 }
252 }
253
254 fn f64(&self) -> Option<f64> {
255 match self {
256 LogosValue::Int(n) => Some(*n as f64),
257 LogosValue::BigInt(b) => Some(b.to_f64()),
258 LogosValue::Rational(r) => Some(r.to_f64()),
259 LogosValue::Decimal(d) => Some(d.to_rational().to_f64()),
260 LogosValue::Float(f) => Some(*f),
261 _ => None,
262 }
263 }
264
265 fn is_decimal(&self) -> bool {
266 matches!(self, LogosValue::Decimal(_))
267 }
268
269 fn is_float(&self) -> bool {
270 matches!(self, LogosValue::Float(_))
271 }
272
273 fn decimal_op(
275 &self,
276 o: &LogosValue,
277 dec: impl Fn(&Decimal, &Decimal) -> Decimal,
278 rat: impl Fn(&Rational, &Rational) -> Rational,
279 flt: impl Fn(f64, f64) -> f64,
280 ) -> Option<LogosValue> {
281 if !self.is_decimal() && !o.is_decimal() {
282 return None;
283 }
284 if let (Some(a), Some(b)) = (self.dec(), o.dec()) {
285 return Some(LogosValue::Decimal(Arc::new(dec(&a, &b))));
286 }
287 if let (Some(a), Some(b)) = (self.rat(), o.rat()) {
288 return Some(LogosValue::from_rational(rat(&a, &b)));
289 }
290 Some(LogosValue::Float(flt(self.f64()?, o.f64()?)))
291 }
292
293 fn tower_op(
296 &self,
297 o: &LogosValue,
298 int: impl Fn(i64, i64) -> Option<i64>,
299 big: impl Fn(&BigInt, &BigInt) -> BigInt,
300 rat: impl Fn(&Rational, &Rational) -> Rational,
301 flt: impl Fn(f64, f64) -> f64,
302 ) -> Option<LogosValue> {
303 use LogosValue::*;
304 Some(match (self, o) {
305 (Int(a), Int(b)) => match int(*a, *b) {
306 Some(n) => Int(n),
307 None => LogosValue::from_bigint(big(&Integer::from_i64(*a), &Integer::from_i64(*b))),
308 },
309 (Int(_) | BigInt(_), Int(_) | BigInt(_)) => {
310 LogosValue::from_bigint(big(&self.big()?, &o.big()?))
311 }
312 (Float(_), Int(_) | BigInt(_) | Float(_) | Rational(_))
313 | (Int(_) | BigInt(_) | Rational(_), Float(_)) => Float(flt(self.f64()?, o.f64()?)),
314 (Rational(_), Int(_) | BigInt(_) | Rational(_)) | (Int(_) | BigInt(_), Rational(_)) => {
315 LogosValue::from_rational(rat(&self.rat()?, &o.rat()?))
316 }
317 _ => return None,
318 })
319 }
320
321 fn arith(op: Op, a: LogosValue, b: LogosValue) -> Result<LogosValue, String> {
322 match op {
323 Op::Add => a.add(&b),
324 Op::Sub => a.sub(&b),
325 Op::Mul => a.mul(&b),
326 Op::Div => a.div(&b),
327 Op::ExactDiv => a.exact_div(&b),
328 Op::FloorDiv => a.floor_div(&b),
329 Op::Mod => a.rem(&b),
330 Op::Pow => a.pow(&b),
331 }
332 }
333
334 pub fn add(&self, o: &LogosValue) -> Result<LogosValue, String> {
336 if let Some(r) = tower::arith(Op::Add, self, o, &LogosValue::arith) {
337 return r;
338 }
339 if let Some(r) = self.decimal_op(o, |a, b| a.add(b), |a, b| a.add(b), |a, b| a + b) {
340 return Ok(r);
341 }
342 if let Some(r) = self.tower_op(o, i64::checked_add, BigInt::add, Rational::add, |a, b| a + b) {
343 return Ok(r);
344 }
345 if let Some(r) = self.other_arith(Op::Add, o) {
346 return r;
347 }
348 match (self, o) {
349 (LogosValue::Text(a), LogosValue::Text(b)) => Ok(LogosValue::text(&format!("{a}{b}"))),
350 (LogosValue::Text(a), other) => Ok(LogosValue::text(&format!("{a}{}", other.display_string()))),
351 (other, LogosValue::Text(b)) => Ok(LogosValue::text(&format!("{}{b}", other.display_string()))),
352 (LogosValue::List(a), LogosValue::List(b)) => {
353 Ok(LogosValue::list(a.iter().chain(b.iter()).cloned().collect()))
354 }
355 _ => Err(format!("Cannot add {} and {}", self.type_name(), o.type_name())),
356 }
357 }
358
359 pub fn sub(&self, o: &LogosValue) -> Result<LogosValue, String> {
361 if let Some(r) = tower::arith(Op::Sub, self, o, &LogosValue::arith) {
362 return r;
363 }
364 if let Some(r) = self.decimal_op(o, |a, b| a.sub(b), |a, b| a.sub(b), |a, b| a - b) {
365 return Ok(r);
366 }
367 if let Some(r) = self.tower_op(o, i64::checked_sub, BigInt::sub, Rational::sub, |a, b| a - b) {
368 return Ok(r);
369 }
370 if let Some(r) = self.other_arith(Op::Sub, o) {
371 return r;
372 }
373 Err(format!("Cannot subtract {} from {}", o.type_name(), self.type_name()))
374 }
375
376 pub fn mul(&self, o: &LogosValue) -> Result<LogosValue, String> {
378 if let Some(r) = tower::arith(Op::Mul, self, o, &LogosValue::arith) {
379 return r;
380 }
381 if let Some(r) = self.decimal_op(o, |a, b| a.mul(b), |a, b| a.mul(b), |a, b| a * b) {
382 return Ok(r);
383 }
384 if let Some(r) = self.tower_op(o, i64::checked_mul, BigInt::mul, Rational::mul, |a, b| a * b) {
385 return Ok(r);
386 }
387 match (self, o) {
388 (LogosValue::List(items), LogosValue::Int(n)) | (LogosValue::Int(n), LogosValue::List(items)) => {
389 let count = (*n).max(0) as usize;
390 let mut out = Vec::with_capacity(items.len() * count);
391 for _ in 0..count {
392 out.extend(items.iter().cloned());
393 }
394 Ok(LogosValue::list(out))
395 }
396 _ => Err(format!("Cannot multiply {} and {}", self.type_name(), o.type_name())),
397 }
398 }
399
400 pub fn div(&self, o: &LogosValue) -> Result<LogosValue, String> {
403 use LogosValue::*;
404 if let Some(r) = tower::arith(Op::Div, self, o, &LogosValue::arith) {
405 return r;
406 }
407 if self.is_decimal() || o.is_decimal() {
408 if let (Some(a), Some(b)) = (self.rat(), o.rat()) {
409 return a
410 .div(&b)
411 .map(LogosValue::from_rational)
412 .ok_or_else(|| DIVISION_BY_ZERO.to_string());
413 }
414 if let (Some(a), Some(b)) = (self.f64(), o.f64()) {
415 if b == 0.0 {
416 return Err(DIVISION_BY_ZERO.to_string());
417 }
418 return Ok(Float(a / b));
419 }
420 }
421 match (self, o) {
422 (Int(a), Int(b)) => {
423 if *b == 0 {
424 return Err(DIVISION_BY_ZERO.to_string());
425 }
426 Ok(match a.checked_div(*b) {
427 Some(q) => Int(q),
428 None => LogosValue::from_bigint(
429 Integer::from_i64(*a).div_rem(&Integer::from_i64(*b)).expect("nonzero divisor").0,
430 ),
431 })
432 }
433 (Int(_) | BigInt(_), Int(_) | BigInt(_)) => {
434 let (a, b) = (self.big().expect("integer"), o.big().expect("integer"));
435 if b.is_zero() {
436 return Err(DIVISION_BY_ZERO.to_string());
437 }
438 Ok(LogosValue::from_bigint(a.div_rem(&b).expect("nonzero divisor").0))
439 }
440 (Float(_), Float(_) | Int(_) | BigInt(_) | Rational(_))
441 | (Int(_) | BigInt(_) | Rational(_), Float(_)) => {
442 let (a, b) = (self.f64().expect("number"), o.f64().expect("number"));
443 let divisor_is_zero = match o {
444 Int(n) => *n == 0,
445 BigInt(_) | Rational(_) => false,
446 _ => b == 0.0,
447 };
448 if divisor_is_zero {
449 return Err(DIVISION_BY_ZERO.to_string());
450 }
451 Ok(Float(a / b))
452 }
453 (Rational(_), Int(_) | BigInt(_) | Rational(_)) | (Int(_) | BigInt(_), Rational(_)) => self
454 .rat()
455 .expect("rational")
456 .div(&o.rat().expect("rational"))
457 .map(LogosValue::from_rational)
458 .ok_or_else(|| DIVISION_BY_ZERO.to_string()),
459 _ => Err(format!("Cannot divide {} by {}", self.type_name(), o.type_name())),
460 }
461 }
462
463 pub fn exact_div(&self, o: &LogosValue) -> Result<LogosValue, String> {
465 if let Some(r) = tower::arith(Op::ExactDiv, self, o, &LogosValue::arith) {
466 return r;
467 }
468 if let (Some(a), Some(b)) = (self.rat(), o.rat()) {
469 return a
470 .div(&b)
471 .map(LogosValue::from_rational)
472 .ok_or_else(|| DIVISION_BY_ZERO.to_string());
473 }
474 if self.is_float() || o.is_float() {
475 return self.div(o);
476 }
477 Err(format!("Cannot divide {} by {}", self.type_name(), o.type_name()))
478 }
479
480 pub fn floor_div(&self, o: &LogosValue) -> Result<LogosValue, String> {
482 if let Some(r) = tower::arith(Op::FloorDiv, self, o, &LogosValue::arith) {
483 return r;
484 }
485 if self.is_float() || o.is_float() {
486 if let (Some(a), Some(b)) = (self.f64(), o.f64()) {
487 if b == 0.0 {
488 return Err(DIVISION_BY_ZERO.to_string());
489 }
490 return Ok(LogosValue::Float((a / b).floor()));
491 }
492 }
493 if let (Some(a), Some(b)) = (self.rat(), o.rat()) {
494 return a
495 .div(&b)
496 .map(|q| LogosValue::from_bigint(q.floor()))
497 .ok_or_else(|| DIVISION_BY_ZERO.to_string());
498 }
499 Err(format!("Cannot floor-divide {} by {}", self.type_name(), o.type_name()))
500 }
501
502 pub fn rem(&self, o: &LogosValue) -> Result<LogosValue, String> {
504 use LogosValue::*;
505 if let Some(r) = tower::arith(Op::Mod, self, o, &LogosValue::arith) {
506 return r;
507 }
508 match (self, o) {
509 (Int(a), Int(b)) => {
510 if *b == 0 {
511 return Err(MODULO_BY_ZERO.to_string());
512 }
513 Ok(Int(a.checked_rem(*b).unwrap_or(0)))
514 }
515 (Int(_) | BigInt(_), Int(_) | BigInt(_)) => {
516 let (a, b) = (self.big().expect("integer"), o.big().expect("integer"));
517 if b.is_zero() {
518 return Err(MODULO_BY_ZERO.to_string());
519 }
520 Ok(LogosValue::from_bigint(a.div_rem(&b).expect("nonzero divisor").1))
521 }
522 _ => Err(format!(
523 "Cannot compute modulo of {} and {}",
524 self.type_name(),
525 o.type_name()
526 )),
527 }
528 }
529
530 pub fn pow(&self, o: &LogosValue) -> Result<LogosValue, String> {
532 use LogosValue::*;
533 if let Some(r) = tower::arith(Op::Pow, self, o, &LogosValue::arith) {
534 return r;
535 }
536 if self.is_float() || o.is_float() {
537 let b = self
538 .f64()
539 .ok_or_else(|| format!("cannot raise {} to a power", self.type_name()))?;
540 let e = o
541 .f64()
542 .ok_or_else(|| format!("cannot raise to a {} power", o.type_name()))?;
543 return Ok(Float(b.powf(e)));
544 }
545 match (self, o) {
546 (Int(b), Int(e)) => {
547 if *e < 0 {
548 return Err(
549 "negative exponent on an integer (an Int can't hold a fraction — use a Float base)"
550 .to_string(),
551 );
552 }
553 let e = u32::try_from(*e).map_err(|_| "exponent too large".to_string())?;
554 Ok(match b.checked_pow(e) {
555 Some(r) => Int(r),
556 None => LogosValue::from_bigint(Integer::from_i64(*b).pow(e)),
557 })
558 }
559 (BigInt(b), Int(e)) => {
560 let e = u32::try_from(*e).map_err(|_| {
561 "negative or too-large exponent on an integer (use a Float base)".to_string()
562 })?;
563 Ok(LogosValue::from_bigint(b.pow(e)))
564 }
565 (Rational(b), Int(e)) => {
566 let e = i32::try_from(*e).map_err(|_| "exponent too large".to_string())?;
567 b.pow(e)
568 .map(LogosValue::from_rational)
569 .ok_or_else(|| "zero raised to a negative power".to_string())
570 }
571 _ => Err(format!(
572 "cannot raise {} to the {} power",
573 self.type_name(),
574 o.type_name()
575 )),
576 }
577 }
578
579 fn relate(rel: Rel, a: LogosValue, b: LogosValue) -> Result<LogosValue, String> {
580 a.relation(rel, &b)
581 }
582
583 pub fn relation(&self, rel: Rel, o: &LogosValue) -> Result<LogosValue, String> {
585 if let Some(r) = tower::relation(rel, self, o, &LogosValue::relate) {
586 return r;
587 }
588 match rel {
589 Rel::Eq => Ok(LogosValue::Bool(self.values_equal(o))),
590 Rel::NotEq => Ok(LogosValue::Bool(!self.values_equal(o))),
591 _ => self.order(o).map(|ord| {
592 LogosValue::Bool(ord.is_some_and(|o| match rel {
593 Rel::Lt => o == Ordering::Less,
594 Rel::Gt => o == Ordering::Greater,
595 Rel::LtEq => o != Ordering::Greater,
596 Rel::GtEq => o != Ordering::Less,
597 Rel::Eq | Rel::NotEq => unreachable!("handled above"),
598 }))
599 }),
600 }
601 }
602
603 fn order(&self, o: &LogosValue) -> Result<Option<Ordering>, String> {
606 use LogosValue::*;
607 Ok(match (self, o) {
608 (Int(a), Int(b)) => Some(a.cmp(b)),
609 (Int(_) | BigInt(_), Int(_) | BigInt(_)) => {
610 Some(self.big().expect("integer").cmp(&o.big().expect("integer")))
611 }
612 (BigInt(a), Float(b)) => cmp_bigint_f64_exact(a, *b),
613 (Float(a), BigInt(b)) => cmp_bigint_f64_exact(b, *a).map(Ordering::reverse),
614 (Rational(_), Int(_) | BigInt(_) | Rational(_)) | (Int(_) | BigInt(_), Rational(_)) => {
615 Some(self.rat().expect("rational").cmp(&o.rat().expect("rational")))
616 }
617 (Rational(a), Float(b)) => cmp_rational_f64_exact(a, *b),
618 (Float(a), Rational(b)) => cmp_rational_f64_exact(b, *a).map(Ordering::reverse),
619 (Float(a), Float(b)) => a.partial_cmp(b),
620 (Int(a), Float(b)) => cmp_i64_f64_exact(*a, *b),
621 (Float(a), Int(b)) => cmp_i64_f64_exact(*b, *a).map(Ordering::reverse),
622 (Other(a), _) if a.order(o).is_some() => a.order(o).expect("checked"),
623 (_, Other(b)) if b.order(self).is_some() => b.order(self).expect("checked").map(Ordering::reverse),
624 (l, r) if l.is_decimal() || r.is_decimal() => match (l.rat(), r.rat()) {
625 (Some(a), Some(b)) => Some(a.cmp(&b)),
626 _ => match (l.f64(), r.f64()) {
627 (Some(a), Some(b)) => a.partial_cmp(&b),
628 _ => {
629 return Err(format!(
630 "Cannot compare {} and {}",
631 l.type_name(),
632 r.type_name()
633 ))
634 }
635 },
636 },
637 _ => {
638 return Err(format!(
639 "Cannot compare {} and {}",
640 self.type_name(),
641 o.type_name()
642 ))
643 }
644 })
645 }
646
647 pub fn values_equal(&self, o: &LogosValue) -> bool {
649 use LogosValue::*;
650 if let Some(same) = tower::structurally_equal(self, o) {
651 return same;
652 }
653 match (self, o) {
654 (Int(a), Int(b)) => a == b,
655 (BigInt(a), BigInt(b)) => a == b,
656 (Rational(a), Rational(b)) => a == b,
657 (Decimal(a), Decimal(b)) => a == b,
658 (Float(a), Float(b)) => a == b,
659 (Int(a), Float(b)) | (Float(b), Int(a)) => {
660 cmp_i64_f64_exact(*a, *b) == Some(Ordering::Equal)
661 }
662 (BigInt(a), Float(b)) | (Float(b), BigInt(a)) => {
663 cmp_bigint_f64_exact(a, *b) == Some(Ordering::Equal)
664 }
665 (Rational(a), Float(b)) | (Float(b), Rational(a)) => {
666 cmp_rational_f64_exact(a, *b) == Some(Ordering::Equal)
667 }
668 (Bool(a), Bool(b)) => a == b,
669 (Text(a), Text(b)) => a == b,
670 (Nothing, Nothing) => true,
671 (List(a), List(b)) | (Tuple(a), Tuple(b)) => {
672 a.len() == b.len() && a.iter().zip(b.iter()).all(|(x, y)| x.values_equal(y))
673 }
674 (Other(a), Other(b)) => a.equals(&**b),
675 _ => false,
676 }
677 }
678
679 pub fn not(&self) -> Result<LogosValue, String> {
681 if let Some(r) = tower::not(self, &|v| v.not()) {
682 return r;
683 }
684 Ok(LogosValue::Bool(!self.is_truthy()))
685 }
686
687 fn rounded(&self, mode: Rounding, name: &str) -> Result<LogosValue, String> {
688 use LogosValue::*;
689 let recurse = |x: LogosValue| x.rounded(mode, name);
690 if let Some(r) = tower::integer_part(self, mode, &recurse) {
691 return r;
692 }
693 let whole = |f: f64| match mode {
694 Rounding::Floor => f.floor(),
695 Rounding::Ceil => f.ceil(),
696 Rounding::Nearest => f.round(),
697 };
698 let exact = |r: Ratio| match mode {
699 Rounding::Floor => r.floor(),
700 Rounding::Ceil => r.ceil(),
701 Rounding::Nearest => r.round(),
702 };
703 match self {
704 Float(f) => Ok(Int(whole(*f) as i64)),
705 Int(_) | BigInt(_) => Ok(self.clone()),
706 Rational(r) => Ok(LogosValue::from_bigint(exact((**r).clone()))),
707 Decimal(d) => Ok(LogosValue::from_bigint(exact(d.to_rational()))),
708 _ => Err(format!("{name}() requires a number, got {}", self.type_name())),
709 }
710 }
711
712 pub fn bit_and(&self, o: &LogosValue) -> Result<LogosValue, String> {
715 use LogosValue::*;
716 match (self, o) {
717 (Int(a), Int(b)) => Ok(Int(a & b)),
718 (BigInt(big), Int(m)) | (Int(m), BigInt(big))
719 if *m >= 0 && (*m as u64).wrapping_add(1).is_power_of_two() && !big.is_negative() =>
720 {
721 let modulus = Integer::from_i64(*m).add(&Integer::from_i64(1));
722 Ok(match big.div_rem(&modulus) {
723 Some((_, r)) => LogosValue::from_bigint(r),
724 None => Int(0),
725 })
726 }
727 (Bool(a), Bool(b)) => Ok(Bool(a & b)),
728 _ => Err(crate::failure::BITAND_OPERANDS.to_string()),
729 }
730 }
731
732 pub fn bit_or(&self, o: &LogosValue) -> Result<LogosValue, String> {
734 use LogosValue::*;
735 match (self, o) {
736 (Int(a), Int(b)) => Ok(Int(a | b)),
737 (Bool(a), Bool(b)) => Ok(Bool(a | b)),
738 _ => Err(crate::failure::BITOR_OPERANDS.to_string()),
739 }
740 }
741
742 pub fn bit_xor(&self, o: &LogosValue) -> Result<LogosValue, String> {
744 use LogosValue::*;
745 match (self, o) {
746 (Int(a), Int(b)) => Ok(Int(a ^ b)),
747 (Bool(a), Bool(b)) => Ok(Bool(a ^ b)),
748 _ => Err(crate::failure::BITXOR_OPERANDS.to_string()),
749 }
750 }
751
752 pub fn shl(&self, o: &LogosValue) -> Result<LogosValue, String> {
754 match (self, o) {
755 (LogosValue::Int(a), LogosValue::Int(b)) => Ok(LogosValue::Int(a.wrapping_shl(*b as u32))),
756 _ => Err(crate::failure::SHL_OPERANDS.to_string()),
757 }
758 }
759
760 pub fn shr(&self, o: &LogosValue) -> Result<LogosValue, String> {
762 match (self, o) {
763 (LogosValue::Int(a), LogosValue::Int(b)) => Ok(LogosValue::Int(a.wrapping_shr(*b as u32))),
764 _ => Err(crate::failure::SHR_OPERANDS.to_string()),
765 }
766 }
767
768 pub fn approx_eq(&self, o: &LogosValue) -> Result<LogosValue, String> {
770 use LogosValue::*;
771 let as_f64 = |v: &LogosValue| match v {
772 Float(f) => Some(*f),
773 Int(n) => Some(*n as f64),
774 BigInt(b) => Some(b.to_f64()),
775 Rational(r) => Some(r.to_f64()),
776 _ => None,
777 };
778 match (as_f64(self), as_f64(o)) {
779 (Some(a), Some(b)) => Ok(Bool(crate::ops::logos_approx_eq(a, b))),
780 _ => Err(format!(
781 "`is approximately` compares numbers, got {} and {}",
782 self.type_name(),
783 o.type_name()
784 )),
785 }
786 }
787
788 pub fn seq_concat(&self, o: &LogosValue) -> Result<LogosValue, String> {
790 match (self, o) {
791 (LogosValue::List(a), LogosValue::List(b)) => {
792 Ok(LogosValue::list(a.iter().chain(b.iter()).cloned().collect()))
793 }
794 _ => Err("`followed by` requires two sequences (merge two sequences into one)".to_string()),
795 }
796 }
797
798 pub fn abs(&self) -> Result<LogosValue, String> {
800 use LogosValue::*;
801 match self {
802 Int(n) => Ok(match n.checked_abs() {
803 Some(a) => Int(a),
804 None => LogosValue::from_bigint(Integer::from_i64(*n).abs()),
805 }),
806 BigInt(b) => Ok(LogosValue::from_bigint(b.abs())),
807 Rational(r) => Ok(LogosValue::from_rational(r.abs())),
808 Decimal(d) => Ok(Decimal(Arc::new(d.abs()))),
809 Float(f) => Ok(Float(f.abs())),
810 _ => Err(format!("abs() requires a number, got {}", self.type_name())),
811 }
812 }
813
814 pub fn sqrt(&self) -> Result<LogosValue, String> {
816 use LogosValue::*;
817 match self {
818 Float(f) => Ok(Float(f.sqrt())),
819 Int(n) => Ok(Float((*n as f64).sqrt())),
820 BigInt(b) => Ok(Float(b.to_f64().sqrt())),
821 _ => Err(format!("sqrt() requires a number, got {}", self.type_name())),
822 }
823 }
824
825 pub fn min(&self, o: &LogosValue) -> Result<LogosValue, String> {
827 use LogosValue::*;
828 match (self, o) {
829 (Int(x), Int(y)) => Ok(Int(*x.min(y))),
830 (Float(x), Float(y)) => Ok(Float(float_min(*x, *y))),
831 (Int(x), Float(y)) => Ok(Float(float_min(*x as f64, *y))),
832 (Float(x), Int(y)) => Ok(Float(float_min(*x, *y as f64))),
833 (Decimal(x), Decimal(y)) => Ok(Decimal(if x <= y { x.clone() } else { y.clone() })),
834 _ => Err("min() requires numbers".to_string()),
835 }
836 }
837
838 pub fn max(&self, o: &LogosValue) -> Result<LogosValue, String> {
840 use LogosValue::*;
841 match (self, o) {
842 (Int(x), Int(y)) => Ok(Int(*x.max(y))),
843 (Float(x), Float(y)) => Ok(Float(float_max(*x, *y))),
844 (Int(x), Float(y)) => Ok(Float(float_max(*x as f64, *y))),
845 (Float(x), Int(y)) => Ok(Float(float_max(*x, *y as f64))),
846 (Decimal(x), Decimal(y)) => Ok(Decimal(if x >= y { x.clone() } else { y.clone() })),
847 _ => Err("max() requires numbers".to_string()),
848 }
849 }
850
851 pub fn power(&self, o: &LogosValue) -> Result<LogosValue, String> {
853 use LogosValue::*;
854 match (self, o) {
855 (Int(b), Int(e)) => Ok(if *e >= 0 {
856 match b.checked_pow(*e as u32) {
857 Some(p) => Int(p),
858 None => LogosValue::from_bigint(Integer::from_i64(*b).pow(*e as u32)),
859 }
860 } else {
861 Float((*b as f64).powi(*e as i32))
862 }),
863 (BigInt(b), Int(e)) => Ok(if *e >= 0 {
864 LogosValue::from_bigint(b.pow(*e as u32))
865 } else {
866 Float(b.to_f64().powi(*e as i32))
867 }),
868 (Float(b), Int(e)) => Ok(Float(b.powi(*e as i32))),
869 (Float(b), Float(e)) => Ok(Float(b.powf(*e))),
870 (Int(b), Float(e)) => Ok(Float((*b as f64).powf(*e))),
871 _ => Err("pow() requires numbers".to_string()),
872 }
873 }
874
875 pub fn length_of(&self) -> Result<i64, String> {
877 match self {
878 LogosValue::List(items) | LogosValue::Tuple(items) => Ok(items.len() as i64),
879 LogosValue::Text(t) => Ok(t.len() as i64),
880 _ => Err(format!("Cannot get length of {}", self.type_name())),
881 }
882 }
883
884 pub fn contains_value(&self, value: &LogosValue) -> Result<bool, String> {
886 match self {
887 LogosValue::List(items) => Ok(items.iter().any(|i| i.values_equal(value))),
888 LogosValue::Text(t) => match value {
889 LogosValue::Text(needle) => Ok(t.contains(&**needle)),
890 other => Err(format!("Cannot check if Text contains {}", other.type_name())),
891 },
892 _ => Err(format!("Cannot check contains on {}", self.type_name())),
893 }
894 }
895
896 pub fn items(&self) -> Result<Vec<LogosValue>, String> {
899 match self {
900 LogosValue::List(items) => Ok(items.to_vec()),
901 LogosValue::Text(t) => Ok(t.chars().map(|c| LogosValue::text(&c.to_string())).collect()),
902 _ => Err(format!("Cannot iterate over {}", self.type_name())),
903 }
904 }
905
906 pub fn push_value(&mut self, value: LogosValue) -> Result<(), String> {
908 match self {
909 LogosValue::List(items) => {
910 Arc::make_mut(items).push(value);
911 Ok(())
912 }
913 _ => Err("Can only push to a List".to_string()),
914 }
915 }
916
917 pub fn set_item(&mut self, index: &LogosValue, value: LogosValue) -> Result<(), String> {
919 match (&mut *self, index) {
920 (LogosValue::List(items), LogosValue::Int(i)) => {
921 let at = crate::indexing::logos_offset(*i, items.len()).ok_or_else(|| index_out_of_bounds(i))?;
922 Arc::make_mut(items)[at] = value;
923 Ok(())
924 }
925 (LogosValue::List(_), _) => Err("List index must be an integer".to_string()),
926 _ => Err(format!("Cannot index into {}", self.type_name())),
927 }
928 }
929
930 pub fn pop_value(&mut self) -> Result<LogosValue, String> {
932 match self {
933 LogosValue::List(items) => Ok(Arc::make_mut(items).pop().unwrap_or(LogosValue::Nothing)),
934 _ => Err("Can only pop from a List".to_string()),
935 }
936 }
937
938 pub fn item(&self, index: &LogosValue) -> Result<LogosValue, String> {
940 use LogosValue::*;
941 match (self, index) {
942 (List(items) | Tuple(items), Int(i)) => crate::indexing::logos_offset(*i, items.len())
943 .map(|at| items[at].clone())
944 .ok_or_else(|| index_out_of_bounds(i)),
945 (Text(t), Int(i)) => {
946 let chars = t.chars().count();
947 crate::indexing::logos_offset(*i, chars)
948 .map(|at| LogosValue::text(&t.chars().nth(at).expect("offset in range").to_string()))
949 .ok_or_else(|| index_out_of_bounds(i))
950 }
951 _ => Err(format!("Cannot index {} with {}", self.type_name(), index.type_name())),
952 }
953 }
954
955 pub fn slice(&self, start: &LogosValue, end: &LogosValue) -> Result<LogosValue, String> {
958 match (self, start, end) {
959 (LogosValue::List(items), LogosValue::Int(s), LogosValue::Int(e)) => {
960 Ok(LogosValue::list(items[crate::logos_slice_range(*s, *e, items.len())].to_vec()))
961 }
962 _ => Err("Slice requires List and Int indices".to_string()),
963 }
964 }
965
966 pub fn integer_at(&self, past_word: &str, fraction: &str) -> Result<i64, String> {
969 match self {
970 LogosValue::Int(n) => Ok(*n),
971 LogosValue::BigInt(_) => Err(past_word.to_string()),
972 LogosValue::Rational(_) => Err(fraction.to_string()),
973 other => Err(format!("Expected Int, found {}", other.type_name())),
974 }
975 }
976
977 pub fn into_real(self) -> f64 {
979 match self {
980 LogosValue::Float(f) => f,
981 other => unreachable!("an operator over a real answered a {}", other.type_name()),
982 }
983 }
984
985 pub fn floor(&self) -> Result<LogosValue, String> {
987 self.rounded(Rounding::Floor, "floor")
988 }
989
990 pub fn ceil(&self) -> Result<LogosValue, String> {
992 self.rounded(Rounding::Ceil, "ceil")
993 }
994
995 pub fn round(&self) -> Result<LogosValue, String> {
997 self.rounded(Rounding::Nearest, "round")
998 }
999}
1000
1001impl fmt::Display for LogosValue {
1002 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1003 f.write_str(&self.display_string())
1004 }
1005}
1006
1007impl Default for LogosValue {
1009 fn default() -> Self {
1010 LogosValue::Nothing
1011 }
1012}
1013
1014impl PartialEq for LogosValue {
1015 fn eq(&self, o: &Self) -> bool {
1016 self.values_equal(o)
1017 }
1018}
1019
1020impl From<i64> for LogosValue {
1021 fn from(n: i64) -> Self {
1022 LogosValue::Int(n)
1023 }
1024}
1025
1026impl From<f64> for LogosValue {
1027 fn from(f: f64) -> Self {
1028 LogosValue::Float(f)
1029 }
1030}
1031
1032impl From<bool> for LogosValue {
1033 fn from(b: bool) -> Self {
1034 LogosValue::Bool(b)
1035 }
1036}
1037
1038impl From<&str> for LogosValue {
1039 fn from(t: &str) -> Self {
1040 LogosValue::text(t)
1041 }
1042}
1043
1044impl From<String> for LogosValue {
1045 fn from(t: String) -> Self {
1046 LogosValue::text(&t)
1047 }
1048}
1049
1050impl From<crate::types::LogosRational> for LogosValue {
1051 fn from(r: crate::types::LogosRational) -> Self {
1052 LogosValue::from_rational(r.0)
1053 }
1054}
1055
1056impl From<crate::types::LogosInt> for LogosValue {
1057 fn from(n: crate::types::LogosInt) -> Self {
1058 match n {
1059 crate::types::LogosInt::Small(n) => LogosValue::Int(n),
1060 crate::types::LogosInt::Big(b) => LogosValue::from_bigint(*b),
1061 }
1062 }
1063}
1064
1065impl<T: Into<LogosValue>> From<Vec<T>> for LogosValue {
1066 fn from(items: Vec<T>) -> Self {
1067 LogosValue::list(items.into_iter().map(Into::into).collect())
1068 }
1069}
1070
1071macro_rules! from_machine_integer {
1072 ($($t:ty),*) => {$(
1073 impl From<$t> for LogosValue {
1074 fn from(n: $t) -> Self {
1075 match i64::try_from(n) {
1076 Ok(n) => LogosValue::Int(n),
1077 Err(_) => LogosValue::from_bigint(
1078 BigInt::parse_decimal(&n.to_string()).expect("a machine integer's digits"),
1079 ),
1080 }
1081 }
1082 }
1083 )*};
1084}
1085
1086from_machine_integer!(i8, i16, i32, i128, u8, u16, u32, u64, u128, usize);
1087
1088impl From<()> for LogosValue {
1089 fn from(_: ()) -> Self {
1090 LogosValue::Nothing
1091 }
1092}
1093
1094impl From<crate::types::LogosDecimal> for LogosValue {
1095 fn from(d: crate::types::LogosDecimal) -> Self {
1096 LogosValue::Decimal(Arc::new(d.0))
1097 }
1098}
1099
1100impl<T: Clone + Into<LogosValue>> From<crate::types::LogosSeq<T>> for LogosValue {
1101 fn from(items: crate::types::LogosSeq<T>) -> Self {
1102 LogosValue::list(items.0.borrow().iter().cloned().map(Into::into).collect())
1103 }
1104}
1105
1106impl crate::ops::Truthy for LogosValue {
1107 fn truthy(&self) -> bool {
1108 answered(self.decided_truth())
1109 }
1110}
1111
1112impl crate::indexing::LogosIndex<i64> for LogosValue {
1113 type Output = LogosValue;
1114 fn logos_get(&self, index: i64) -> LogosValue {
1115 answered(self.item(&LogosValue::Int(index)))
1116 }
1117}
1118
1119impl crate::indexing::LogosIndex<LogosValue> for LogosValue {
1120 type Output = LogosValue;
1121 fn logos_get(&self, index: LogosValue) -> LogosValue {
1122 answered(self.item(&index))
1123 }
1124}
1125
1126impl TowerValue for LogosValue {
1127 fn exact(&self) -> Option<ExactRef<'_, Self>> {
1128 Some(match self {
1129 LogosValue::Algebraic(a) => ExactRef::Algebraic(a),
1130 LogosValue::ExactReal(x) => ExactRef::Real(x),
1131 LogosValue::Hyperreal(h) => ExactRef::Hyper(h),
1132 LogosValue::Hypernatural(n) => ExactRef::Natural(n),
1133 LogosValue::Ordinal(o) => ExactRef::Ordinal(o),
1134 LogosValue::Depends(d) => ExactRef::Depends(d),
1135 LogosValue::Converging(c) => ExactRef::Converging(c),
1136 _ => return None,
1137 })
1138 }
1139
1140 fn standard_rational(&self) -> Option<Rational> {
1141 match self {
1142 LogosValue::Float(f) => rational_from_f64_exact(*f),
1143 other => other.rat(),
1144 }
1145 }
1146
1147 fn integer(&self) -> Option<BigInt> {
1148 self.big()
1149 }
1150
1151 fn exact_rational(&self) -> Option<Rational> {
1152 match self {
1153 LogosValue::Int(n) => Some(Rational::from_i64(*n)),
1154 LogosValue::BigInt(b) => Some(Rational::from_bigint((**b).clone())),
1155 LogosValue::Rational(r) => Some((**r).clone()),
1156 _ => None,
1157 }
1158 }
1159
1160 fn is_standard_number(&self) -> bool {
1161 matches!(
1162 self,
1163 LogosValue::Int(_)
1164 | LogosValue::BigInt(_)
1165 | LogosValue::Rational(_)
1166 | LogosValue::Decimal(_)
1167 | LogosValue::Float(_)
1168 )
1169 }
1170
1171 fn text(&self) -> Option<&str> {
1172 match self {
1173 LogosValue::Text(t) => Some(t),
1174 _ => None,
1175 }
1176 }
1177
1178 fn type_name(&self) -> String {
1179 LogosValue::type_name(self).to_string()
1180 }
1181
1182 fn display_string(&self) -> String {
1183 LogosValue::display_string(self)
1184 }
1185
1186 fn values_equal(&self, other: &Self) -> bool {
1187 LogosValue::values_equal(self, other)
1188 }
1189
1190 fn is_nothing(&self) -> bool {
1191 matches!(self, LogosValue::Nothing)
1192 }
1193
1194 fn from_rational(r: Rational) -> Self {
1195 LogosValue::from_rational(r)
1196 }
1197
1198 fn from_bigint(b: BigInt) -> Self {
1199 LogosValue::from_bigint(b)
1200 }
1201
1202 fn boolean(b: bool) -> Self {
1203 LogosValue::Bool(b)
1204 }
1205
1206 fn nothing() -> Self {
1207 LogosValue::Nothing
1208 }
1209
1210 fn algebraic(a: Algebraic) -> Self {
1211 LogosValue::Algebraic(Arc::new(a))
1212 }
1213
1214 fn exact_real(x: ExactReal) -> Self {
1215 LogosValue::ExactReal(Arc::new(x))
1216 }
1217
1218 fn hyper(h: HyperValue) -> Self {
1219 LogosValue::Hyperreal(Arc::new(h))
1220 }
1221
1222 fn natural(n: NaturalValue) -> Self {
1223 LogosValue::Hypernatural(Arc::new(n))
1224 }
1225
1226 fn ordinal(o: Ordinal) -> Self {
1227 LogosValue::Ordinal(Arc::new(o))
1228 }
1229
1230 fn depends(d: DependsValue<Self>) -> Self {
1231 LogosValue::Depends(Arc::new(d))
1232 }
1233
1234 fn converging(c: Converging<Self>) -> Self {
1235 LogosValue::Converging(Arc::new(c))
1236 }
1237}
1238
1239impl ValueAlgebra for LogosValue {
1240 fn add(a: Self, b: Self) -> Result<Self, String> {
1241 a.add(&b)
1242 }
1243
1244 fn subtract(a: Self, b: Self) -> Result<Self, String> {
1245 a.sub(&b)
1246 }
1247
1248 fn multiply(a: Self, b: Self) -> Result<Self, String> {
1249 a.mul(&b)
1250 }
1251
1252 fn exact_divide(a: Self, b: Self) -> Result<Self, String> {
1253 a.exact_div(&b)
1254 }
1255
1256 fn modulo(a: Self, b: Self) -> Result<Self, String> {
1257 a.rem(&b)
1258 }
1259
1260 fn power(a: Self, b: Self) -> Result<Self, String> {
1261 a.pow(&b)
1262 }
1263
1264 fn holds(rel: Rel, a: &Self, b: &Self) -> Result<bool, String> {
1265 a.relation(rel, b)?.decided_truth()
1266 }
1267
1268 fn floor(v: Self) -> Result<Self, String> {
1269 LogosValue::floor(&v)
1270 }
1271
1272 fn ceil(v: Self) -> Result<Self, String> {
1273 LogosValue::ceil(&v)
1274 }
1275
1276 fn list(values: Vec<Self>) -> Self {
1277 LogosValue::list(values)
1278 }
1279
1280 fn shape(&self) -> Shape<Self> {
1281 match self {
1282 LogosValue::Bool(_) => Shape::Bool,
1283 LogosValue::Int(n) => Shape::Int(*n),
1284 LogosValue::Float(_) => Shape::Float,
1285 LogosValue::BigInt(b) => Shape::BigInt((**b).clone()),
1286 LogosValue::Rational(r) => Shape::Rational((**r).clone()),
1287 LogosValue::Decimal(_) => Shape::Decimal,
1288 LogosValue::Text(t) => Shape::Text(t.len()),
1289 LogosValue::List(items) => Shape::List(items.to_vec()),
1290 LogosValue::Tuple(items) => Shape::Tuple(items.to_vec()),
1291 _ => Shape::Other,
1292 }
1293 }
1294}
1295
1296type Definer = Arc<dyn Fn(i64) -> Result<LogosValue, String> + Send + Sync>;
1301
1302pub(crate) fn answered_rational(name: &str, n: u64, v: &LogosValue) -> Result<Rational, String> {
1304 match v {
1305 LogosValue::Int(i) => Ok(Rational::from_i64(*i)),
1306 LogosValue::BigInt(b) => Ok(Rational::from_bigint((**b).clone())),
1307 LogosValue::Rational(r) => Ok((**r).clone()),
1308 other => Err(logicaffeine_base::exact_real::not_a_rational_answer(name, n, other.type_name())),
1309 }
1310}
1311
1312struct Compiled {
1314 name: String,
1315 f: Definer,
1316}
1317
1318impl logicaffeine_base::exact_real::Approximant for Compiled {
1319 fn call(&self, n: u64) -> Result<Rational, String> {
1320 let arg = i64::try_from(n).map_err(|_| format!("{}({n}) is past every Int", self.name))?;
1321 answered_rational(&self.name, n, &(self.f)(arg)?)
1322 }
1323}
1324
1325impl LogosValue {
1326 pub fn defined_real(
1330 kind: &str,
1331 base: i64,
1332 name: &str,
1333 f: impl Fn(i64) -> LogosValue + Send + Sync + 'static,
1334 ) -> LogosValue {
1335 answered(Self::define_real(kind, base, name, move |n| Ok(f(n))))
1336 }
1337
1338 pub fn define_real(
1341 kind: &str,
1342 base: i64,
1343 name: &str,
1344 f: impl Fn(i64) -> Result<LogosValue, String> + Send + Sync + 'static,
1345 ) -> Result<LogosValue, String> {
1346 let definition = logicaffeine_base::exact_real::Definition::named(kind, base)?;
1347 let source = Arc::new(Compiled {
1348 name: name.to_string(),
1349 f: Arc::new(f),
1350 });
1351 Ok(LogosValue::ExactReal(Arc::new(ExactReal::defined(definition, name, source))))
1352 }
1353
1354 pub fn sendable(&self) -> Result<(), String> {
1357 match self {
1358 LogosValue::List(items) | LogosValue::Tuple(items) => items.iter().try_for_each(LogosValue::sendable),
1359 v if logicaffeine_base::tower::transmissible(v) => Ok(()),
1360 _ => Err(logicaffeine_base::exact_real::not_sendable()),
1361 }
1362 }
1363}
1364
1365#[cfg(test)]
1366#[path = "../../logicaffeine_tests/unit/logicaffeine_data/exact/tests.rs"]
1367mod tests;