]> Repositorios git - scryer-prolog.git/commitdiff
add (^) as an evaluable factor, re: #39
authorMark Thom <[email protected]>
Sat, 1 Sep 2018 19:48:04 +0000 (13:48 -0600)
committerMark Thom <[email protected]>
Sat, 1 Sep 2018 19:48:04 +0000 (13:48 -0600)
Cargo.toml
README.md
src/prolog/arithmetic.rs
src/prolog/ast.rs
src/prolog/io.rs
src/prolog/lib/builtins.pl
src/prolog/machine/machine_errors.rs
src/prolog/machine/machine_state_impl.rs
src/tests.rs

index 25d4472f5b97ef8ddeae4c85acf9adf2998054e9..6c9f0e0afec27c377f2f4737a21be6bf89668c8f 100644 (file)
@@ -4,9 +4,9 @@ version = "0.7.9"
 authors = ["Mark Thom"]
 
 [dependencies]
+downcast = "0.9.1"
 num = "0.2"
 ordered-float = "0.5.0"
-downcast = "0.9.1"
 
 [dependencies.termion]
 version = "1.4.0"
\ No newline at end of file
index 5fccf0294399128bef3199644ca0df69ac04c5b2..03f26aafcb5d0eb8e5b36849cc3367c10cb504f6 100644 (file)
--- a/README.md
+++ b/README.md
@@ -28,7 +28,7 @@ Extend rusty-wam to include the following, among other features:
 * Built-in control operators (`,`, `;`, `->`, etc.) (_done_).
 * A revised, not-terrible module system (_done, I think_).
 * Built-in predicates for list processing and top-level declarative
-  control (`setup_call_control/3`, `call_with_inference_limit/3`,
+  control (`setup_call_cleanup/3`, `call_with_inference_limit/3`,
   etc.) (_done_)
 * Default representation of strings as list of chars, using a packed
   internal representation (_done_).
@@ -110,7 +110,7 @@ information.
 The following predicates are built-in to rusty-wam.
 
 * Arithmetic support:
-    * `is/2` works for `(+)/2`, `(-)/{1,2}`, `(*)/2`, `(//)/2`, `(div)/2`, `(/)/2`, `(rdiv)/2`,
+    * `is/2` works for `(+)/2`, `(-)/{1,2}`, `(*)/2`, `(//)/2`, `(^)/2`, `(div)/2`, `(/)/2`, `(rdiv)/2`,
       `(xor)/2`, `(rem)/2`, `(mod)/2`, `(/\)/2`, `(\/)/2`, `(>>)/2`, `(<<)/2`.
     * Comparison operators: `>`, `<`, `=<`, `>=`, `=:=`, `=\=`.
 * `(:)/2`
index 79ca2c90325009fe4390674ac8ba5636c94e5746..ecde8b59bd4524010241e07b48313e7326ee864d 100644 (file)
@@ -105,6 +105,7 @@ impl<'a> ArithmeticEvaluator<'a>
                        -> Result<ArithmeticInstruction, ArithmeticError>
     {
         match name.as_str() {
+            "abs" => Ok(ArithmeticInstruction::Abs(a1, t)),
             "-" => Ok(ArithmeticInstruction::Neg(a1, t)),
              _  => Err(ArithmeticError::InvalidOp)
         }
@@ -121,6 +122,7 @@ impl<'a> ArithmeticEvaluator<'a>
             "div"  => Ok(ArithmeticInstruction::FIDiv(a1, a2, t)),
             "rdiv" => Ok(ArithmeticInstruction::RDiv(a1, a2, t)),
             "*"    => Ok(ArithmeticInstruction::Mul(a1, a2, t)),
+            "^"    => Ok(ArithmeticInstruction::Pow(a1, a2, t)),
             ">>"   => Ok(ArithmeticInstruction::Shr(a1, a2, t)),
             "<<"   => Ok(ArithmeticInstruction::Shl(a1, a2, t)),
             "/\\"  => Ok(ArithmeticInstruction::And(a1, a2, t)),
index 693c98e63af9789727ce22201539751b43798b95..d006dd66a592fe270366446f57b5703459f6a8a7 100644 (file)
@@ -1,6 +1,6 @@
-use prolog::num::bigint::BigInt;
-use prolog::num::{Float, ToPrimitive, Zero};
-use prolog::num::rational::Ratio;
+use prolog::num::bigint::{BigInt, BigUint};
+use prolog::num::{Float, Integer, One, Signed, ToPrimitive, Zero};
+use prolog::num::rational::{BigRational, Ratio};
 use prolog::ordered_float::*;
 use prolog::string_list::*;
 use prolog::tabled_rc::*;
@@ -11,7 +11,7 @@ use std::collections::{BTreeSet, HashMap, VecDeque};
 use std::fmt;
 use std::hash::{Hash, Hasher};
 use std::io::Error as IOError;
-use std::ops::{Add, AddAssign, Div, Index, IndexMut, Sub, Mul, Neg};
+use std::ops::{Add, AddAssign, Div, Index, IndexMut, Sub, Mul, MulAssign, Neg};
 use std::rc::Rc;
 use std::str::Utf8Error;
 use std::vec::Vec;
@@ -476,6 +476,7 @@ pub enum ArithmeticError {
     InvalidAtom,
     InvalidOp,
     InvalidTerm,
+    NoRoots,
     UninstantiatedVar
 }
 
@@ -557,7 +558,7 @@ impl PartialEq for Constant {
             (&Constant::Number(ref n1), &Constant::Number(ref n2)) =>
                 n1 == n2,
             (&Constant::String(ref s1), &Constant::String(ref s2)) =>
-                s1 == s2,                      
+                s1 == s2,
             (&Constant::EmptyList, &Constant::EmptyList) =>
                 true,
             (&Constant::Usize(u1), &Constant::Usize(u2)) =>
@@ -724,7 +725,7 @@ impl QueryTerm {
             _ => {}
         };
     }
-    
+
     pub fn arity(&self) -> usize {
         match self {
             &QueryTerm::Clause(_, _, ref subterms, ..) => subterms.len(),
@@ -909,7 +910,7 @@ impl<'a> From<&'a TabledRc<Atom>> for ClauseName {
     }
 }
 
-impl ClauseName {    
+impl ClauseName {
     pub fn as_str(&self) -> &str {
         match self {
             &ClauseName::BuiltIn(s) => s,
@@ -1173,7 +1174,136 @@ impl Default for Number {
     }
 }
 
+fn binary_pow<T>(mut n: T, mut power: BigUint) -> T
+    where T: Clone + Mul + One,
+    for<'a> T: MulAssign<&'a T>
+{
+    if power.is_zero() {
+        return T::one();
+    }
+
+    let mut oddand = T::one();
+    let one = BigUint::one();
+
+    while power > one {
+        if power.is_odd() {
+            oddand *= &n;
+        }
+
+        n = n.clone() * n;
+        power >>= 1;
+    }
+
+    n * oddand
+}
+
+fn rational_pow(r1: BigRational, r2: BigRational) -> Result<BigRational, ArithmeticError>
+{
+    #[inline]
+    fn to_unsigned(n: &BigInt) -> Result<BigUint, ArithmeticError> {
+        n.abs().to_biguint().ok_or(ArithmeticError::NoRoots)
+    };
+
+    #[inline]
+    fn to_big_rational(n: BigUint) -> BigRational {
+        BigRational::from_integer(BigInt::from(n))
+    };
+
+    let r2 = r2.reduced(); // so that gcd(numer, denom) = 1
+    let n = to_unsigned(r2.denom())?;
+
+    if n == BigUint::one() {
+        return if r2.is_positive() {
+            Ok(binary_pow(r1, to_unsigned(&r2.numer())?))
+        } else if r2.is_negative() {
+            Ok(binary_pow(r1, to_unsigned(&r2.numer())?).recip())
+        } else {
+            Ok(BigRational::one())
+        };
+    }
+
+    if (n.is_even() && r1.is_negative()) || (r2.is_negative() && r1.is_zero()) {
+        return Err(ArithmeticError::NoRoots);
+    }
+
+    let sgn = r1.signum();
+    let r1 = r1 * &sgn; // set r1 to its absolute value.
+
+    let epsilon = BigRational::new_raw(BigInt::one(), BigInt::from(10000));
+    let n1      = n.clone() - BigUint::one(); // n -1
+
+    // 1 + r1 / (n-1) is a good initial point.
+    let mut x_i = BigRational::one() + r1.clone() / to_big_rational(n1.clone());
+    let mut x_i_n1 = binary_pow(x_i.clone(), n1.clone()); // x_i^{n-1}
+    let mut delta_x_i = BigRational::one();
+
+    while delta_x_i.abs() > epsilon {
+        x_i = x_i.reduced();
+        x_i_n1 = x_i_n1.reduced();
+
+        let r_quot = r1.clone() / &x_i_n1; // r1 / x_i^{n-1}
+        let r_n    = to_big_rational(n.clone());
+
+        delta_x_i = ( r_quot - &x_i ) / &r_n;
+
+        x_i += &delta_x_i;
+        x_i_n1 = binary_pow(x_i.clone(), n1.clone());
+    }
+
+    if r2.is_positive() {
+        Ok(binary_pow(sgn * x_i, to_unsigned(r2.numer())?))
+    } else {
+        Ok(binary_pow(sgn * x_i, to_unsigned(r2.numer())?).recip())
+    }
+}
+
+fn pow_float(f1: f64, f2: f64) -> Result<Number, ArithmeticError> {
+    let result = OrderedFloat(f1.powf(f2));
+
+    if result.is_finite() {
+        Ok(Number::Float(result))
+    } else {
+        Err(ArithmeticError::NoRoots)
+    }
+}
+
+fn rational_to_f64(r: &BigRational) -> Option<f64> {
+    match (r.numer().to_f64(), r.denom().to_f64()) {
+        (Some(ref f1), Some(ref f2)) if f2.is_normal() => Some(*f1 / *f2),
+        _ => None
+    }
+}
+
 impl Number {
+    pub fn pow(self, other: Number) -> Result<Self, ArithmeticError> {
+        match NumberPair::from(self, other) {
+            NumberPair::Integer(n1, n2) =>
+                if let Some(n2) = n2.to_biguint() {
+                    Ok(Number::Integer(Rc::new(binary_pow((*n1).clone(), n2))))
+                } else if n1.is_zero() {
+                    Err(ArithmeticError::NoRoots)
+                } else {
+                    let r1 = Ratio::new(BigInt::one(), (*n1).clone());
+                    let n2 = n2.abs().to_biguint().unwrap();
+
+                    Ok(Number::Rational(Rc::new(binary_pow(r1, n2))))
+                },
+            NumberPair::Float(n1, n2) =>
+                pow_float(n1.into_inner(), n2.into_inner()),
+            NumberPair::Rational(r1, r2) => {
+                if let (Some(f1), Some(f2)) = (rational_to_f64(&r1), rational_to_f64(&r2)) {                    
+                    if let Ok(result) = pow_float(f1, f2) {
+                        return Ok(result);
+                    }
+                }
+                
+                let root = rational_pow((*r1).clone(), (*r2).clone())?;
+                Ok(Number::Rational(Rc::new(root)))
+            }                    
+        }
+    }
+
+    #[inline]
     pub fn is_zero(&self) -> bool {
         match self {
             &Number::Float(fl)       => fl.into_inner().is_zero(),
@@ -1181,6 +1311,15 @@ impl Number {
             &Number::Rational(ref r) => r.is_zero()
         }
     }
+
+    #[inline]
+    pub fn abs(&self) -> Self {
+        match self {
+            &Number::Float(ref fl)   => Number::Float(OrderedFloat(fl.into_inner().abs())),
+            &Number::Integer(ref n)  => Number::Integer(Rc::new((*n).clone().abs())),
+            &Number::Rational(ref r) => Number::Rational(Rc::new((*r).clone().abs()))
+        }
+    }
 }
 
 pub enum NumberPair {
@@ -1364,6 +1503,7 @@ pub enum ArithmeticInstruction {
     Add(ArithmeticTerm, ArithmeticTerm, usize),
     Sub(ArithmeticTerm, ArithmeticTerm, usize),
     Mul(ArithmeticTerm, ArithmeticTerm, usize),
+    Pow(ArithmeticTerm, ArithmeticTerm, usize),
     IDiv(ArithmeticTerm, ArithmeticTerm, usize),
     FIDiv(ArithmeticTerm, ArithmeticTerm, usize),
     RDiv(ArithmeticTerm, ArithmeticTerm, usize),
@@ -1375,7 +1515,8 @@ pub enum ArithmeticInstruction {
     Or(ArithmeticTerm, ArithmeticTerm, usize),
     Mod(ArithmeticTerm, ArithmeticTerm, usize),
     Rem(ArithmeticTerm, ArithmeticTerm, usize),
-    Neg(ArithmeticTerm, usize)
+    Abs(ArithmeticTerm, usize),
+    Neg(ArithmeticTerm, usize),
 }
 
 #[derive(Clone)]
index d9eb51229b427c32b2bf46b47685cf08ef4913df..ccd05857daa553932e5f9316ce444efa964f8361 100644 (file)
@@ -244,12 +244,16 @@ impl fmt::Display for ArithmeticTerm {
 impl fmt::Display for ArithmeticInstruction {
     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
         match self {
+            &ArithmeticInstruction::Abs(ref a1, ref t) =>
+                write!(f, "abs {}, @{}", a1, t),
             &ArithmeticInstruction::Add(ref a1, ref a2, ref t) =>
                 write!(f, "add {}, {}, @{}", a1, a2, t),
             &ArithmeticInstruction::Sub(ref a1, ref a2, ref t) =>
                 write!(f, "sub {}, {}, @{}", a1, a2, t),
             &ArithmeticInstruction::Mul(ref a1, ref a2, ref t) =>
                 write!(f, "mul {}, {}, @{}", a1, a2, t),
+            &ArithmeticInstruction::Pow(ref a1, ref a2, ref t) =>
+                write!(f, "pow {}, {}, @{}", a1, a2, t),
             &ArithmeticInstruction::Div(ref a1, ref a2, ref t) =>
                 write!(f, "div {}, {}, @{}", a1, a2, t),
             &ArithmeticInstruction::IDiv(ref a1, ref a2, ref t) =>
index d15b3f25b71cd5ea5760c4f6274451ab21b5e7f3..5e209bbe7fe6d9ec6d1f46fff32ebe99c72b8a40 100644 (file)
@@ -1,6 +1,6 @@
 :- op(400, yfx, /).
 
-:- module(builtins, [(=)/2, (+)/2, (*)/2, (-)/2, (/)/2, (/\)/2,
+:- module(builtins, [(=)/2, (+)/2, (^)/2, (*)/2, (-)/2, (/)/2, (/\)/2,
        (\/)/2, (is)/2, (xor)/2, (div)/2, (//)/2, (rdiv)/2, (<<)/2,
        (>>)/2, (mod)/2, (rem)/2, (>)/2, (<)/2, (=\=)/2, (=:=)/2,
        (-)/1, (>=)/2, (=<)/2, (,)/2, (->)/2, (;)/2, (=..)/2, (==)/2,
@@ -19,6 +19,7 @@
 :- op(500, yfx, +).
 :- op(500, yfx, -).
 :- op(400, yfx, *).
+:- op(200, xfy, ^).
 :- op(500, yfx, /\).
 :- op(500, yfx, \/).
 :- op(500, yfx, xor).
index c744c91be0001b93cac4f992b263a75136be0728..797dd220aa4af9382eab866b8329d13cd6391081 100644 (file)
@@ -233,7 +233,8 @@ pub enum EvalError {
 //    IntOverflow,
 //    Undefined,
 //    Underflow,
-    ZeroDivisor
+    ZeroDivisor,
+    NoRoots
 }
 
 impl EvalError {
@@ -243,7 +244,8 @@ impl EvalError {
 //            EvalError::IntOverflow => "int_overflow",
 //            EvalError::Undefined => "undefined",
 //            EvalError::Underflow => "underflow",
-            EvalError::ZeroDivisor => "zero_divisor"
+            EvalError::ZeroDivisor => "zero_divisor",
+            EvalError::NoRoots => "no_roots"
         }
     }
 }
index b6d53876a49cd0416624f1fc4024f960c243bfc9..04037889e3fab9f033b8efd528a4ff500af36cbd 100644 (file)
@@ -60,7 +60,7 @@ impl MachineState {
     pub fn machine_flags(&self) -> MachineFlags {
         self.flags
     }
-    
+
     fn next_global_index(&self) -> usize {
         max(if self.and_stack.len() > 0 { self.and_stack[self.e].global_index } else { 0 },
             if self.b > 0 { self.or_stack[self.b - 1].global_index } else { 0 }) + 1
@@ -193,15 +193,15 @@ impl MachineState {
                     (Addr::Lis(a1), Addr::Con(Constant::String(ref s)))
                   | (Addr::Con(Constant::String(ref s)), Addr::Lis(a1))
                         if self.flags.double_quotes.is_chars() =>
-                            if let Some(c) = s.head() {                                
+                            if let Some(c) = s.head() {
                                 pdl.push(Addr::Con(Constant::String(s.tail())));
                                 pdl.push(Addr::HeapCell(a1 + 1));
-                                
+
                                 pdl.push(Addr::Con(Constant::Char(c)));
                                 pdl.push(Addr::HeapCell(a1));
                             } else {
                                 self.fail = true;
-                            },                   
+                            },
                     (Addr::Con(Constant::EmptyList), Addr::Con(Constant::String(ref s)))
                   | (Addr::Con(Constant::String(ref s)), Addr::Con(Constant::EmptyList))
                         if self.flags.double_quotes.is_chars() => {
@@ -220,7 +220,7 @@ impl MachineState {
                                 if c1 == c2 {
                                     pdl.push(Addr::Con(Constant::String(s1.tail())));
                                     pdl.push(Addr::Con(Constant::String(s2.tail())));
-                                    
+
                                     continue;
                                 }
                             }
@@ -232,7 +232,7 @@ impl MachineState {
 
                         self.fail = true;
                     },
-                    (Addr::Con(ref c1), Addr::Con(ref c2)) => 
+                    (Addr::Con(ref c1), Addr::Con(ref c2)) =>
                         if c1 != c2 {
                             self.fail = true;
                         },
@@ -355,8 +355,8 @@ impl MachineState {
             Addr::StackCell(fr, sc) => {
                 self.and_stack[fr][sc] = Addr::Con(c.clone());
                 self.trail(Ref::StackCell(fr, sc));
-            },            
-            Addr::Con(Constant::String(s)) =>            
+            },
+            Addr::Con(Constant::String(s)) =>
                 self.fail = match c {
                     Constant::EmptyList
                       if self.flags.double_quotes.is_chars() =>
@@ -429,6 +429,7 @@ impl MachineState {
                         "-" => interms.push(a1 - a2),
                         "*" => interms.push(a1 * a2),
                         "/" => interms.push(self.div(a1, a2)?),
+                        "^" => interms.push(self.pow(a1, a2)?),
                         "rdiv" => {
                             let r1 = self.get_rational(&ArithmeticTerm::Number(a1), &caller)?;
                             let r2 = self.get_rational(&ArithmeticTerm::Number(a2), &caller)?;
@@ -535,6 +536,18 @@ impl MachineState {
         }
     }
 
+    fn pow(&self, n1: Number, n2: Number) -> Result<Number, MachineStub>
+    {
+        match n1.pow(n2) {
+            Ok(result) => Ok(result),
+            Err(_) => {
+                let stub = MachineError::functor_stub(clause_name!("^"), 2);
+                Err(self.error_form(MachineError::evaluation_error(EvalError::NoRoots),
+                                    stub))
+            }
+        }
+    }
+
     fn shr(&self, n1: Number, n2: Number) -> Result<Rc<BigInt>, MachineStub>
     {
         let stub = MachineError::functor_stub(clause_name!("(>>)"), 2);
@@ -678,7 +691,7 @@ impl MachineState {
     }
 
     pub(super) fn execute_arith_instr(&mut self, instr: &ArithmeticInstruction) {
-        match instr {
+        match instr {            
             &ArithmeticInstruction::Add(ref a1, ref a2, t) => {
                 let n1 = try_or_fail!(self, self.get_number(a1));
                 let n2 = try_or_fail!(self, self.get_number(a2));
@@ -700,6 +713,13 @@ impl MachineState {
                 self.interms[t - 1] = n1 * n2;
                 self.p += 1;
             },
+            &ArithmeticInstruction::Pow(ref a1, ref a2, t) => {
+                let n1 = try_or_fail!(self, self.get_number(a1));
+                let n2 = try_or_fail!(self, self.get_number(a2));
+
+                self.interms[t - 1] = try_or_fail!(self, self.pow(n1, n2));
+                self.p += 1;
+            },
             &ArithmeticInstruction::RDiv(ref a1, ref a2, t) => {
                 let stub = MachineError::functor_stub(clause_name!("(rdiv)"), 2);
 
@@ -723,6 +743,12 @@ impl MachineState {
                 self.interms[t - 1] = Number::Integer(try_or_fail!(self, self.idiv(n1, n2)));
                 self.p += 1;
             },
+            &ArithmeticInstruction::Abs(ref a1, t) => {
+                let n1 = try_or_fail!(self, self.get_number(a1));
+
+                self.interms[t - 1] = n1.abs();
+                self.p += 1;
+            },
             &ArithmeticInstruction::Neg(ref a1, t) => {
                 let n1 = try_or_fail!(self, self.get_number(a1));
 
@@ -802,7 +828,7 @@ impl MachineState {
                         if self.flags.double_quotes.is_chars() => {
                             if let Some(c) = s.head() {
                                 let h = self.heap.h;
-                                
+
                                 self.heap.push(HeapCellValue::Addr(Addr::Con(Constant::Char(c))));
                                 self.heap.push(HeapCellValue::Addr(Addr::Con(Constant::String(s.tail()))));
 
@@ -1368,7 +1394,7 @@ impl MachineState {
                         Ordering::Less
                     },
                 (HeapCellValue::Addr(Addr::Con(Constant::String(ref s))),
-                 HeapCellValue::Addr(Addr::Con(Constant::EmptyList))) 
+                 HeapCellValue::Addr(Addr::Con(Constant::EmptyList)))
                     if self.flags.double_quotes.is_chars() => if s.is_empty() {
                         return Ordering::Equal;
                     } else {
@@ -1463,7 +1489,7 @@ impl MachineState {
                         return Ordering::Less;
                     } else {
                         return n.as_str().cmp(".");
-                    },                
+                    },
                 (HeapCellValue::NamedStr(..), _) =>
                     return Ordering::Greater,
                 (HeapCellValue::Addr(Addr::Lis(_)), _) =>
@@ -1800,7 +1826,7 @@ impl MachineState {
                         }
 
                         return true;
-                    },                
+                    },
                (HeapCellValue::Addr(Addr::Con(Constant::String(ref s1))),
                 HeapCellValue::Addr(Addr::Con(Constant::String(ref s2)))) =>
                     match s1.head() {
@@ -1819,7 +1845,7 @@ impl MachineState {
                  HeapCellValue::Addr(Addr::Con(Constant::String(ref s))))
                     if self.flags.double_quotes.is_chars() => if !s.is_empty() {
                         return true;
-                    },                
+                    },
                 (HeapCellValue::NamedStr(ar1, n1, _), HeapCellValue::NamedStr(ar2, n2, _)) =>
                     if ar1 != ar2 || n1 != n2 {
                         return true;
@@ -1924,14 +1950,14 @@ impl MachineState {
                               use_default_cp: bool)
     {
         let mut default_call_policy: Box<CallPolicy> = Box::new(DefaultCallPolicy {});
-        let call_policy = if use_default_cp {           
+        let call_policy = if use_default_cp {
             &mut default_call_policy
         } else {
             call_policy
         };
 
         self.last_call = lco;
-                
+
         match ct {
             &ClauseType::BuiltIn(ref ct) =>
                 try_or_fail!(self, call_policy.call_builtin(self, ct, code_dirs)),
@@ -1943,7 +1969,7 @@ impl MachineState {
                 try_or_fail!(self, call_policy.context_call(self, name.clone(), arity, idx.clone(),
                                                             code_dirs)),
             &ClauseType::System(ref ct) =>
-                try_or_fail!(self, self.system_call(ct, code_dirs, call_policy, cut_policy))                
+                try_or_fail!(self, self.system_call(ct, code_dirs, call_policy, cut_policy))
         };
     }
 
@@ -1957,7 +1983,7 @@ impl MachineState {
                 self.allocate(num_cells),
             &ControlInstruction::CallClause(ref ct, arity, _, lco, use_default_cp) =>
                 self.handle_call_clause(code_dirs, call_policy, cut_policy, ct, arity, lco,
-                                        use_default_cp),            
+                                        use_default_cp),
             &ControlInstruction::Deallocate => self.deallocate(),
             &ControlInstruction::JmpBy(arity, offset, _, lco) => {
                 if !lco {
@@ -2011,7 +2037,7 @@ impl MachineState {
     pub(super) fn execute_choice_instr(&mut self, instr: &ChoiceInstruction,
                                        call_policy: &mut Box<CallPolicy>)
     {
-        match instr {            
+        match instr {
             &ChoiceInstruction::TryMeElse(offset) => {
                 let n = self.num_of_args;
                 let gi = self.next_global_index();
index 321f2874e5f788d7261f4c9fd90c39384ca9dec9..9356d75dd5efd9454d603e9b7ad2e7d1cf46be58 100644 (file)
@@ -1019,6 +1019,75 @@ fn test_queries_on_arithmetic()
     assert_prolog_success!(&mut wam, "?- f(5, Sum).", [["Sum = 33"]]);
     assert_prolog_success!(&mut wam, "?- f(5, 33).");
     assert_prolog_failure!(&mut wam, "?- f(5, 32).");
+
+    // exponentiation.    
+
+    // the ~ operators tests whether |X - Y| <= 1/10000...
+    // or whatever degree of approximation used by Newton's method in rational_pow.
+    submit(&mut wam, ":- op(900, xfx, ~).");
+    submit(&mut wam, "X ~ Y :- abs(X - Y) =< 1 rdiv 10000.");
+
+    assert_prolog_success!(&mut wam, "?- X is 3 ^ 3.",
+                           [["X = 27"]]);
+    assert_prolog_success!(&mut wam, "?- X is 3 ^ 0.",
+                           [["X = 1"]]);
+    assert_prolog_success!(&mut wam, "?- X is 3 ^ -0.",
+                           [["X = 1"]]);
+    assert_prolog_success!(&mut wam, "?- X is 3 ^ 1.",
+                           [["X = 3"]]);
+    assert_prolog_success!(&mut wam, "?- X is 3 ^ -3.",
+                           [["X = 1/27"]]);
+    assert_prolog_success!(&mut wam, "?- X is (-3) ^ 3.",
+                           [["X = -27"]]);
+    assert_prolog_success!(&mut wam, "?- X is (-3) ^ 3.",
+                           [["X = -27"]]);
+    assert_prolog_success!(&mut wam, "?- X is (-3) ^ 0.",
+                           [["X = 1"]]);
+    assert_prolog_success!(&mut wam, "?- X is (-3) ^ -0.",
+                           [["X = 1"]]);
+    assert_prolog_success!(&mut wam, "?- X is (-3) ^ 1.",
+                           [["X = -3"]]);
+    assert_prolog_success!(&mut wam, "?- X is (-3) ^ -3.",
+                           [["X = -1/27"]]);
+    assert_prolog_success!(&mut wam, "?- X is (1 rdiv 27) ^ -3, X ~ 19683.");
+    assert_prolog_success!(&mut wam, "?- X is (-1 rdiv 27) ^ -3, X ~ -19683.");
+
+    assert_prolog_success!(&mut wam, "?- X is 0.0 ^ 0.",
+                           [["X = 1"]]);
+    assert_prolog_success!(&mut wam, "?- catch(_ is 0.0 ^ -2342, error(E, _), true).",
+                           [["E = evaluation_error(no_roots)"]]);
+    assert_prolog_success!(&mut wam, "?- X is 0.0 ^ 2342.",
+                           [["X = 0"]]);
+
+    assert_prolog_success!(&mut wam, "?- catch(_ is (-3) ^ (1 rdiv 2), error(E, _), true).",
+                           [["E = evaluation_error(no_roots)"]]);
+    assert_prolog_success!(&mut wam, "?- catch(_ is (-3/2) ^ (1 rdiv 2), error(E, _), true).",
+                           [["E = evaluation_error(no_roots)"]]);
+    assert_prolog_success!(&mut wam, "?- catch(_ is (-3 rdiv 2) ^ (1 rdiv 4), error(E, _), true).",
+                           [["E = evaluation_error(no_roots)"]]);
+    assert_prolog_success!(&mut wam, "?- catch(_ is (-3 rdiv 2) ^ (-1 rdiv 4), error(E, _), true).",
+                           [["E = evaluation_error(no_roots)"]]);
+    assert_prolog_success!(&mut wam, "?- catch(_ is 0 ^ (-5 rdiv 4), error(E, _), true).",
+                           [["E = evaluation_error(no_roots)"]]);
+
+    assert_prolog_success!(&mut wam, "?- X is 3 ^ (1 rdiv 3), Y is X ^ 3, Y ~ 3.");
+    assert_prolog_success!(&mut wam, "?- X is (-3) ^ (1 rdiv 3), Y is X ^ 3, Y ~ -3.");
+    assert_prolog_failure!(&mut wam, "?- X is (-5) ^ (1 rdiv 3), Y is X ^ 3, Y ~ -3.");
+    assert_prolog_failure!(&mut wam, "?- X is 5 ^ (1 rdiv 3), Y is X ^ 3, Y ~ 3.");
+    assert_prolog_failure!(&mut wam, "?- X is (1 rdiv 3) ^ 0.5, Y is X ^ 2, X ~ Y.");
+    assert_prolog_success!(&mut wam, "?- X is (1 rdiv 3) ^ 0.5, Y is X ^ 2, 1 rdiv 3 ~ Y.");
+
+    assert_prolog_success!(&mut wam, "?- X is (-5) ^ (-1 rdiv 3), Y is X ^ 3, Y ~ -1 rdiv 5.");
+    assert_prolog_failure!(&mut wam, "?- X is (-5) ^ (-1 rdiv 3), Y is X ^ 3, Y ~ 1 rdiv 5.");    
+    
+    assert_prolog_success!(&mut wam, "?- X is (0 rdiv 5) ^ 5.",
+                           [["X = 0"]]);
+    assert_prolog_success!(&mut wam, "?- X is (-0 rdiv 5) ^ 5.",
+                           [["X = 0"]]);
+    assert_prolog_success!(&mut wam, "?- X is (0 rdiv 5) ^ 0.",
+                           [["X = 1"]]);
+    assert_prolog_success!(&mut wam, "?- catch(_ is (0 rdiv 0) ^ 5, error(E, _), true).",
+                           [["E = evaluation_error(zero_divisor)"]]);
 }
 
 #[test]
@@ -1788,7 +1857,7 @@ fn test_queries_on_string_lists()
     assert_prolog_failure!(&mut wam, "?- matcher(\"abcdef\", Y).");
 
     submit(&mut wam, "?- set_prolog_flag(double_quotes, chars).");
-    
+
     assert_prolog_success!(&mut wam, "?- X = \"abc\", X = ['a' | Y], set_prolog_flag(double_quotes, atom).",
                            [["X = \"abc\"", "Y = \"bc\""]]);
 }