use std::collections::BTreeMap;
use std::collections::{HashMap, HashSet};
use std::fmt::Write;
use std::rc::Rc;
use std::string::ToString;
use std::sync::{Arc, RwLock};
use ::log::trace;
use super::debugger::{DebugEvent, Debugger};
use super::error::{self, PolarResult};
use super::events::*;
use super::formatting::ToPolarString;
use super::kb::*;
use super::lexer::loc_to_pos;
use super::messages::*;
use super::numerics::*;
use super::rules::*;
use super::sources::*;
use super::terms::*;
use super::traces::*;
pub const MAX_STACK_SIZE: usize = 10_000;
#[cfg(not(target_arch = "wasm32"))]
pub const QUERY_TIMEOUT_S: std::time::Duration = std::time::Duration::from_secs(30);
#[cfg(target_arch = "wasm32")]
pub const QUERY_TIMEOUT_S: f64 = 30_000.0;
#[derive(Clone, Debug)]
#[must_use = "ignored goals are never accomplished"]
#[allow(clippy::large_enum_variant)]
pub enum Goal {
    Backtrack,
    Cut {
        choice_index: usize, 
    },
    Debug {
        message: String,
    },
    Halt,
    Isa {
        left: Term,
        right: Term,
    },
    IsMoreSpecific {
        left: Arc<Rule>,
        right: Arc<Rule>,
        args: TermList,
    },
    IsSubspecializer {
        answer: Symbol,
        left: Term,
        right: Term,
        arg: Term,
    },
    Lookup {
        dict: Dictionary,
        field: Term,
        value: Term,
    },
    LookupExternal {
        call_id: u64,
        instance: Term,
        field: Term,
        check_errors: bool,
    },
    MakeExternal {
        constructor: Term,
        instance_id: u64,
    },
    IsaExternal {
        instance: Term,
        literal: InstanceLiteral,
    },
    UnifyExternal {
        left_instance_id: u64,
        right_instance_id: u64,
    },
    CheckError,
    Noop,
    Query {
        term: Term,
    },
    PopQuery {
        term: Term,
    },
    FilterRules {
        args: TermList,
        applicable_rules: Rules,
        unfiltered_rules: Rules,
    },
    SortRules {
        args: TermList,
        rules: Rules,
        outer: usize,
        inner: usize,
    },
    TraceRule {
        trace: Rc<Trace>,
    },
    TracePush,
    TracePop,
    Unify {
        left: Term,
        right: Term,
    },
}
#[derive(Clone, Debug)]
pub struct Binding(pub Symbol, pub Term);
#[derive(Clone, Debug)]
pub struct Choice {
    pub alternatives: Vec<GoalStack>,
    bsp: usize,            
    pub goals: GoalStack,  
    queries: Queries,      
    trace: Vec<Rc<Trace>>, 
    trace_stack: TraceStack,
}
pub type BindingStack = Vec<Binding>;
pub type Choices = Vec<Choice>;
pub type Goals = Vec<Goal>;
pub type TraceStack = Vec<Rc<Vec<Rc<Trace>>>>;
#[derive(Clone, Debug, Default)]
pub struct GoalStack(Vec<Rc<Goal>>);
impl GoalStack {
    fn new_reversed(goals: Goals) -> Self {
        Self(goals.into_iter().rev().map(Rc::new).collect())
    }
}
impl std::ops::Deref for GoalStack {
    type Target = Vec<Rc<Goal>>;
    fn deref(&self) -> &Self::Target {
        &self.0
    }
}
impl std::ops::DerefMut for GoalStack {
    fn deref_mut(&mut self) -> &mut Self::Target {
        &mut self.0
    }
}
pub type Queries = TermList;
pub struct PolarVirtualMachine {
    
    pub goals: GoalStack,
    pub bindings: BindingStack,
    choices: Choices,
    pub queries: Queries,
    pub tracing: bool,
    pub trace_stack: TraceStack, 
    pub trace: Vec<Rc<Trace>>,   
    
    pub external_error: Option<String>,
    #[cfg(not(target_arch = "wasm32"))]
    query_start_time: Option<std::time::Instant>,
    #[cfg(target_arch = "wasm32")]
    query_start_time: Option<f64>,
    #[cfg(not(target_arch = "wasm32"))]
    query_timeout: std::time::Duration,
    #[cfg(target_arch = "wasm32")]
    query_timeout: f64,
    
    stack_limit: usize,
    
    csp: usize,
    
    pub debugger: Debugger,
    
    pub kb: Arc<RwLock<KnowledgeBase>>,
    
    call_id_symbols: HashMap<u64, Symbol>,
    
    log: bool,
    polar_log: bool,
    polar_log_mute: bool,
    
    pub messages: MessageQueue,
}
impl Default for PolarVirtualMachine {
    fn default() -> Self {
        PolarVirtualMachine::new(
            Arc::new(RwLock::new(KnowledgeBase::default())),
            false,
            vec![],
            
            MessageQueue::new(),
        )
    }
}
impl PolarVirtualMachine {
    
    
    pub fn new(
        kb: Arc<RwLock<KnowledgeBase>>,
        trace: bool,
        goals: Goals,
        messages: MessageQueue,
    ) -> Self {
        let constants = kb
            .read()
            .expect("cannot acquire KB read lock")
            .constants
            .clone();
        let mut vm = Self {
            goals: GoalStack::new_reversed(goals),
            bindings: vec![],
            query_start_time: None,
            query_timeout: QUERY_TIMEOUT_S,
            stack_limit: MAX_STACK_SIZE,
            csp: 0,
            choices: vec![],
            queries: vec![],
            tracing: trace,
            trace_stack: vec![],
            trace: vec![],
            external_error: None,
            debugger: Debugger::default(),
            kb,
            call_id_symbols: HashMap::new(),
            log: std::env::var("RUST_LOG").is_ok(),
            polar_log: std::env::var("POLAR_LOG").is_ok(),
            polar_log_mute: false,
            messages,
        };
        vm.bind_constants(constants);
        vm
    }
    pub fn new_test(kb: Arc<RwLock<KnowledgeBase>>, trace: bool, goals: Goals) -> Self {
        PolarVirtualMachine::new(kb, trace, goals, MessageQueue::new())
    }
    #[cfg(test)]
    fn set_stack_limit(&mut self, limit: usize) {
        self.stack_limit = limit;
    }
    #[cfg(test)]
    fn set_query_timeout(&mut self, timeout_s: u64) {
        self.query_timeout = std::time::Duration::from_secs(timeout_s);
    }
    pub fn new_id(&self) -> u64 {
        self.kb
            .read()
            .expect("cannot acquire KB read lock")
            .new_id()
    }
    fn new_call_id(&mut self, symbol: &Symbol) -> u64 {
        let call_id = self.new_id();
        self.call_id_symbols.insert(call_id, symbol.clone());
        call_id
    }
    
    
    fn next(&mut self, goal: Rc<Goal>) -> PolarResult<QueryEvent> {
        if self.log {
            self.print(&format!("{}", goal));
        }
        self.check_timeout()?;
        match goal.as_ref() {
            Goal::Backtrack => self.backtrack()?,
            Goal::Cut { choice_index } => self.cut(*choice_index),
            Goal::Debug { message } => return Ok(self.debug(&message)),
            Goal::Halt => return Ok(self.halt()),
            Goal::Isa { left, right } => self.isa(&left, &right)?,
            Goal::IsMoreSpecific { left, right, args } => {
                self.is_more_specific(left, right, args)?
            }
            Goal::IsSubspecializer {
                answer,
                left,
                right,
                arg,
            } => return self.is_subspecializer(answer, left, right, arg),
            Goal::Lookup { dict, field, value } => self.lookup(dict, field, value)?,
            Goal::LookupExternal {
                call_id,
                instance,
                field,
                check_errors,
            } => return self.lookup_external(*call_id, instance, field, *check_errors),
            Goal::IsaExternal { instance, literal } => return self.isa_external(instance, literal),
            Goal::UnifyExternal {
                left_instance_id,
                right_instance_id,
            } => return self.unify_external(*left_instance_id, *right_instance_id),
            Goal::MakeExternal {
                constructor,
                instance_id,
            } => return Ok(self.make_external(constructor, *instance_id)),
            Goal::CheckError => return self.check_error(),
            Goal::Noop => {}
            Goal::Query { term } => {
                let result = self.query(term);
                self.maybe_break(DebugEvent::Query)?;
                return result;
            }
            Goal::PopQuery { .. } => self.pop_query(),
            Goal::FilterRules {
                applicable_rules,
                unfiltered_rules,
                args,
            } => self.filter_rules(applicable_rules, unfiltered_rules, args)?,
            Goal::SortRules {
                rules,
                outer,
                inner,
                args,
            } => self.sort_rules(rules, args, *outer, *inner)?,
            Goal::TracePush => {
                self.trace_stack.push(Rc::new(self.trace.clone()));
                self.trace = vec![];
            }
            Goal::TracePop => {
                let mut children = self.trace.clone();
                self.trace = self.trace_stack.pop().unwrap().as_ref().clone();
                let mut trace = self.trace.pop().unwrap();
                let trace = Rc::make_mut(&mut trace);
                trace.children.append(&mut children);
                self.trace.push(Rc::new(trace.clone()));
            }
            Goal::TraceRule { trace } => {
                if let Node::Rule(rule) = &trace.node {
                    self.log_with(
                        || {
                            let source_str = self.rule_source(&rule);
                            format!("RULE: {}", source_str)
                        },
                        &[],
                    );
                }
                self.trace.push(trace.clone());
            }
            Goal::Unify { left, right } => self.unify(&left, &right)?,
        }
        Ok(QueryEvent::None)
    }
    
    
    
    
    pub fn run(&mut self) -> PolarResult<QueryEvent> {
        if self.query_start_time.is_none() {
            #[cfg(not(target_arch = "wasm32"))]
            let query_start_time = Some(std::time::Instant::now());
            #[cfg(target_arch = "wasm32")]
            let query_start_time = Some(js_sys::Date::now());
            self.query_start_time = query_start_time;
        }
        if self.goals.is_empty() {
            if self.choices.is_empty() {
                return Ok(QueryEvent::Done);
            } else {
                self.backtrack()?;
            }
        }
        while let Some(goal) = self.goals.pop() {
            match self.next(goal.clone())? {
                QueryEvent::None => (),
                event => {
                    self.external_error = None;
                    return Ok(event);
                }
            }
            self.maybe_break(DebugEvent::Goal(goal.clone()))?;
        }
        if self.log {
            self.print("⇒ result");
            if self.tracing {
                for t in &self.trace {
                    self.print(&format!("trace\n{}", t.draw(&self)));
                }
            }
        }
        let trace = if self.tracing {
            let trace = self.trace.first().cloned();
            trace.map(|trace| TraceResult {
                formatted: trace.draw(&self),
                trace,
            })
        } else {
            None
        };
        Ok(QueryEvent::Result {
            bindings: self.bindings(false),
            trace,
        })
    }
    
    pub fn is_halted(&self) -> bool {
        self.goals.is_empty() && self.choices.is_empty()
    }
    
    pub fn push_goal(&mut self, goal: Goal) -> PolarResult<()> {
        if self.goals.len() >= self.stack_limit {
            return Err(error::RuntimeError::StackOverflow {
                msg: format!("Goal stack overflow! MAX_GOALS = {}", self.stack_limit),
            }
            .into());
        }
        self.goals.push(Rc::new(goal));
        Ok(())
    }
    
    
    
    
    
    
    
    
    
    
    fn push_choice<I>(&mut self, alternatives: I)
    where
        I: IntoIterator<Item = Goals>,
        I::IntoIter: std::iter::DoubleEndedIterator,
    {
        
        let alternatives = alternatives
            .into_iter()
            .rev()
            .map(GoalStack::new_reversed)
            .collect();
        assert!(self.choices.len() < self.stack_limit, "too many choices");
        self.choices.push(Choice {
            alternatives,
            bsp: self.bsp(),
            goals: self.goals.clone(),
            queries: self.queries.clone(),
            trace: self.trace.clone(),
            trace_stack: self.trace_stack.clone(),
        });
    }
    
    
    
    
    
    
    
    fn choose<I>(&mut self, alternatives: I) -> PolarResult<()>
    where
        I: IntoIterator<Item = Goals>,
        I::IntoIter: std::iter::DoubleEndedIterator,
    {
        let mut alternatives_iter = alternatives.into_iter();
        if let Some(alternative) = alternatives_iter.next() {
            self.push_choice(alternatives_iter);
            self.append_goals(alternative)?;
            Ok(())
        } else {
            self.backtrack()
        }
    }
    
    
    
    
    fn choose_conditional(
        &mut self,
        mut conditional: Goals,
        consequent: Goals,
        mut alternative: Goals,
    ) -> PolarResult<()> {
        
        let cut_consequent = Goal::Cut {
            choice_index: self.choices.len(),
        };
        alternative.insert(0, cut_consequent);
        
        self.push_choice(vec![consequent]);
        let cut_alternative = Goal::Cut {
            choice_index: self.choices.len(),
        };
        conditional.push(cut_alternative);
        conditional.push(Goal::Backtrack);
        self.choose(vec![conditional, alternative])?;
        Ok(())
    }
    
    fn append_goals<I>(&mut self, goals: I) -> PolarResult<()>
    where
        I: IntoIterator<Item = Goal>,
        I::IntoIter: std::iter::DoubleEndedIterator,
    {
        goals
            .into_iter()
            .rev()
            .try_for_each(|goal| self.push_goal(goal))
    }
    
    fn bind(&mut self, var: &Symbol, value: Term) {
        if self.log {
            self.print(&format!(
                "⇒ bind: {} ← {}",
                var.to_polar(),
                value.to_polar()
            ));
        }
        self.bindings.push(Binding(var.clone(), value));
    }
    
    
    pub fn bind_constants(&mut self, bindings: Bindings) {
        assert_eq!(self.bsp(), self.csp);
        for (var, value) in bindings.iter() {
            self.bind(var, value.clone());
        }
        self.csp += bindings.len();
    }
    
    pub fn bindings(&self, include_temps: bool) -> Bindings {
        let mut bindings = HashMap::new();
        for Binding(var, value) in &self.bindings[self.csp..] {
            if !include_temps && self.is_temporary_var(&var) {
                continue;
            }
            bindings.insert(var.clone(), self.deep_deref(value));
        }
        bindings
    }
    
    pub fn variable_bindings(&self, variables: &HashSet<Symbol>) -> Bindings {
        let mut bindings = HashMap::new();
        for Binding(var, value) in &self.bindings[self.csp..] {
            if !variables.contains(var) {
                continue;
            }
            bindings.insert(var.clone(), self.deep_deref(value));
        }
        bindings
    }
    
    pub fn relevant_bindings(&self, terms: &[&Term]) -> Bindings {
        let mut variables = HashSet::new();
        for t in terms {
            t.variables(&mut variables);
        }
        self.variable_bindings(&variables)
    }
    
    fn bsp(&self) -> usize {
        self.bindings.len()
    }
    
    
    fn value(&self, variable: &Symbol) -> Option<&Term> {
        self.bindings
            .iter()
            .rev()
            .find(|binding| binding.0 == *variable)
            .map(|binding| &binding.1)
    }
    pub fn deep_deref(&self, term: &Term) -> Term {
        term.cloned_map_replace(&mut |t| self.deref(t))
    }
    
    pub fn deref(&self, term: &Term) -> Term {
        match &term.value() {
            Value::List(list) => {
                
                let mut rest = false;
                if let Some(last) = list.last() {
                    if matches!(last.value(), Value::RestVariable(_)) {
                        rest = true;
                    }
                }
                
                let mut derefed: Vec<Term> = list.iter().map(|t| self.deref(t)).collect();
                
                if rest {
                    if let Some(last_term) = derefed.pop() {
                        if let Value::List(terms) = last_term.value() {
                            derefed.append(&mut terms.clone());
                        } else {
                            derefed.push(last_term);
                        }
                    }
                }
                term.clone_with_value(Value::List(derefed))
            }
            Value::Variable(symbol) | Value::RestVariable(symbol) => {
                self.value(&symbol).map_or(term.clone(), |t| {
                    if t == term {
                        t.clone()
                    } else {
                        self.deref(t)
                    }
                })
            }
            _ => term.clone(),
        }
    }
    
    fn is_temporary_var(&self, name: &Symbol) -> bool {
        name.0.starts_with('_')
    }
    
    fn is_constant_var(&self, name: &Symbol) -> bool {
        self.bindings
            .iter()
            .take(self.csp)
            .any(|binding| binding.0 == *name)
    }
    
    fn rename_rule_vars(&self, rule: &Rule) -> Rule {
        let mut renames = HashMap::<Symbol, Symbol>::new();
        let mut rule = rule.clone();
        rule.map_replace(&mut move |term| match term.value() {
            Value::Variable(sym) if !self.is_constant_var(sym) => {
                if let Some(new) = renames.get(sym) {
                    term.clone_with_value(Value::Variable(new.clone()))
                } else {
                    let new = self.kb.read().unwrap().gensym(&sym.0);
                    renames.insert(sym.clone(), new.clone());
                    term.clone_with_value(Value::Variable(new))
                }
            }
            Value::RestVariable(sym) => {
                if let Some(new) = renames.get(sym) {
                    term.clone_with_value(Value::RestVariable(new.clone()))
                } else {
                    let new = self.kb.read().unwrap().gensym(&sym.0);
                    renames.insert(sym.clone(), new.clone());
                    term.clone_with_value(Value::RestVariable(new))
                }
            }
            _ => term.clone(),
        });
        rule
    }
    
    fn print<S: Into<String>>(&self, message: S) {
        let message = message.into();
        trace!("{}", &message);
        self.messages.push(MessageKind::Print, message);
    }
    fn log(&self, message: &str, terms: &[&Term]) {
        self.log_with(|| message, terms)
    }
    fn log_with<F, R>(&self, message_fn: F, terms: &[&Term])
    where
        F: FnOnce() -> R,
        R: AsRef<str>,
    {
        if self.polar_log && !self.polar_log_mute {
            let mut indent = String::new();
            for _ in 0..=self.queries.len() {
                indent.push_str("  ");
            }
            let message = message_fn();
            let lines = message.as_ref().split('\n').collect::<Vec<&str>>();
            if let Some(line) = lines.first() {
                let mut msg = format!("[debug] {}{}", &indent, line);
                if !terms.is_empty() {
                    let relevant_bindings = self.relevant_bindings(terms);
                    msg.push_str(&format!(
                        ", BINDINGS: {{{}}}",
                        relevant_bindings
                            .iter()
                            .map(|(var, val)| format!("{} = {}", var.0, val.to_polar()))
                            .collect::<Vec<String>>()
                            .join(", ")
                    ));
                }
                self.print(msg);
                for line in &lines[1..] {
                    self.print(format!("[debug] {}{}", &indent, line));
                }
            }
        }
    }
    pub fn source(&self, term: &Term) -> Option<Source> {
        term.get_source_id()
            .and_then(|id| self.kb.read().unwrap().sources.get_source(id))
    }
    
    pub fn stack_trace(&self) -> String {
        let mut trace_stack = self.trace_stack.clone();
        let mut trace = self.trace.clone();
        
        
        let mut stack = vec![];
        while let Some(t) = trace.last() {
            stack.push(t.clone());
            trace = trace_stack
                .pop()
                .map(|ts| ts.as_ref().clone())
                .unwrap_or_else(Vec::new);
        }
        stack.reverse();
        let mut st = String::new();
        let _ = write!(st, "trace (most recent evaluation last):");
        let mut rule = None;
        for t in stack {
            match &t.node {
                Node::Rule(r) => {
                    rule = Some(r.clone());
                }
                Node::Term(t) => {
                    if matches!(t.value(), Value::Expression(Operation { operator: Operator::And, args}) if args.len() == 1)
                    {
                        continue;
                    }
                    let _ = write!(st, "\n  ");
                    if let Some(source) = self.source(t) {
                        if let Some(rule) = &rule {
                            let _ = write!(st, "in rule {} ", rule.name.to_polar());
                        } else {
                            let _ = write!(st, "in query ");
                        }
                        let (row, column) = loc_to_pos(&source.src, t.offset());
                        let _ = write!(st, "at line {}, column {}", row + 1, column + 1);
                        if let Some(filename) = source.filename {
                            let _ = write!(st, " in file {}", filename);
                        }
                        let _ = writeln!(st);
                    };
                    let _ = write!(st, "    {}", self.term_source(t, false));
                }
            }
        }
        st
    }
    #[cfg(not(target_arch = "wasm32"))]
    fn check_timeout(&self) -> PolarResult<()> {
        let now = std::time::Instant::now();
        let start_time = self
            .query_start_time
            .expect("Query start time not recorded");
        if now - start_time > self.query_timeout {
            return Err(error::RuntimeError::QueryTimeout {
                msg: format!(
                    "Query running for {}. Exceeded query timeout of {} seconds",
                    (now - start_time).as_secs(),
                    self.query_timeout.as_secs()
                ),
            }
            .into());
        }
        Ok(())
    }
    #[cfg(target_arch = "wasm32")]
    fn check_timeout(&self) -> PolarResult<()> {
        let now = js_sys::Date::now();
        let start_time = self
            .query_start_time
            .expect("Query start time not recorded");
        if now - start_time > self.query_timeout {
            return Err(error::RuntimeError::QueryTimeout {
                msg: format!(
                    "Query running for {}. Exceeded query timeout of {} seconds",
                    (now - start_time) / 1_000.0,
                    self.query_timeout / 1_000.0
                ),
            }
            .into());
        }
        Ok(())
    }
}
impl PolarVirtualMachine {
    
    
    fn backtrack(&mut self) -> PolarResult<()> {
        if self.log {
            self.print("⇒ backtrack");
        }
        self.log("BACKTRACK", &[]);
        loop {
            match self.choices.pop() {
                None => return self.push_goal(Goal::Halt),
                Some(Choice {
                    mut alternatives,
                    bsp,
                    goals,
                    queries,
                    trace,
                    trace_stack,
                }) => {
                    self.bindings.truncate(bsp);
                    if let Some(mut alternative) = alternatives.pop() {
                        if alternatives.is_empty() {
                            self.goals = goals;
                            self.queries = queries;
                            self.trace = trace;
                            self.trace_stack = trace_stack;
                        } else {
                            self.goals.clone_from(&goals);
                            self.queries.clone_from(&queries);
                            self.trace.clone_from(&trace);
                            self.trace_stack.clone_from(&trace_stack);
                            self.choices.push(Choice {
                                alternatives,
                                bsp,
                                goals,
                                queries,
                                trace,
                                trace_stack,
                            })
                        }
                        self.goals.append(&mut alternative);
                        break;
                    }
                }
            }
        }
        Ok(())
    }
    
    fn cut(&mut self, index: usize) {
        let _ = self.choices.truncate(index);
    }
    
    fn pop_query(&mut self) {
        self.queries.pop();
    }
    
    fn debug(&mut self, message: &str) -> QueryEvent {
        
        self.query_start_time.take();
        QueryEvent::Debug {
            message: message.to_string(),
        }
    }
    
    pub fn halt(&mut self) -> QueryEvent {
        self.log("HALT", &[]);
        self.goals.clear();
        self.choices.clear();
        assert!(self.is_halted());
        QueryEvent::Done
    }
    
    pub fn isa(&mut self, left: &Term, right: &Term) -> PolarResult<()> {
        
        
        
        
        assert!(
            !matches!(&right.value(), Value::InstanceLiteral(_)),
            "Called isa with bare instance lit!"
        );
        assert!(
            !matches!(&right.value(), Value::Dictionary(_)),
            "Called isa with bare dictionary!"
        );
        self.log_with(
            || format!("MATCHES: {} matches {}", left.to_polar(), right.to_polar()),
            &[left, right],
        );
        match (&left.value(), &right.value()) {
            (Value::List(left), Value::List(right)) => {
                self.unify_lists(left, right, |(left, right)| Goal::Isa {
                    left: left.clone(),
                    right: right.clone(),
                })?;
            }
            (Value::Dictionary(left), Value::Pattern(Pattern::Dictionary(right))) => {
                
                let left_fields: HashSet<&Symbol> = left.fields.keys().collect();
                let right_fields: HashSet<&Symbol> = right.fields.keys().collect();
                if !right_fields.is_subset(&left_fields) {
                    return self.push_goal(Goal::Backtrack);
                }
                
                
                for (k, v) in right.fields.iter() {
                    let left = left
                        .fields
                        .get(&k)
                        .expect("left fields should be a superset of right fields")
                        .clone();
                    self.push_goal(Goal::Isa {
                        left,
                        right: v.clone(),
                    })?
                }
            }
            (Value::InstanceLiteral(_), _) => {
                panic!("How did an instance literal get here???");
            }
            (Value::ExternalInstance(_), Value::Pattern(Pattern::Dictionary(right))) => {
                
                
                for (field, right_value) in right.fields.iter() {
                    let left_value = self.kb.read().unwrap().gensym("isa_value");
                    let call_id = self.new_call_id(&left_value);
                    let lookup = Goal::LookupExternal {
                        instance: left.clone(),
                        call_id,
                        field: right_value.clone_with_value(Value::String(field.0.clone())),
                        check_errors: false,
                    };
                    let isa = Goal::Isa {
                        left: left.clone_with_value(Value::Variable(left_value)),
                        right: right_value.clone(),
                    };
                    self.append_goals(vec![lookup, isa])?;
                }
            }
            (Value::Variable(symbol), _) => {
                if let Some(value) = self.value(&symbol).cloned() {
                    self.push_goal(Goal::Isa {
                        left: value,
                        right: right.clone(),
                    })?;
                } else {
                    self.push_goal(Goal::Unify {
                        left: left.clone(),
                        right: right.clone(),
                    })?;
                }
            }
            (_, Value::Variable(symbol)) => {
                if let Some(value) = self.value(&symbol).cloned() {
                    self.push_goal(Goal::Isa {
                        left: left.clone(),
                        right: value,
                    })?;
                } else {
                    self.push_goal(Goal::Unify {
                        left: left.clone(),
                        right: right.clone(),
                    })?;
                }
            }
            (Value::RestVariable(symbol), _) => {
                if let Some(value) = self.value(&symbol).cloned() {
                    self.push_goal(Goal::Isa {
                        left: value,
                        right: right.clone(),
                    })?;
                } else {
                    self.push_goal(Goal::Unify {
                        left: left.clone(),
                        right: right.clone(),
                    })?;
                }
            }
            (_, Value::RestVariable(symbol)) => {
                if let Some(value) = self.value(&symbol).cloned() {
                    self.push_goal(Goal::Isa {
                        left: left.clone(),
                        right: value,
                    })?;
                } else {
                    self.push_goal(Goal::Unify {
                        left: left.clone(),
                        right: right.clone(),
                    })?;
                }
            }
            (
                Value::ExternalInstance(left_instance),
                Value::Pattern(Pattern::Instance(right_literal)),
            ) => {
                
                self.push_goal(Goal::Isa {
                    left: left.clone_with_value(Value::ExternalInstance(left_instance.clone())),
                    right: right.clone_with_value(Value::Pattern(Pattern::Dictionary(
                        right_literal.fields.clone(),
                    ))),
                })?;
                
                self.push_goal(Goal::IsaExternal {
                    instance: left.clone(),
                    literal: right_literal.clone(),
                })?;
            }
            (_, Value::Pattern(Pattern::Instance(right_literal))) => {
                self.push_goal(Goal::IsaExternal {
                    instance: left.clone(),
                    literal: right_literal.clone(),
                })?;
            }
            
            _ => self.push_goal(Goal::Unify {
                left: left.clone(),
                right: right.clone(),
            })?,
        }
        Ok(())
    }
    pub fn lookup(&mut self, dict: &Dictionary, field: &Term, value: &Term) -> PolarResult<()> {
        
        let field = self.deref(&field);
        match field.value() {
            Value::Variable(_) => {
                let mut alternatives = vec![];
                for (k, v) in &dict.fields {
                    let mut goals: Goals = vec![];
                    
                    
                    
                    goals.push(Goal::Unify {
                        left: field.clone_with_value(Value::String(k.clone().0)),
                        right: field.clone(),
                    });
                    
                    goals.push(Goal::Unify {
                        left: v.clone(),
                        right: value.clone(),
                    });
                    alternatives.push(goals);
                }
                self.choose(alternatives)?;
            }
            Value::String(field) => {
                if let Some(retrieved) = dict.fields.get(&Symbol(field.clone())) {
                    self.push_goal(Goal::Unify {
                        left: retrieved.clone(),
                        right: value.clone(),
                    })?;
                } else {
                    self.push_goal(Goal::Backtrack)?;
                }
            }
            v => {
                return Err(self.type_error(
                    &field,
                    format!("cannot look up field {:?} on a dictionary", v),
                ))
            }
        };
        Ok(())
    }
    
    
    
    pub fn lookup_external(
        &mut self,
        call_id: u64,
        instance: &Term,
        field: &Term,
        check_errors: bool,
    ) -> PolarResult<QueryEvent> {
        let (field_name, args, kwargs): (
            Symbol,
            Option<Vec<Term>>,
            Option<BTreeMap<Symbol, Term>>,
        ) = match self.deref(field).value() {
            Value::Call(Call { name, args, kwargs }) => (
                name.clone(),
                Some(args.iter().map(|arg| self.deep_deref(arg)).collect()),
                match kwargs {
                    Some(unwrapped) => Some(
                        unwrapped
                            .iter()
                            .map(|(k, v)| (k.to_owned(), self.deep_deref(v)))
                            .collect(),
                    ),
                    None => None,
                },
            ),
            Value::String(field) => (Symbol(field.clone()), None, None),
            v => {
                return Err(self.type_error(
                    &field,
                    format!("cannot look up field {:?} on an external instance", v),
                ))
            }
        };
        self.push_choice(vec![vec![Goal::LookupExternal {
            call_id,
            instance: instance.clone(),
            field: field.clone(),
            check_errors,
        }]]);
        if check_errors {
            self.push_goal(Goal::CheckError)?;
        }
        self.log_with(
            || {
                let mut msg = format!("LOOKUP: {}.{}", instance.to_string(), field_name);
                msg.push('(');
                let args = args
                    .clone()
                    .unwrap_or_else(Vec::new)
                    .into_iter()
                    .map(|a| a.to_polar());
                let kwargs = kwargs
                    .clone()
                    .unwrap_or_else(BTreeMap::new)
                    .into_iter()
                    .map(|(k, v)| format!("{}: {}", k, v.to_polar()));
                msg.push_str(&args.chain(kwargs).collect::<Vec<String>>().join(", "));
                msg.push(')');
                msg
            },
            &[],
        );
        Ok(QueryEvent::ExternalCall {
            call_id,
            instance: self.deep_deref(instance),
            attribute: field_name,
            args,
            kwargs,
        })
    }
    pub fn isa_external(
        &mut self,
        instance: &Term,
        literal: &InstanceLiteral,
    ) -> PolarResult<QueryEvent> {
        let result = self.kb.read().unwrap().gensym("isa");
        let call_id = self.new_call_id(&result);
        self.bind(&result, Term::new_temporary(Value::Boolean(false)));
        self.push_goal(Goal::Unify {
            left: Term::new_temporary(Value::Variable(result)),
            right: Term::new_temporary(Value::Boolean(true)),
        })?;
        Ok(QueryEvent::ExternalIsa {
            call_id,
            instance: self.deep_deref(instance),
            class_tag: literal.tag.clone(),
        })
    }
    pub fn unify_external(
        &mut self,
        left_instance_id: u64,
        right_instance_id: u64,
    ) -> PolarResult<QueryEvent> {
        let result = self.kb.read().unwrap().gensym("unify");
        let call_id = self.new_call_id(&result);
        self.bind(&result, Term::new_temporary(Value::Boolean(false)));
        self.push_goal(Goal::Unify {
            left: Term::new_temporary(Value::Variable(result)),
            right: Term::new_temporary(Value::Boolean(true)),
        })?;
        Ok(QueryEvent::ExternalUnify {
            call_id,
            left_instance_id,
            right_instance_id,
        })
    }
    pub fn make_external(&self, constructor: &Term, instance_id: u64) -> QueryEvent {
        QueryEvent::MakeExternal {
            instance_id,
            constructor: self.deep_deref(&constructor),
        }
    }
    pub fn check_error(&self) -> PolarResult<QueryEvent> {
        if let Some(error) = &self.external_error {
            let trace = self.trace.last().unwrap();
            let term = match &trace.node {
                Node::Term(t) => Some(t),
                _ => None,
            };
            let stack_trace = self.stack_trace();
            let error = error::RuntimeError::Application {
                msg: error.clone(),
                stack_trace: Some(stack_trace),
            };
            if let Some(term) = term {
                Err(self.set_error_context(term, error))
            } else {
                Err(error.into())
            }
        } else {
            Ok(QueryEvent::None)
        }
    }
    
    
    
    
    
    
    fn query(&mut self, term: &Term) -> PolarResult<QueryEvent> {
        
        match &term.value() {
            Value::Expression(Operation {
                operator: Operator::And,
                args,
            }) if args.len() < 2 => (),
            _ => {
                self.log_with(|| format!("QUERY: {}", term.to_polar()), &[term]);
            }
        };
        self.queries.push(term.clone());
        self.push_goal(Goal::PopQuery { term: term.clone() })?;
        self.trace.push(Rc::new(Trace {
            node: Node::Term(term.clone()),
            children: vec![],
        }));
        match &term.value() {
            Value::Call(predicate) => {
                self.query_for_predicate(predicate.clone())?;
            }
            Value::Expression(Operation { operator, args }) => {
                return self.query_for_operation(&term, *operator, args.clone());
            }
            _ => {
                let term = self.deref(term);
                self.query_for_value(&term)?;
            }
        }
        Ok(QueryEvent::None)
    }
    
    
    
    fn query_for_predicate(&mut self, predicate: Call) -> PolarResult<()> {
        assert!(predicate.kwargs.is_none());
        let goals = match self.kb.read().unwrap().rules.get(&predicate.name) {
            None => vec![Goal::Backtrack],
            Some(generic_rule) => {
                assert_eq!(generic_rule.name, predicate.name);
                
                let args = predicate.args.iter().map(|t| self.deep_deref(&t)).collect();
                let pre_filter = generic_rule.get_applicable_rules(&args);
                self.polar_log_mute = true;
                
                vec![
                    Goal::TracePush,
                    Goal::FilterRules {
                        applicable_rules: vec![],
                        unfiltered_rules: pre_filter,
                        args: predicate.args,
                    },
                    Goal::TracePop,
                ]
            }
        };
        self.append_goals(goals)
    }
    fn query_for_operation(
        &mut self,
        term: &Term,
        operator: Operator,
        mut args: Vec<Term>,
    ) -> PolarResult<QueryEvent> {
        match operator {
            Operator::And => {
                
                self.push_goal(Goal::TracePop)?;
                self.append_goals(args.into_iter().map(|term| Goal::Query { term }))?;
                self.push_goal(Goal::TracePush)?;
            }
            Operator::Or => {
                
                self.choose(args.into_iter().map(|term| vec![Goal::Query { term }]))?;
            }
            Operator::Not => {
                
                assert_eq!(args.len(), 1);
                let term = args.pop().unwrap();
                let alternatives = vec![
                    vec![
                        Goal::Query { term },
                        Goal::Cut {
                            choice_index: self.choices.len(),
                        },
                        Goal::Backtrack,
                    ],
                    vec![Goal::Noop],
                ];
                self.choose(alternatives)?;
            }
            Operator::Assign => {
                assert_eq!(args.len(), 2);
                let right = args.pop().unwrap();
                let left = args.pop().unwrap();
                match (left.value(), right.value()) {
                    (Value::Variable(var), _) => match self.value(var) {
                        None => self.push_goal(Goal::Unify { left, right })?,
                        Some(value) => {
                            return Err(self.type_error( &left, format!("Can only assign to unbound variables, {} is bound to value {}.", var.to_polar(), value.to_polar())));
                        }
                    },
                    _ => {
                        return Err(self.type_error(
                            &left,
                            format!("Cannot assign to type {}.", left.to_polar()),
                        ))
                    }
                }
            }
            Operator::Unify => {
                
                assert_eq!(args.len(), 2);
                let right = args.pop().unwrap();
                let left = args.pop().unwrap();
                self.push_goal(Goal::Unify { left, right })?
            }
            Operator::Dot => self.dot_op_helper(args)?,
            op @ Operator::Lt
            | op @ Operator::Gt
            | op @ Operator::Leq
            | op @ Operator::Geq
            | op @ Operator::Eq
            | op @ Operator::Neq => {
                return self.comparison_op_helper(term, op, args);
            }
            op @ Operator::Add | op @ Operator::Sub | op @ Operator::Mul | op @ Operator::Div => {
                return self.arithmetic_op_helper(term, op, args);
            }
            Operator::In => {
                assert_eq!(args.len(), 2);
                let item = &args[0];
                let list = self.deref(&args[1]);
                match list.value() {
                    Value::List(list) if list.is_empty() => {
                        
                        self.backtrack()?;
                    }
                    Value::List(terms) => {
                        
                        let x = self.deref(item);
                        let v = x.value();
                        let g = v.is_ground();
                        self.choose(
                            terms
                                .iter()
                                .filter(|term| !g || !term.is_ground() || term.value() == v)
                                .map(|term| {
                                    vec![Goal::Unify {
                                        left: item.clone(),
                                        right: term.clone(),
                                    }]
                                })
                                .collect::<Vec<Goals>>(),
                        )?;
                    }
                    _ => {
                        return Err(self.type_error(
                            item,
                            format!("can only use `in` on a list, this is {:?}", item.value()),
                        ));
                    }
                }
            }
            Operator::Debug => {
                let mut message = "Welcome to the debugger!".to_string();
                if !args.is_empty() {
                    message += &format!(
                        "\ndebug({})",
                        args.iter()
                            .map(|arg| self.deref(arg).to_polar())
                            .collect::<Vec<String>>()
                            .join(", ")
                    );
                }
                self.push_goal(Goal::Debug { message })?
            }
            Operator::Print => {
                self.print(
                    &args
                        .iter()
                        .map(|arg| self.deref(arg).to_polar())
                        .collect::<Vec<String>>()
                        .join(", "),
                );
            }
            Operator::New => {
                assert_eq!(args.len(), 2);
                let result = args.pop().unwrap();
                assert!(
                    matches!(result.value(), Value::Variable(_)),
                    "Must have result variable as second arg."
                );
                let constructor = args.pop().unwrap();
                let instance_id = self.new_id();
                let instance =
                    constructor.clone_with_value(Value::ExternalInstance(ExternalInstance {
                        instance_id,
                        constructor: Some(constructor.clone()),
                        repr: Some(constructor.to_polar()),
                    }));
                
                
                self.append_goals(vec![
                    Goal::Unify {
                        left: result,
                        right: instance,
                    },
                    Goal::MakeExternal {
                        instance_id,
                        constructor,
                    },
                ])?;
            }
            Operator::Cut => {
                
                
                let mut choice_index = self.choices.len();
                for choice in self.choices.iter().rev() {
                    
                    let prefix = &self.queries[..(self.queries.len() - 2)];
                    if choice.queries.starts_with(prefix) {
                        
                        
                        choice_index -= 1;
                    } else {
                        break;
                    }
                }
                self.push_goal(Goal::Cut { choice_index })?;
            }
            Operator::Isa => {
                assert_eq!(args.len(), 2);
                let right = args.pop().unwrap();
                let left = args.pop().unwrap();
                self.push_goal(Goal::Isa { left, right })?
            }
            Operator::ForAll => {
                assert_eq!(args.len(), 2);
                let action = args.pop().unwrap();
                let condition = args.pop().unwrap();
                
                let op = Operation {
                    operator: Operator::Not,
                    args: vec![term.clone_with_value(Value::Expression(Operation {
                        operator: Operator::And,
                        args: vec![
                            condition,
                            term.clone_with_value(Value::Expression(Operation {
                                operator: Operator::Not,
                                args: vec![action],
                            })),
                        ],
                    }))],
                };
                let double_negation = term.clone_with_value(Value::Expression(op));
                self.push_goal(Goal::Query {
                    term: double_negation,
                })?;
            }
        }
        Ok(QueryEvent::None)
    }
    
    
    fn query_for_value(&mut self, term: &Term) -> PolarResult<()> {
        if let Value::Boolean(value) = term.value() {
            if !value {
                
                self.push_goal(Goal::Backtrack)?;
            }
            Ok(())
        } else {
            Err(self.type_error(
                &term,
                format!("can't query for: {}", term.value().to_polar()),
            ))
        }
    }
    
    fn dot_op_helper(&mut self, mut args: Vec<Term>) -> PolarResult<()> {
        assert_eq!(args.len(), 3);
        let object = self.deref(&args[0]);
        let field = self.deref(&args[1]);
        let value = &args[2];
        match object.value() {
            
            Value::Dictionary(dict) if matches!(field.value(), Value::String(_) | Value::Variable(_)) => {
                self.push_goal(Goal::Lookup {
                    dict: dict.clone(),
                    field,
                    value: args.remove(2),
                })?
            }
            
            Value::Dictionary(_)
            | Value::ExternalInstance(_)
            | Value::List(_)
            | Value::Number(_)
            | Value::String(_) => {
                let value = value
                    .value()
                    .clone()
                    .symbol()
                    .map_err(|mut e| {
                        e.add_stack_trace(self);
                        e
                    })
                    .expect("bad lookup value");
                let call_id = self.new_call_id(&value);
                self.push_goal(Goal::LookupExternal {
                    call_id,
                    instance: object.clone(),
                    field,
                    check_errors: true,
                })?;
            }
            _ => {
                return Err(self.type_error(
                    &object,
                    format!(
                        "can only perform lookups on dicts and instances, this is {:?}",
                        object.value()
                    ),
                ))
            }
        }
        Ok(())
    }
    
    fn arithmetic_op_helper(
        &mut self,
        term: &Term,
        op: Operator,
        args: Vec<Term>,
    ) -> PolarResult<QueryEvent> {
        assert_eq!(args.len(), 3);
        let left_term = self.deref(&args[0]);
        let right_term = self.deref(&args[1]);
        let result = &args[2];
        assert!(matches!(result.value(), Value::Variable(_)));
        self.log_with(
            || {
                format!(
                    "MATH: {} {} {} = {}",
                    left_term.to_polar(),
                    op.to_polar(),
                    right_term.to_polar(),
                    result.to_polar()
                )
            },
            &[&left_term, &right_term, result],
        );
        match (left_term.value(), right_term.value()) {
            (Value::Number(left), Value::Number(right)) => {
                if let Some(answer) = match op {
                    Operator::Add => *left + *right,
                    Operator::Sub => *left - *right,
                    Operator::Mul => *left * *right,
                    Operator::Div => *left / *right,
                    _ => {
                        return Err(self.set_error_context(
                            term,
                            error::RuntimeError::Unsupported {
                                msg: format!("numeric operation {}", op.to_polar()),
                            },
                        ))
                    }
                } {
                    self.push_goal(Goal::Unify {
                        left: term.clone_with_value(Value::Number(answer)),
                        right: result.clone(),
                    })?;
                } else {
                    return Err(self.set_error_context(
                        term,
                        error::RuntimeError::ArithmeticError {
                            msg: term.to_polar(),
                        },
                    ));
                }
            }
            (_, _) => todo!(),
        }
        Ok(QueryEvent::None)
    }
    
    fn comparison_op_helper(
        &mut self,
        term: &Term,
        op: Operator,
        args: Vec<Term>,
    ) -> PolarResult<QueryEvent> {
        assert_eq!(args.len(), 2);
        let mut left_term = self.deref(&args[0]);
        let mut right_term = self.deref(&args[1]);
        self.log_with(
            || {
                format!(
                    "CMP: {} {} {}",
                    left_term.to_polar(),
                    op.to_polar(),
                    right_term.to_polar(),
                )
            },
            &[&left_term, &right_term],
        );
        
        fn to_int(x: bool) -> i64 {
            if x {
                1
            } else {
                0
            }
        }
        if let Value::Boolean(x) = left_term.value() {
            left_term = left_term.clone_with_value(Value::Number(Numeric::Integer(to_int(*x))));
        }
        if let Value::Boolean(x) = right_term.value() {
            right_term = right_term.clone_with_value(Value::Number(Numeric::Integer(to_int(*x))));
        }
        
        match (left_term.value(), right_term.value()) {
            (Value::Number(left), Value::Number(right)) => {
                if !match op {
                    Operator::Lt => left < right,
                    Operator::Leq => left <= right,
                    Operator::Gt => left > right,
                    Operator::Geq => left >= right,
                    Operator::Eq => left == right,
                    Operator::Neq => left != right,
                    _ => unreachable!("{:?} is not a comparison operator", op),
                } {
                    self.push_goal(Goal::Backtrack)?;
                }
                Ok(QueryEvent::None)
            }
            (Value::String(left), Value::String(right)) => {
                if !match op {
                    Operator::Lt => left < right,
                    Operator::Leq => left <= right,
                    Operator::Gt => left > right,
                    Operator::Geq => left >= right,
                    Operator::Eq => left == right,
                    Operator::Neq => left != right,
                    _ => unreachable!("{:?} is not a comparison operator", op),
                } {
                    self.push_goal(Goal::Backtrack)?;
                }
                Ok(QueryEvent::None)
            }
            (Value::ExternalInstance(_), Value::ExternalInstance(_)) => {
                
                let answer = self.kb.read().unwrap().gensym("external_op_result");
                self.bind(&answer, Term::new_temporary(Value::Boolean(false)));
                
                self.append_goals(vec![Goal::Unify {
                    left: Term::new_temporary(Value::Variable(answer.clone())),
                    right: Term::new_temporary(Value::Boolean(true)),
                }])?;
                let call_id = self.new_call_id(&answer);
                Ok(QueryEvent::ExternalOp {
                    call_id,
                    operator: op,
                    args: vec![left_term, right_term],
                })
            }
            (left, right) => Err(self.type_error(
                term,
                format!(
                    "{} expects comparable arguments, got: {}, {}",
                    op.to_polar(),
                    left.to_polar(),
                    right.to_polar()
                ),
            )),
        }
    }
    
    
    
    
    
    
    pub fn external_call_result(&mut self, call_id: u64, term: Option<Term>) -> PolarResult<()> {
        
        
        if let Some(value) = term {
            self.log_with(|| format!("=> {}", value.to_string()), &[]);
            self.bind(
                &self
                    .call_id_symbols
                    .get(&call_id)
                    .expect("unregistered external call ID")
                    .clone(),
                value,
            );
        } else {
            self.log("=> No more results.", &[]);
            
            
            self.call_id_symbols.remove(&call_id).expect("bad call ID");
            let check_error = if let Some(goal) = self.goals.last() {
                match *(*goal) {
                    Goal::CheckError => true,
                    _ => false,
                }
            } else {
                false
            };
            self.push_goal(Goal::Backtrack)?;
            self.push_goal(Goal::Cut {
                choice_index: self.choices.len() - 1,
            })?;
            if check_error {
                self.push_goal(Goal::CheckError)?;
            }
        }
        Ok(())
    }
    
    pub fn external_question_result(&mut self, call_id: u64, answer: bool) {
        let var = self.call_id_symbols.remove(&call_id).expect("bad call id");
        self.bind(&var, Term::new_temporary(Value::Boolean(answer)));
    }
    
    pub fn external_error(&mut self, message: String) {
        self.external_error = Some(message);
    }
    
    
    
    
    
    
    fn unify(&mut self, left: &Term, right: &Term) -> PolarResult<()> {
        match (&left.value(), &right.value()) {
            
            (Value::Variable(var), _) => self.unify_var(var, right)?,
            (_, Value::Variable(var)) => self.unify_var(var, left)?,
            
            (Value::RestVariable(var), _) => self.unify_var(var, right)?,
            (Value::List(_), Value::RestVariable(var)) => self.unify_var(var, left)?,
            
            (Value::List(left), Value::List(right)) => {
                self.unify_lists(left, right, |(left, right)| Goal::Unify {
                    left: left.clone(),
                    right: right.clone(),
                })?
            }
            (Value::Dictionary(left), Value::Dictionary(right)) => {
                
                let left_fields: HashSet<&Symbol> = left.fields.keys().collect();
                let right_fields: HashSet<&Symbol> = right.fields.keys().collect();
                if left_fields != right_fields {
                    self.push_goal(Goal::Backtrack)?;
                    return Ok(());
                }
                
                for (k, v) in left.fields.iter() {
                    let right = right
                        .fields
                        .get(&k)
                        .expect("fields should be equal")
                        .clone();
                    self.push_goal(Goal::Unify {
                        left: v.clone(),
                        right,
                    })?
                }
            }
            
            (Value::Number(left), Value::Number(right)) => {
                if left != right {
                    self.push_goal(Goal::Backtrack)?;
                }
            }
            
            (Value::String(left), Value::String(right)) => {
                if left != right {
                    self.push_goal(Goal::Backtrack)?;
                }
            }
            
            (Value::Boolean(left), Value::Boolean(right)) => {
                if left != right {
                    self.push_goal(Goal::Backtrack)?;
                }
            }
            
            (Value::Call(left), Value::Call(right)) => {
                
                assert!(left.kwargs.is_none());
                assert!(right.kwargs.is_none());
                if left.name == right.name && left.args.len() == right.args.len() {
                    self.append_goals(left.args.iter().zip(right.args.iter()).map(
                        |(left, right)| Goal::Unify {
                            left: left.clone(),
                            right: right.clone(),
                        },
                    ))?;
                } else {
                    self.push_goal(Goal::Backtrack)?
                }
            }
            
            
            
            
            (
                Value::ExternalInstance(ExternalInstance {
                    instance_id: left_instance,
                    ..
                }),
                Value::ExternalInstance(ExternalInstance {
                    instance_id: right_instance,
                    ..
                }),
            ) => {
                
                if left_instance != right_instance {
                    self.push_goal(Goal::UnifyExternal {
                        left_instance_id: *left_instance,
                        right_instance_id: *right_instance,
                    })?;
                }
            }
            (Value::InstanceLiteral(_), Value::InstanceLiteral(_)) => {
                return Err(
                    self.type_error(&left, String::from("Cannot unify two instance literals."))
                );
            }
            (Value::InstanceLiteral(_), Value::ExternalInstance(_))
            | (Value::ExternalInstance(_), Value::InstanceLiteral(_)) => {
                return Err(self.type_error(
                    &left,
                    String::from("Cannot unify instance literal with external instance."),
                ));
            }
            
            (_, _) => self.push_goal(Goal::Backtrack)?,
        }
        Ok(())
    }
    
    
    fn unify_var(&mut self, left: &Symbol, right: &Term) -> PolarResult<()> {
        let left_value = self.value(&left).cloned();
        let mut right_value = None;
        if let Value::Variable(ref right_sym) | Value::RestVariable(ref right_sym) = right.value() {
            right_value = self.value(right_sym).cloned();
        }
        match (left_value, right_value) {
            (Some(left), Some(right)) => {
                
                self.push_goal(Goal::Unify { left, right })?;
            }
            (Some(left), _) => {
                
                self.push_goal(Goal::Unify {
                    left,
                    right: right.clone(),
                })?;
            }
            (None, Some(value)) => {
                
                
                self.bind(left, value);
            }
            (None, None) => {
                
                
                self.bind(left, right.clone());
            }
        }
        Ok(())
    }
    
    
    
    #[allow(clippy::ptr_arg)]
    fn unify_lists<F>(&mut self, left: &TermList, right: &TermList, unify: F) -> PolarResult<()>
    where
        F: FnMut((&Term, &Term)) -> Goal,
    {
        if has_rest_var(left) && has_rest_var(right) {
            self.unify_two_lists_with_rest(left, right, unify)
        } else if has_rest_var(left) {
            self.unify_rest_list_with_list(left, right, unify)
        } else if has_rest_var(right) {
            self.unify_rest_list_with_list(right, left, unify)
        } else if left.len() == right.len() {
            
            self.append_goals(left.iter().zip(right).map(unify))
        } else {
            self.push_goal(Goal::Backtrack)
        }
    }
    
    
    #[allow(clippy::ptr_arg)]
    fn unify_two_lists_with_rest<F>(
        &mut self,
        rest_list_a: &TermList,
        rest_list_b: &TermList,
        mut unify: F,
    ) -> PolarResult<()>
    where
        F: FnMut((&Term, &Term)) -> Goal,
    {
        if rest_list_a.len() == rest_list_b.len() {
            let n = rest_list_b.len() - 1;
            let rest = unify((&rest_list_b[n].clone(), &rest_list_a[n].clone()));
            self.append_goals(
                rest_list_b
                    .iter()
                    .take(n)
                    .zip(rest_list_a)
                    .map(unify)
                    .chain(vec![rest]),
            )
        } else {
            let (shorter, longer) = {
                if rest_list_a.len() < rest_list_b.len() {
                    (rest_list_a, rest_list_b)
                } else {
                    (rest_list_b, rest_list_a)
                }
            };
            let n = shorter.len() - 1;
            let rest = unify((
                &shorter[n].clone(),
                &Term::new_temporary(Value::List(longer[n..].to_vec())),
            ));
            self.append_goals(
                shorter
                    .iter()
                    .take(n)
                    .zip(longer)
                    .map(unify)
                    .chain(vec![rest]),
            )
        }
    }
    
    
    #[allow(clippy::ptr_arg)]
    fn unify_rest_list_with_list<F>(
        &mut self,
        rest_list: &TermList,
        list: &TermList,
        mut unify: F,
    ) -> PolarResult<()>
    where
        F: FnMut((&Term, &Term)) -> Goal,
    {
        let n = rest_list.len() - 1;
        if list.len() >= n {
            let rest = unify((
                &rest_list[n].clone(),
                &Term::new_temporary(Value::List(list[n..].to_vec())),
            ));
            self.append_goals(
                rest_list
                    .iter()
                    .take(n)
                    .zip(list)
                    .map(unify)
                    .chain(vec![rest]),
            )
        } else {
            self.push_goal(Goal::Backtrack)
        }
    }
    
    
    #[allow(clippy::ptr_arg)]
    fn filter_rules(
        &mut self,
        applicable_rules: &Rules,
        unfiltered_rules: &Rules,
        args: &TermList,
    ) -> PolarResult<()> {
        if unfiltered_rules.is_empty() {
            
            self.push_goal(Goal::SortRules {
                rules: applicable_rules.iter().rev().cloned().collect(),
                args: args.clone(),
                outer: 1,
                inner: 1,
            })
        } else {
            
            let mut unfiltered_rules = unfiltered_rules.clone();
            let rule = unfiltered_rules.pop().unwrap();
            let inapplicable = Goal::FilterRules {
                args: args.clone(),
                applicable_rules: applicable_rules.clone(),
                unfiltered_rules: unfiltered_rules.clone(),
            };
            if rule.params.len() != args.len() {
                return self.push_goal(inapplicable); 
            }
            let mut applicable_rules = applicable_rules.clone();
            applicable_rules.push(rule.clone());
            let applicable = Goal::FilterRules {
                args: args.clone(),
                applicable_rules,
                unfiltered_rules,
            };
            
            if rule.is_ground() {
                return self.push_goal(applicable);
            }
            
            
            let Rule { params, .. } = self.rename_rule_vars(&rule);
            let mut check_applicability = vec![];
            for (arg, param) in args.iter().zip(params.iter()) {
                check_applicability.push(Goal::Unify {
                    left: arg.clone(),
                    right: param.parameter.clone(),
                });
                if let Some(specializer) = ¶m.specializer {
                    check_applicability.push(Goal::Isa {
                        left: arg.clone(),
                        right: specializer.clone(),
                    });
                }
            }
            self.choose_conditional(check_applicability, vec![applicable], vec![inapplicable])?;
            Ok(())
        }
    }
    
    
    
    
    
    
    
    
    #[allow(clippy::ptr_arg)]
    fn sort_rules(
        &mut self,
        rules: &Rules,
        args: &TermList,
        outer: usize,
        inner: usize,
    ) -> PolarResult<()> {
        if rules.is_empty() {
            return self.push_goal(Goal::Backtrack);
        }
        assert!(outer <= rules.len(), "bad outer index");
        assert!(inner <= rules.len(), "bad inner index");
        assert!(inner <= outer, "bad insertion sort state");
        let next_outer = Goal::SortRules {
            rules: rules.clone(),
            args: args.clone(),
            outer: outer + 1,
            inner: outer + 1,
        };
        
        
        if outer < rules.len() {
            if inner > 0 {
                let compare = Goal::IsMoreSpecific {
                    left: rules[inner].clone(),
                    right: rules[inner - 1].clone(),
                    args: args.clone(),
                };
                let mut rules = rules.clone();
                rules.swap(inner - 1, inner);
                let next_inner = Goal::SortRules {
                    rules,
                    outer,
                    inner: inner - 1,
                    args: args.clone(),
                };
                
                
                self.choose_conditional(vec![compare], vec![next_inner], vec![next_outer])?;
            } else {
                assert_eq!(inner, 0);
                self.push_goal(next_outer)?;
            }
        } else {
            
            
            self.polar_log_mute = false;
            self.log_with(
                || {
                    let mut rule_strs = "APPLICABLE_RULES:".to_owned();
                    for rule in rules {
                        rule_strs.push_str(&format!("\n  {}", self.rule_source(&rule)));
                    }
                    rule_strs
                },
                &[],
            );
            let mut alternatives = Vec::with_capacity(rules.len());
            for rule in rules.iter() {
                let mut goals = Vec::with_capacity(2 * args.len() + 4);
                goals.push(Goal::TraceRule {
                    trace: Rc::new(Trace {
                        node: Node::Rule(rule.clone()),
                        children: vec![],
                    }),
                });
                goals.push(Goal::TracePush);
                let Rule { body, params, .. } = self.rename_rule_vars(rule);
                
                for (arg, param) in args.iter().zip(params.iter()) {
                    goals.push(Goal::Unify {
                        left: arg.clone(),
                        right: param.parameter.clone(),
                    });
                    if let Some(specializer) = ¶m.specializer {
                        goals.push(Goal::Isa {
                            left: arg.clone(),
                            right: specializer.clone(),
                        });
                    }
                }
                
                goals.push(Goal::Query { term: body.clone() });
                goals.push(Goal::TracePop);
                alternatives.push(goals)
            }
            
            self.choose(alternatives)?;
        }
        Ok(())
    }
    
    #[allow(clippy::ptr_arg)]
    fn is_more_specific(&mut self, left: &Rule, right: &Rule, args: &TermList) -> PolarResult<()> {
        let zipped = left.params.iter().zip(right.params.iter()).zip(args.iter());
        for ((left_param, right_param), arg) in zipped {
            match (&left_param.specializer, &right_param.specializer) {
                (Some(left_spec), Some(right_spec)) => {
                    
                    
                    
                    
                    if left_spec != right_spec {
                        let answer = self.kb.read().unwrap().gensym("is_subspecializer");
                        
                        
                        
                        
                        
                        self.bind(&answer, Term::new_temporary(Value::Boolean(false)));
                        return self.append_goals(vec![
                            Goal::IsSubspecializer {
                                answer: answer.clone(),
                                left: left_spec.clone(),
                                right: right_spec.clone(),
                                arg: arg.clone(),
                            },
                            Goal::Unify {
                                left: Term::new_temporary(Value::Variable(answer)),
                                right: Term::new_temporary(Value::Boolean(true)),
                            },
                        ]);
                    }
                }
                
                
                (None, Some(_)) => return self.push_goal(Goal::Backtrack),
                
                
                (Some(_), None) => return Ok(()),
                
                (None, None) => (),
            }
        }
        
        self.push_goal(Goal::Backtrack)
    }
    
    fn is_subspecializer(
        &mut self,
        answer: &Symbol,
        left: &Term,
        right: &Term,
        arg: &Term,
    ) -> PolarResult<QueryEvent> {
        assert!(!matches!(left.value(), Value::InstanceLiteral(_)));
        assert!(!matches!(right.value(), Value::InstanceLiteral(_)));
        let arg = self.deref(&arg);
        match (arg.value(), left.value(), right.value()) {
            (
                Value::ExternalInstance(instance),
                Value::Pattern(Pattern::Instance(left_lit)),
                Value::Pattern(Pattern::Instance(right_lit)),
            ) => {
                let call_id = self.new_call_id(&answer);
                let instance_id = instance.instance_id;
                if left_lit.tag == right_lit.tag
                    && !(left_lit.fields.fields.is_empty() && right_lit.fields.fields.is_empty())
                {
                    self.push_goal(Goal::IsSubspecializer {
                        answer: answer.clone(),
                        left: left.clone_with_value(Value::Pattern(Pattern::Dictionary(
                            left_lit.fields.clone(),
                        ))),
                        right: right.clone_with_value(Value::Pattern(Pattern::Dictionary(
                            right_lit.fields.clone(),
                        ))),
                        arg,
                    })?;
                }
                
                Ok(QueryEvent::ExternalIsSubSpecializer {
                    call_id,
                    instance_id,
                    left_class_tag: left_lit.tag.clone(),
                    right_class_tag: right_lit.tag.clone(),
                })
            }
            (
                _,
                Value::Pattern(Pattern::Dictionary(left)),
                Value::Pattern(Pattern::Dictionary(right)),
            ) => {
                let left_fields: HashSet<&Symbol> = left.fields.keys().collect();
                let right_fields: HashSet<&Symbol> = right.fields.keys().collect();
                
                
                
                if left_fields.len() != right_fields.len() {
                    self.bind(
                        &answer,
                        Term::new_temporary(Value::Boolean(right_fields.len() < left.fields.len())),
                    );
                }
                Ok(QueryEvent::None)
            }
            (_, Value::Pattern(Pattern::Instance(_)), Value::Pattern(Pattern::Dictionary(_))) => {
                self.bind(&answer, Term::new_temporary(Value::Boolean(true)));
                Ok(QueryEvent::None)
            }
            _ => {
                self.bind(&answer, Term::new_temporary(Value::Boolean(false)));
                Ok(QueryEvent::None)
            }
        }
    }
    pub fn term_source(&self, term: &Term, include_info: bool) -> String {
        let source = self.source(term);
        let span = term.span();
        let mut source_string = match (&source, &span) {
            (Some(source), Some((left, right))) => {
                source.src.chars().take(*right).skip(*left).collect()
            }
            _ => term.to_polar(),
        };
        if include_info {
            if let Some(source) = source {
                let offset = term.offset();
                let (row, column) = crate::lexer::loc_to_pos(&source.src, offset);
                source_string.push_str(&format!(" at line {}, column {}", row + 1, column));
                if let Some(filename) = source.filename {
                    source_string.push_str(&format!(" in file {}", filename));
                }
            }
        }
        source_string
    }
    pub fn rule_source(&self, rule: &Rule) -> String {
        let head = format!(
            "{}({})",
            rule.name,
            rule.params.iter().fold(String::new(), |mut acc, p| {
                if acc != "" {
                    acc += ", ";
                }
                acc += &self.term_source(&p.parameter, false);
                if let Some(spec) = &p.specializer {
                    acc += ": ";
                    acc += &self.term_source(&spec, false);
                }
                acc
            })
        );
        match rule.body.value() {
            Value::Expression(Operation {
                operator: Operator::And,
                args,
            }) if !args.is_empty() => head + " if " + &self.term_source(&rule.body, false) + ";",
            _ => head + ";",
        }
    }
    fn set_error_context(
        &self,
        term: &Term,
        error: impl Into<error::PolarError>,
    ) -> error::PolarError {
        let source = self.source(term);
        let error: error::PolarError = error.into();
        error.set_context(source.as_ref(), Some(term))
    }
    fn type_error(&self, term: &Term, msg: String) -> error::PolarError {
        let stack_trace = self.stack_trace();
        let error = error::RuntimeError::TypeError {
            msg,
            stack_trace: Some(stack_trace),
        };
        self.set_error_context(term, error)
    }
}
#[cfg(test)]
mod tests {
    use permute::permute;
    use super::*;
    use crate::rewrites::unwrap_and;
    
    
    
    
    
    
    
    
    
    
    macro_rules! query {
        ($term:expr) => {
            Goal::Query {
                term: term!($term)
            }
        };
        ($($term:expr),+) => {
            Goal::Query {
                term: term!(op!(And, $($term),+))
            }
        };
    }
    
    
    
    
    
    
    
    
    
    
    
    
    macro_rules! assert_query_events {
        ($vm:ident, []) => {
            assert!($vm.is_halted());
        };
        ($vm:ident, [QueryEvent::Result{$result:expr}]) => {
            assert!(matches!($vm.run().unwrap(), QueryEvent::Result{bindings, ..} if bindings == $result));
            assert_query_events!($vm, []);
        };
        ($vm:ident, [QueryEvent::Result{$result:expr}, $($tail:tt)*]) => {
            assert!(matches!($vm.run().unwrap(), QueryEvent::Result{bindings, ..} if bindings == $result));
            assert_query_events!($vm, [$($tail)*]);
        };
        ($vm:ident, [$( $pattern:pat )|+ $( if $guard: expr )?]) => {
            assert!(matches!($vm.run().unwrap(), $($pattern)|+ $(if $guard)?));
            assert_query_events!($vm, []);
        };
        ($vm:ident, [$( $pattern:pat )|+ $( if $guard: expr )?, $($tail:tt)*]) => {
            assert!(matches!($vm.run().unwrap(), $($pattern)|+ $(if $guard)?));
            assert_query_events!($vm, [$($tail)*]);
        };
        
    }
    #[test]
    fn deref() {
        let mut vm = PolarVirtualMachine::default();
        let value = term!(1);
        let x = sym!("x");
        let y = sym!("y");
        let term_x = term!(x.clone());
        let term_y = term!(y.clone());
        
        assert_eq!(vm.deref(&term_x), term_x);
        
        vm.bind(&x, term_y.clone());
        assert_eq!(vm.deref(&term_x), term_y);
        
        assert_eq!(vm.deref(&value), value.clone());
        
        vm.bind(&x, value.clone());
        assert_eq!(vm.deref(&term_x), value);
        
        vm.bind(&x, term_y);
        vm.bind(&y, value.clone());
        assert_eq!(vm.deref(&term_x), value);
    }
    #[test]
    #[allow(clippy::cognitive_complexity)]
    fn and_expression() {
        let f1 = rule!("f", [1]);
        let f2 = rule!("f", [2]);
        let rule = GenericRule::new(sym!("f"), vec![Arc::new(f1), Arc::new(f2)]);
        let mut kb = KnowledgeBase::new();
        kb.rules.insert(rule.name.clone(), rule);
        let goal = query!(op!(And));
        let mut vm = PolarVirtualMachine::new_test(Arc::new(RwLock::new(kb)), false, vec![goal]);
        assert_query_events!(vm, [
            QueryEvent::Result{hashmap!()},
            QueryEvent::Done
        ]);
        assert!(vm.is_halted());
        let f1 = term!(call!("f", [1]));
        let f2 = term!(call!("f", [2]));
        let f3 = term!(call!("f", [3]));
        
        vm.push_goal(query!(op!(And, f1.clone()))).unwrap();
        assert_query_events!(vm, [
            QueryEvent::Result{hashmap!{}},
            QueryEvent::Done
        ]);
        
        vm.push_goal(query!(f1.clone(), f2.clone())).unwrap();
        assert_query_events!(vm, [
            QueryEvent::Result{hashmap!{}},
            QueryEvent::Done
        ]);
        
        vm.push_goal(query!(op!(And, f3.clone()))).unwrap();
        assert_query_events!(vm, [QueryEvent::Done]);
        
        let parts = vec![f1, f2, f3];
        for permutation in permute(parts) {
            vm.push_goal(Goal::Query {
                term: Term::new_from_test(Value::Expression(Operation {
                    operator: Operator::And,
                    args: permutation,
                })),
            })
            .unwrap();
            assert_query_events!(vm, [QueryEvent::Done]);
        }
    }
    #[test]
    fn unify_expression() {
        let mut vm = PolarVirtualMachine::default();
        vm.push_goal(query!(op!(Unify, term!(1), term!(1))))
            .unwrap();
        assert_query_events!(vm, [
            QueryEvent::Result{hashmap!{}},
            QueryEvent::Done
        ]);
        let q = op!(Unify, term!(1), term!(2));
        vm.push_goal(query!(q)).unwrap();
        assert_query_events!(vm, [QueryEvent::Done]);
    }
    #[test]
    #[allow(clippy::cognitive_complexity)]
    fn isa_on_lists() {
        let mut vm = PolarVirtualMachine::default();
        let one = term!(1);
        let one_list = term!([1]);
        let one_two_list = term!([1, 2]);
        let two_one_list = term!([2, 1]);
        let empty_list = term!([]);
        
        vm.push_goal(Goal::Isa {
            left: empty_list.clone(),
            right: empty_list.clone(),
        })
        .unwrap();
        assert!(
            matches!(vm.run().unwrap(), QueryEvent::Result{bindings, ..} if bindings.is_empty())
        );
        assert!(matches!(vm.run().unwrap(), QueryEvent::Done));
        assert!(vm.is_halted());
        
        vm.push_goal(Goal::Isa {
            left: one_two_list.clone(),
            right: one_two_list.clone(),
        })
        .unwrap();
        assert!(
            matches!(vm.run().unwrap(), QueryEvent::Result{bindings, ..} if bindings.is_empty())
        );
        assert!(matches!(vm.run().unwrap(), QueryEvent::Done));
        assert!(vm.is_halted());
        
        vm.push_goal(Goal::Isa {
            left: one_two_list.clone(),
            right: two_one_list,
        })
        .unwrap();
        assert!(matches!(vm.run().unwrap(), QueryEvent::Done));
        assert!(vm.is_halted());
        
        vm.push_goal(Goal::Isa {
            left: one_list.clone(),
            right: one_two_list.clone(),
        })
        .unwrap();
        assert!(matches!(vm.run().unwrap(), QueryEvent::Done));
        assert!(vm.is_halted());
        
        vm.push_goal(Goal::Isa {
            left: one_two_list.clone(),
            right: one_list.clone(),
        })
        .unwrap();
        assert!(matches!(vm.run().unwrap(), QueryEvent::Done));
        assert!(vm.is_halted());
        
        vm.push_goal(Goal::Isa {
            left: one_list.clone(),
            right: empty_list.clone(),
        })
        .unwrap();
        assert!(matches!(vm.run().unwrap(), QueryEvent::Done));
        assert!(vm.is_halted());
        
        vm.push_goal(Goal::Isa {
            left: empty_list,
            right: one_list.clone(),
        })
        .unwrap();
        assert!(matches!(vm.run().unwrap(), QueryEvent::Done));
        assert!(vm.is_halted());
        
        vm.push_goal(Goal::Isa {
            left: one_list.clone(),
            right: one.clone(),
        })
        .unwrap();
        assert!(matches!(vm.run().unwrap(), QueryEvent::Done));
        assert!(vm.is_halted());
        
        vm.push_goal(Goal::Isa {
            left: one,
            right: one_list,
        })
        .unwrap();
        assert!(matches!(vm.run().unwrap(), QueryEvent::Done));
        assert!(vm.is_halted());
        
        vm.push_goal(Goal::Isa {
            left: one_two_list,
            right: term!([1, Value::RestVariable(sym!("rest"))]),
        })
        .unwrap();
        assert_query_events!(vm, [
            QueryEvent::Result{hashmap!{sym!("rest") => term!([2])}},
            QueryEvent::Done
        ]);
    }
    #[test]
    #[allow(clippy::cognitive_complexity)]
    fn isa_on_dicts() {
        let mut vm = PolarVirtualMachine::default();
        let left = term!(btreemap! {
            sym!("x") => term!(1),
            sym!("y") => term!(2),
        });
        let right = Pattern::term_as_pattern(&term!(btreemap! {
            sym!("x") => term!(1),
            sym!("y") => term!(2),
        }));
        vm.push_goal(Goal::Isa {
            left: left.clone(),
            right,
        })
        .unwrap();
        assert_query_events!(vm, [QueryEvent::Result { hashmap!() }, QueryEvent::Done]);
        
        let right = Pattern::term_as_pattern(&term!(btreemap! {
            sym!("x") => term!(2),
            sym!("y") => term!(1),
        }));
        vm.push_goal(Goal::Isa {
            left: left.clone(),
            right,
        })
        .unwrap();
        assert_query_events!(vm, [QueryEvent::Done]);
        
        vm.push_goal(Goal::Isa {
            left: term!(btreemap! {}),
            right: Pattern::term_as_pattern(&term!(btreemap! {})),
        })
        .unwrap();
        assert_query_events!(vm, [QueryEvent::Result { hashmap!() }, QueryEvent::Done]);
        
        vm.push_goal(Goal::Isa {
            left: left.clone(),
            right: Pattern::term_as_pattern(&term!(btreemap! {})),
        })
        .unwrap();
        assert_query_events!(vm, [QueryEvent::Result { hashmap!() }, QueryEvent::Done]);
        
        vm.push_goal(Goal::Isa {
            left: term!(btreemap! {}),
            right: Pattern::term_as_pattern(&left),
        })
        .unwrap();
        assert_query_events!(vm, [QueryEvent::Done]);
        
        vm.push_goal(Goal::Isa {
            left: left.clone(),
            right: Pattern::term_as_pattern(&term!(btreemap! {sym!("x") => term!(1)})),
        })
        .unwrap();
        assert_query_events!(vm, [QueryEvent::Result { hashmap!() }, QueryEvent::Done]);
        
        vm.push_goal(Goal::Isa {
            left: term!(btreemap! {sym!("x") => term!(1)}),
            right: Pattern::term_as_pattern(&left),
        })
        .unwrap();
        assert_query_events!(vm, [QueryEvent::Done]);
    }
    #[test]
    fn unify_dicts() {
        let mut vm = PolarVirtualMachine::default();
        
        let left = term!(btreemap! {
            sym!("x") => term!(1),
            sym!("y") => term!(2),
        });
        let right = term!(btreemap! {
            sym!("x") => term!(1),
            sym!("y") => term!(2),
        });
        vm.push_goal(Goal::Unify {
            left: left.clone(),
            right,
        })
        .unwrap();
        assert_query_events!(vm, [QueryEvent::Result { hashmap!() }, QueryEvent::Done]);
        
        let right = term!(btreemap! {
            sym!("x") => term!(2),
            sym!("y") => term!(1),
        });
        vm.push_goal(Goal::Unify {
            left: left.clone(),
            right,
        })
        .unwrap();
        assert_query_events!(vm, [QueryEvent::Done]);
        
        vm.push_goal(Goal::Unify {
            left: term!(btreemap! {}),
            right: term!(btreemap! {}),
        })
        .unwrap();
        assert_query_events!(vm, [QueryEvent::Result { hashmap!() }, QueryEvent::Done]);
        
        vm.push_goal(Goal::Unify {
            left: left.clone(),
            right: term!(btreemap! {}),
        })
        .unwrap();
        assert_query_events!(vm, [QueryEvent::Done]);
        
        let right = term!(btreemap! {
            sym!("x") => term!(1),
        });
        vm.push_goal(Goal::Unify { left, right }).unwrap();
        assert_query_events!(vm, [QueryEvent::Done]);
    }
    #[test]
    fn unify_nested_dicts() {
        let mut vm = PolarVirtualMachine::default();
        let left = term!(btreemap! {
            sym!("x") => term!(btreemap!{
                sym!("y") => term!(1)
            })
        });
        let right = term!(btreemap! {
            sym!("x") => term!(btreemap!{
                sym!("y") => term!(sym!("result"))
            })
        });
        vm.push_goal(Goal::Unify { left, right }).unwrap();
        assert_query_events!(vm, [QueryEvent::Result { hashmap!{sym!("result") => term!(1)} }, QueryEvent::Done]);
    }
    #[test]
    fn lookup() {
        let mut vm = PolarVirtualMachine::default();
        let fields = btreemap! {
            sym!("x") => term!(1),
        };
        let dict = Dictionary { fields };
        vm.push_goal(Goal::Lookup {
            dict: dict.clone(),
            field: term!(string!("x")),
            value: term!(1),
        })
        .unwrap();
        assert_query_events!(vm, [
            QueryEvent::Result{hashmap!{}}
        ]);
        
        vm.push_goal(Goal::Lookup {
            dict: dict.clone(),
            field: term!(string!("x")),
            value: term!(2),
        })
        .unwrap();
        assert_query_events!(vm, [QueryEvent::Done]);
        
        vm.push_goal(Goal::Lookup {
            dict,
            field: term!(string!("x")),
            value: term!(sym!("y")),
        })
        .unwrap();
        assert_query_events!(vm, [
            QueryEvent::Result{hashmap!{sym!("y") => term!(1)}}
        ]);
    }
    #[test]
    fn bind() {
        let x = sym!("x");
        let y = sym!("y");
        let zero = term!(0);
        let mut vm = PolarVirtualMachine::default();
        vm.bind(&x, zero.clone());
        assert_eq!(vm.value(&x), Some(&zero));
        assert_eq!(vm.value(&y), None);
    }
    #[test]
    fn debug() {
        let mut vm = PolarVirtualMachine::new_test(
            Arc::new(RwLock::new(KnowledgeBase::new())),
            false,
            vec![Goal::Debug {
                message: "Hello".to_string(),
            }],
        );
        assert!(matches!(
            vm.run().unwrap(),
            QueryEvent::Debug { message } if &message[..] == "Hello"
        ));
    }
    #[test]
    fn halt() {
        let mut vm = PolarVirtualMachine::new_test(
            Arc::new(RwLock::new(KnowledgeBase::new())),
            false,
            vec![Goal::Halt],
        );
        let _ = vm.run().unwrap();
        assert_eq!(vm.goals.len(), 0);
        assert_eq!(vm.bindings.len(), 0);
    }
    #[test]
    fn unify() {
        let x = sym!("x");
        let y = sym!("y");
        let vars = term!([x.clone(), y.clone()]);
        let zero = value!(0);
        let one = value!(1);
        let vals = term!([zero.clone(), one.clone()]);
        let mut vm = PolarVirtualMachine::new_test(
            Arc::new(RwLock::new(KnowledgeBase::new())),
            false,
            vec![Goal::Unify {
                left: vars,
                right: vals,
            }],
        );
        let _ = vm.run().unwrap();
        assert_eq!(vm.value(&x), Some(&Term::new_from_test(zero)));
        assert_eq!(vm.value(&y), Some(&Term::new_from_test(one)));
    }
    #[test]
    fn unify_var() {
        let x = sym!("x");
        let y = sym!("y");
        let z = sym!("z");
        let one = term!(1);
        let two = term!(2);
        let mut vm = PolarVirtualMachine::default();
        
        vm.bind(&y, one.clone());
        vm.append_goals(vec![Goal::Unify {
            left: term!(x),
            right: term!(y),
        }])
        .unwrap();
        let _ = vm.run().unwrap();
        assert_eq!(vm.value(&sym!("x")), Some(&one));
        vm.backtrack().unwrap();
        
        vm.bind(&z, one.clone());
        vm.append_goals(vec![Goal::Unify {
            left: term!(z.clone()),
            right: one.clone(),
        }])
        .unwrap();
        let _ = vm.run().unwrap();
        assert_eq!(vm.value(&z), Some(&one));
        
        vm.bind(&z, one.clone());
        vm.append_goals(vec![Goal::Unify {
            left: term!(z.clone()),
            right: two,
        }])
        .unwrap();
        let _ = vm.run().unwrap();
        assert_eq!(vm.value(&z), Some(&one));
    }
    #[test]
    fn test_gen_var() {
        let vm = PolarVirtualMachine::default();
        let rule = Rule {
            name: Symbol::new("foo"),
            params: vec![],
            body: Term::new_from_test(Value::Expression(Operation {
                operator: Operator::And,
                args: vec![
                    term!(1),
                    Term::new_from_test(Value::Variable(Symbol("x".to_string()))),
                    Term::new_from_test(Value::Variable(Symbol("x".to_string()))),
                    Term::new_from_test(Value::List(vec![Term::new_from_test(Value::Variable(
                        Symbol("y".to_string()),
                    ))])),
                ],
            })),
        };
        let renamed_rule = vm.rename_rule_vars(&rule);
        let renamed_terms = unwrap_and(renamed_rule.body);
        assert_eq!(renamed_terms[1].value(), renamed_terms[2].value());
        let x_value = match &renamed_terms[1].value() {
            Value::Variable(sym) => Some(sym.0.clone()),
            _ => None,
        };
        assert_eq!(x_value.unwrap(), "_x_0");
        let y_value = match &renamed_terms[3].value() {
            Value::List(terms) => match &terms[0].value() {
                Value::Variable(sym) => Some(sym.0.clone()),
                _ => None,
            },
            _ => None,
        };
        assert_eq!(y_value.unwrap(), "_y_1");
    }
    #[test]
    fn test_filter_rules() {
        let rule_a = Arc::new(rule!("bar", ["_"; instance!("a")]));
        let rule_b = Arc::new(rule!("bar", ["_"; instance!("b")]));
        let rule_1a = Arc::new(rule!("bar", [value!(1)]));
        let rule_1b = Arc::new(rule!("bar", ["_"; value!(1)]));
        let gen_rule = GenericRule::new(sym!("bar"), vec![rule_a, rule_b, rule_1a, rule_1b]);
        let mut kb = KnowledgeBase::new();
        kb.add_generic_rule(gen_rule);
        let external_instance = Value::ExternalInstance(ExternalInstance {
            instance_id: 1,
            constructor: None,
            repr: None,
        });
        let query = query!(call!("bar", [sym!("x")]));
        let mut vm = PolarVirtualMachine::new_test(Arc::new(RwLock::new(kb)), false, vec![query]);
        vm.bind(&sym!("x"), Term::new_from_test(external_instance));
        let mut external_isas = vec![];
        loop {
            match vm.run().unwrap() {
                QueryEvent::Done => break,
                QueryEvent::ExternalIsa {
                    call_id, class_tag, ..
                } => {
                    external_isas.push(class_tag.clone());
                    
                    vm.external_question_result(call_id, class_tag.0 == "a")
                }
                QueryEvent::ExternalIsSubSpecializer { .. } | QueryEvent::Result { .. } => (),
                _ => panic!("Unexpected event"),
            }
        }
        let expected = vec![sym!("b"), sym!("a"), sym!("a")];
        assert_eq!(external_isas, expected);
        vm.bind(&sym!("x"), Term::new_from_test(value!(1)));
        let _ = vm
            .query(&Term::new_from_test(Value::Call(call!("bar", [sym!("x")]))))
            .unwrap();
        let mut results = vec![];
        loop {
            match vm.run().unwrap() {
                QueryEvent::Done => break,
                QueryEvent::ExternalIsa { .. } => (),
                QueryEvent::Result { bindings, .. } => results.push(bindings),
                _ => panic!("Unexpected event"),
            }
        }
        assert_eq!(results.len(), 2);
        assert_eq!(
            results,
            vec![
                hashmap! {sym!("x") => term!(1)},
                hashmap! {sym!("x") => term!(1)},
            ]
        );
    }
    #[test]
    fn test_sort_rules() {
        
        let bar_rule = GenericRule::new(
            sym!("bar"),
            vec![
                Arc::new(rule!("bar", ["_"; instance!("b"), "_"; instance!("a"), value!(3)])),
                Arc::new(rule!("bar", ["_"; instance!("a"), "_"; instance!("a"), value!(1)])),
                Arc::new(rule!("bar", ["_"; instance!("a"), "_"; instance!("b"), value!(2)])),
                Arc::new(rule!("bar", ["_"; instance!("b"), "_"; instance!("b"), value!(4)])),
            ],
        );
        let mut kb = KnowledgeBase::new();
        kb.add_generic_rule(bar_rule);
        let external_instance = Value::ExternalInstance(ExternalInstance {
            instance_id: 1,
            constructor: None,
            repr: None,
        });
        let mut vm = PolarVirtualMachine::new_test(
            Arc::new(RwLock::new(kb)),
            false,
            vec![query!(call!(
                "bar",
                [external_instance.clone(), external_instance, sym!("z")]
            ))],
        );
        let mut results = Vec::new();
        loop {
            match vm.run().unwrap() {
                QueryEvent::Done => break,
                QueryEvent::Result { bindings, .. } => results.push(bindings),
                QueryEvent::ExternalIsSubSpecializer {
                    call_id,
                    left_class_tag,
                    right_class_tag,
                    ..
                } => {
                    
                    vm.external_question_result(call_id, left_class_tag < right_class_tag)
                }
                QueryEvent::MakeExternal { .. } => (),
                QueryEvent::ExternalIsa { call_id, .. } => {
                    
                    vm.external_question_result(call_id, true)
                }
                _ => panic!("Unexpected event"),
            }
        }
        assert_eq!(results.len(), 4);
        assert_eq!(
            results,
            vec![
                hashmap! {sym!("z") => term!(1)},
                hashmap! {sym!("z") => term!(2)},
                hashmap! {sym!("z") => term!(3)},
                hashmap! {sym!("z") => term!(4)},
            ]
        );
    }
    #[test]
    fn test_is_subspecializer() {
        let mut vm = PolarVirtualMachine::default();
        
        
        
        
        let arg = term!(Value::ExternalInstance(ExternalInstance {
            instance_id: 1,
            constructor: None,
            repr: None,
        }));
        let left = term!(value!(Pattern::Instance(InstanceLiteral {
            tag: sym!("Any"),
            fields: Dictionary {
                fields: btreemap! {}
            }
        })));
        let right = term!(Value::Pattern(Pattern::Dictionary(Dictionary {
            fields: btreemap! {sym!("a") => term!("a")},
        })));
        let answer = vm.kb.read().unwrap().gensym("is_subspecializer");
        match vm.is_subspecializer(&answer, &left, &right, &arg).unwrap() {
            QueryEvent::None => (),
            event => panic!("Expected None, got {:?}", event),
        }
        assert_eq!(
            vm.deref(&term!(Value::Variable(answer))),
            term!(value!(true))
        );
    }
    #[test]
    fn test_timeout() {
        let mut vm = PolarVirtualMachine::default();
        vm.set_query_timeout(1);
        
        vm.set_stack_limit(std::usize::MAX);
        loop {
            vm.push_goal(Goal::Noop).unwrap();
            vm.push_goal(Goal::UnifyExternal {
                left_instance_id: 1,
                right_instance_id: 1,
            })
            .unwrap();
            let result = vm.run();
            match result {
                Ok(event) => assert!(matches!(event, QueryEvent::ExternalUnify { .. })),
                Err(err) => {
                    assert!(matches!(err, error::PolarError {
                        kind: error::ErrorKind::Runtime(error::RuntimeError::QueryTimeout { .. }),
                        ..
                    }));
                    
                    break;
                }
            }
        }
    }
    #[test]
    fn test_prefiltering() {
        let bar_rule = GenericRule::new(
            sym!("bar"),
            vec![
                Arc::new(rule!("bar", [value!([1])])),
                Arc::new(rule!("bar", [value!([2])])),
            ],
        );
        let mut kb = KnowledgeBase::new();
        kb.add_generic_rule(bar_rule);
        let mut vm = PolarVirtualMachine::new_test(Arc::new(RwLock::new(kb)), false, vec![]);
        vm.bind(&sym!("x"), term!(1));
        let _ = vm.run();
        let _ = vm.next(Rc::new(query!(call!("bar", [value!([sym!("x")])]))));
        
        
        let next_goal = vm
            .goals
            .iter()
            .find(|g| matches!(g.as_ref(), Goal::FilterRules { .. }))
            .unwrap();
        let goal_debug = format!("{:#?}", next_goal);
        assert!(
            matches!(next_goal.as_ref(), Goal::FilterRules {
            ref applicable_rules, ref unfiltered_rules, ..
        } if unfiltered_rules.len() == 1 && applicable_rules.is_empty()),
            "Goal should contain just one prefiltered rule: {}",
            goal_debug
        );
    }
    #[test]
    fn choose_conditional() {
        let mut vm = PolarVirtualMachine::new_test(
            Arc::new(RwLock::new(KnowledgeBase::new())),
            false,
            vec![],
        );
        let consequent = Goal::Debug {
            message: "consequent".to_string(),
        };
        let alternative = Goal::Debug {
            message: "alternative".to_string(),
        };
        
        vm.choose_conditional(
            vec![Goal::Noop],
            vec![consequent.clone()],
            vec![alternative.clone()],
        )
        .unwrap();
        assert_query_events!(vm, [
            QueryEvent::Debug { message } if &message[..] == "consequent" && vm.is_halted(),
            QueryEvent::Done
        ]);
        
        vm.choose_conditional(
            vec![Goal::Backtrack],
            vec![consequent.clone()],
            vec![alternative.clone()],
        )
        .unwrap();
        assert_query_events!(vm, [
            QueryEvent::Debug { message } if &message[..] == "alternative" && vm.is_halted(),
            QueryEvent::Done
        ]);
        
        vm.choose_conditional(
            vec![
                Goal::Unify {
                    left: term!(sym!("x")),
                    right: term!(true),
                },
                query!(sym!("x")),
            ],
            vec![consequent],
            vec![alternative],
        )
        .unwrap();
        assert_query_events!(vm, [
            QueryEvent::Debug { message } if &message[..] == "consequent" && vm.bindings(true).is_empty() && vm.is_halted(),
            QueryEvent::Done
        ]);
    }
}