<!DOCTYPE html>
<html>

  <head>
    <script data-require="jquery@1.7.2" data-semver="1.7.2" src="//cdnjs.cloudflare.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script>
    <link rel="stylesheet" href="style.css" />
    <script src="script.js"></script>
    <script src="re_lib.js"></script>
    <script src="prolog_js_headed.js"></script>
    <script src="demo_es.js"></script>
    <script type="text/javascript">
    
    function re_finished() {
      $('#finished_control_div').show();
    }
    
    function start_over() {
      $('#output_div').html('');
      $('#finished_control_div').hide();
      $('#input_div').hide();
      $('#radio_input_div').hide();
      $('#control_div').show();
    }
    
    
     
  function showControlDiv() {
    console.log("_dbg in showControlDiv");
    $('#control_div').show();
  }



  function run_re() {
    $('#finished_control_div').show();
    var ctrl_div = document.getElementById("control_div");
    ctrl_div.style.display = "none";
    var output_div = document.getElementById("output_div");
    output_div.style.display = "inline";

    RuleEngine.reset();
    var re = setup_re();

    var diybio_help_term_Q = new Term('DIYBIO_HELP');
    
    var get_diybio_help_rule_call = new Rule('get_diybio_help');
    get_diybio_help_rule_call.addArg(diybio_help_term_Q);

    RuleEngine.base_query = get_diybio_help_rule_call;
    RuleEngine.finished_cb = re_finished;
    
    RuleEngine.gnd_val_only = true;
    RuleEngine.no_solutions_found_txt = 'Unable to determine appropriate help for your for your PC needs, thank you.';
    


    re.fireRule(get_diybio_help_rule_call);
  }
  </script>
  </head>

  <body>
  
    <header>
      <h1>New PC Purchase Helper</h1>
    </header>
    
    <div id="wrap">
      <div id="main-content">
        <!--
        <div align="center">
          <div class="app_button" id="version_button">Version 0.0.1</div>
        </div>
        -->
        
        <div id="output_div"></div>

        <div align="center">
          <div id="input_div">
            <form id="input_form" method="POST" onSubmit="Util.handle_binary_input(); return false;">
              <input type="radio" name="binary_input" value="yes" />
Yes              <br />
              <input type="radio" name="binary_input" value="no" />
No              <br />
              <input type="submit" id='binary_input_btn' value="Continue" onClick="Util.handle_binary_input();" />
            </form>
          </div>
          <div id="radio_input_div">
            <form id="radio_input_form" method="POST" onSubmit="Util.handle_radio_input(); return false;">
              <input type="radio" name="radio_input" value="yes" />
Yes              <br />
              <input type="radio" name="radio_input" value="no" />
No
                <input type="submit" id='radio_input_btn' value="Submit" onClick="Util.handle_radio_input();" />
            </form>
          </div>
        </div>
        <div align="center">
          <div id="control_div">
          <b>Welcome to PC purchase helper tool.<br /> Find answers to your PC questions in seconds.</b>
          <br /><br />
            <form name="control_form" method="POST" onSubmit="return false;">
              <input type="submit" value="Find My New PC" onClick="run_re();" />
            </form>
          </div>
           <div id="finished_control_div">
              <a href="javascript:;" onClick="start_over();">Start Over</a>
          </div>
        </div>
      </div>
    </div>
    
    <footer></footer>
    
  </body>

</html>
// Code goes here
$(function() {
$('input[type="radio"][name="binary_input"]').click(function () { $closestForm = $(this).parents('form');  $closestForm.submit();});
$('#binary_input_btn').hide();
});


/* Styles go here */

html, body{
 margin: 0;
 padding: 0;
 border: 0;
}

body {
 font-family:Arial,  sans-serif;
 line-height:1.5;
 font-size:16px;
 background: #fff;
 padding:5px;
 word-wrap: break-word;
 -webkit-text-size-adjust: none;
}
h1, h2, h3, h4, h5, h6{ font-weight: normal; }
p img { float: left; margin: 0 10px 5px 0; padding: 0; }
img { border: 0; max-width: 100%; }

/*  Main Styles */
body{
    margin: 0 auto 0 auto;
    width:95%;
    background: rgb(240,240,240);
}
header{
    top:0; left:0;
    height:45px;
    float: top;
}
footer {
    bottom:0; left:auto;
    height:48px;
    background-color:#c27b00;
    border-top:1px solid #eee;
    position:fixed;
    width:95%;
    margin: 0 auto;
    z-index:2;
}

header, footer{
    background-color: #DEEFFF;
    padding:0;
    //position:absolute;
    //position:relative;
    z-index:2;
    font-size:20px;
    //width:80%;
    text-align:center;
    color:#333;
    font-weight:bold;
    text-shadow:0 -1px 0 rgba(0,0,0,0.5);
    line-height:45px;
}
h1{
    margin:0;
    text-transform: uppercase;
}
#wrap {
    margin: 0 auto 0 auto;
    top:45px; bottom:48px; left:0;
    width:100%;
    overflow:auto;
    align:center;
}
#main-content {
    width: 500px;
	border: solid 1px #CCC;
    padding: 15px;
	margin: 10px auto;
	background-color: #FFF;
}

.app_button, button {
  -webkit-border-radius: 20px;
  border-radius: 20px;
  display: inline-block;
  padding: 0 20px;
  margin-bottom: 20px;
  cursor: pointer;
  width: 200px;
  background:#F7941E;
  background: -webkit-gradient(linear, 0 0, 0 70%, from(#F7941E), to(#D37F18));
  background: linear-gradient(#F7941E, #D37F18 70%);
  border: 1px solid #C97917;
  font-size: 18px;
  font-weight: 200;
  text-shadow: 0 -1px 0 #7E4B0D;
  height: 40px;
  line-height: 40px;
  color:#fff;
  text-align: center;
}

#output_div {
  margin:0 auto 0 auto;
  width:200px;
  display: none;
  color: rgb(0, 0, 0);
  text-align: center;
}

#input_div {
  margin:0 auto 0 auto;
  width:200px;
  display: none;
  color: rgb(0, 0, 0);
}

#radio_input_div {
  margin:0 auto 0 auto;
  width:200px;
  display: none;
  color: rgb(0, 0, 0);
}

#finish_control_div {
  margin:0 auto 0 auto;
  width:200px;
  display: none;
}


#control_div {
  margin:0 auto 0 auto;
  width:200px;
  display: inline;
}

#finished_control_div {
  margin: 5px auto 0 auto;
  font-size: 12px;
  display: none;
  width:200px;  
}
/* parser generated by jison 0.4.4 */
/*
  Returns a Parser object of the following structure:

  Parser: {
    yy: {}
  }

  Parser.prototype: {
    yy: {},
    trace: function(),
    symbols_: {associative list: name ==> number},
    terminals_: {associative list: number ==> name},
    productions_: [...],
    performAction: function anonymous(yytext, yyleng, yylineno, yy, yystate, $$, _$),
    table: [...],
    defaultActions: {...},
    parseError: function(str, hash),
    parse: function(input),

    lexer: {
        EOF: 1,
        parseError: function(str, hash),
        setInput: function(input),
        input: function(),
        unput: function(str),
        more: function(),
        less: function(n),
        pastInput: function(),
        upcomingInput: function(),
        showPosition: function(),
        test_match: function(regex_match_array, rule_index),
        next: function(),
        lex: function(),
        begin: function(condition),
        popState: function(),
        _currentRules: function(),
        topState: function(),
        pushState: function(condition),

        options: {
            ranges: boolean           (optional: true ==> token location info will include a .range[] member)
            flex: boolean             (optional: true ==> flex-like lexing behaviour where the rules are tested exhaustively to find the longest match)
            backtrack_lexer: boolean  (optional: true ==> lexer regexes are tested in order and for each matching regex the action code is invoked; the lexer terminates the scan when a token is returned by the action code)
        },

        performAction: function(yy, yy_, $avoiding_name_collisions, YY_START),
        rules: [...],
        conditions: {associative list: name ==> set},
    }
  }


  token location info (@$, _$, etc.): {
    first_line: n,
    last_line: n,
    first_column: n,
    last_column: n,
    range: [start_number, end_number]       (where the numbers are indexes into the input string, regular zero-based)
  }


  the parseError function receives a 'hash' object with these members for lexer and parser errors: {
    text:        (matched text)
    token:       (the produced terminal token, if any)
    line:        (yylineno)
  }
  while parser (grammar) errors will also provide these members, i.e. parser errors deliver a superset of attributes: {
    loc:         (yylloc)
    expected:    (string describing the set of expected tokens)
    recoverable: (boolean: TRUE when the parser has a error recovery rule available for this particular error)
  }
*/
var prolog = (function(){
var parser = {trace: function trace() { },
yy: {},
symbols_: {"error":2,"expressions":3,"program":4,"EOF":5,"clauselist":6,"query":7,"clause":8,"predicate":9,"PERIOD":10,"NECK":11,"predicatelist":12,"COMMA":13,"atom":14,"LEFTPAREN":15,"termlist":16,"RIGHTPAREN":17,"VARIABLE":18,"EQCMP":19,"string":20,"ASSIGN":21,"CUT":22,"term":23,"QUERYSYMBOL":24,"SMALLATOM":25,"STRING":26,"$accept":0,"$end":1},
terminals_: {2:"error",5:"EOF",10:"PERIOD",11:"NECK",13:"COMMA",15:"LEFTPAREN",17:"RIGHTPAREN",18:"VARIABLE",19:"EQCMP",21:"ASSIGN",22:"CUT",24:"QUERYSYMBOL",25:"SMALLATOM",26:"STRING"},
productions_: [0,[3,2],[4,1],[4,2],[4,1],[6,1],[6,2],[8,2],[8,4],[12,1],[12,3],[9,1],[9,4],[9,3],[9,3],[9,1],[16,1],[16,3],[23,1],[23,1],[7,3],[14,1],[14,1],[20,1]],
performAction: function anonymous(yytext, yyleng, yylineno, yy, yystate /* action[1] */, $$ /* vstack */, _$ /* lstack */) {
/* this == yyval */

var $0 = $$.length - 1;
switch (yystate) {
case 1: 
          //typeof console !== 'undefined' ? console.log($$[$0-1]) : print($$[$0-1]);
          if(is_debug) {
            console.log("here 0");
          }
          return $$[$0-1]; 
        
break;
case 2:
          if(is_debug) {
            console.log("here 1");
          }
        
break;
case 3:
          if(is_debug) {
            console.log("here 1.1");
          }
        
break;
case 4:
          if(is_debug) {
            console.log("here 2");
          }
        
break;
case 5:
          if(is_debug) {
            console.log("here 3");
          }
        
break;
case 6:
          if(is_debug) {
            console.log("here 4");
          }
        
break;
case 7:
          if(is_debug) {
            console.log("here 5");
          }
        
break;
case 8:
         if(is_debug) {
           console.log("here 6 1: " + $$[$0-3]);
           console.log("_dbg rules.length: " + rules.length); 
         }
         if(rules.length > 0) {
	   header = rules.pop();
           if(is_debug) {
	     console.log("_dbg header rule name: " + header.name + " with arg: " + header.args[0]);
           }

           for(var i=0;i< header.args.length;i++) {
             if(is_debug) {
	       console.log("_dbg header rule name: " + header.name + " adding term: " + header.args[i]);
             }
             //header.args[i] = new Term(header.args[i]);
           }
	   while(rules.length > 0) {
	     var body_rule = rules.pop();
             if(is_debug) {
	       console.log("_dbg adding to header body rule name: " + body_rule.name);
               for(var i=0;i< body_rule.args.length;i++) {
	         console.log("  _dbg with arg: " + i + " "+ body_rule.args[i]);

               }
             }
             if(body_rule.name == 'write' && body_rule.args[0]) {
               if(is_debug) {
                 console.log("_dbg write body_rule.args[0]: " + body_rule.args[0]);
               }
               body_rule = new Rule('write(' + body_rule.args[0] + ')');
             }

             if(body_rule.name == 'i' && body_rule.args[0]) {
               if(is_debug) {
                 console.log("_dbg i body_rule.args[0]: " + body_rule.args[0].name);
               }
               body_rule = new Rule('i(' + body_rule.args[0].name + ')');
             }
             //ex: iradiobtn(CPUCHOICE, {"Single":"single", "Dual":"dual"})
             if(body_rule.name == 'iradiobtn' && body_rule.args[0] && body_rule.args[1]) {
               if(is_debug) {
                 console.log("_dbg iradiobtn body_rule.args[0].name: " + body_rule.args[0].name + ' body_rule.args[1].name: ' + body_rule.args[1]);
               }
               body_rule = new Rule('iradiobtn(' + body_rule.args[0].name + ',' + body_rule.args[1] + ')');
               console.log("_dbg body_rule.name: " + body_rule.name);
             }


	     header.addRule(body_rule);
	   }
         }
         re.addRule(header);
        
break;
case 9:
         if(is_debug) {
           console.log("here 7 1: " + $$[$0]);
         }
         this.$=$$[$0];
        
break;
case 10:
         if(is_debug) {
           console.log("here 8");
         }
        
break;
case 11:
         if(is_debug) {
           console.log("here 9 1: " + $$[$0]);
         }
         this.$=$$[$0];
         var r = new Rule($$[$0]);
         rules.unshift(r);
        
break;
case 12:
         if(is_debug) {
           console.log("here 10");
           console.log("here 10 adding rule: " + $$[$0-3] + " with arg: " + args[0].name);
         }
         var r = new Rule($$[$0-3]);
         for(var i=0;i<args.length;i++) {
           r.addArg(args[i]); 
         }
         rules.unshift(r);
         args = [];
        
break;
case 13:
         this.$=$$[$0-2]+$$[$0-1]+$$[$0]
         var r = new Rule(this.$);
         rules.unshift(r);
         if(is_debug) {
           console.log("here 10.1: " + this.$);
         }

        
break;
case 14:
         this.$=$$[$0-2]+$$[$0-1]+$$[$0]
         var r = new Rule(this.$);
         rules.unshift(r);
         if(is_debug) {
           console.log("here 10.2: " + this.$);
         }

        
break;
case 15:
         this.$=$$[$0];
         var r = new Rule(this.$);
         rules.unshift(r);
         if(is_debug) {
           console.log("here 10.3: " + this.$);
         }

        
break;
case 16:
         if(is_debug) {
           console.log("here 11 1: " + $$[$0]);
         }
         this.$ = $$[$0]
        
break;
case 17:
         if(is_debug) {
           console.log("here 12");
         }
        
break;
case 18:
         if(is_debug) {
           console.log("here 14 1: " + $$[$0]);
         }
         this.$ = $$[$0];
         args.push($$[$0]);
        
break;
case 19:
         if(is_debug) {
           console.log("here 15 1: " + $$[$0]);
         }
         this.$ = $$[$0];
         var t_var = new Term(this.$);
         args.push(t_var);
        
break;
case 20:
         if(is_debug) {
           console.log("here 16 1: " + $$[$0-2]);
           console.log("here 16 2: " + $$[$0-1]);
         }
        
break;
case 21:
         if(is_debug) {
           console.log("here 18 1: " + $$[$0]);
         }
         this.$=$$[$0];
        
break;
case 22:
         if(is_debug) {
           console.log("here 19 1: " + $$[$0]);
         }
         this.$=$$[$0];
        
break;
case 23:
	this.$ = $$[$0].substring(1, $$[$0].length - 1);//js
	
break;
}
},
table: [{3:1,4:2,6:3,7:4,8:5,9:7,14:8,18:[1,9],20:12,22:[1,10],24:[1,6],25:[1,11],26:[1,13]},{1:[3]},{5:[1,14]},{5:[2,2],7:15,8:16,9:7,14:8,18:[1,9],20:12,22:[1,10],24:[1,6],25:[1,11],26:[1,13]},{5:[2,4]},{5:[2,5],18:[2,5],22:[2,5],24:[2,5],25:[2,5],26:[2,5]},{9:18,12:17,14:8,18:[1,9],20:12,22:[1,10],25:[1,11],26:[1,13]},{10:[1,19],11:[1,20]},{10:[2,11],11:[2,11],13:[2,11],15:[1,21]},{19:[1,22],21:[1,23]},{10:[2,15],11:[2,15],13:[2,15]},{10:[2,21],11:[2,21],13:[2,21],15:[2,21],17:[2,21]},{10:[2,22],11:[2,22],13:[2,22],15:[2,22],17:[2,22]},{10:[2,23],11:[2,23],13:[2,23],15:[2,23],17:[2,23]},{1:[2,1]},{5:[2,3]},{5:[2,6],18:[2,6],22:[2,6],24:[2,6],25:[2,6],26:[2,6]},{10:[1,24],13:[1,25]},{10:[2,9],13:[2,9]},{5:[2,7],18:[2,7],22:[2,7],24:[2,7],25:[2,7],26:[2,7]},{9:18,12:26,14:8,18:[1,9],20:12,22:[1,10],25:[1,11],26:[1,13]},{14:29,16:27,18:[1,30],20:12,23:28,25:[1,11],26:[1,13]},{20:31,26:[1,13]},{20:32,26:[1,13]},{5:[2,20]},{9:33,14:8,18:[1,9],20:12,22:[1,10],25:[1,11],26:[1,13]},{10:[1,34],13:[1,25]},{13:[1,36],17:[1,35]},{13:[2,16],17:[2,16]},{13:[2,18],17:[2,18]},{13:[2,19],17:[2,19]},{10:[2,13],11:[2,13],13:[2,13]},{10:[2,14],11:[2,14],13:[2,14]},{10:[2,10],13:[2,10]},{5:[2,8],18:[2,8],22:[2,8],24:[2,8],25:[2,8],26:[2,8]},{10:[2,12],11:[2,12],13:[2,12]},{14:29,18:[1,30],20:12,23:37,25:[1,11],26:[1,13]},{13:[2,17],17:[2,17]}],
defaultActions: {4:[2,4],14:[2,1],15:[2,3],24:[2,20]},
parseError: function parseError(str, hash) {
    if (hash.recoverable) {
        this.trace(str);
    } else {
        throw new Error(str);
    }
},
parse: function parse(input) {
    var self = this, stack = [0], vstack = [null], lstack = [], table = this.table, yytext = '', yylineno = 0, yyleng = 0, recovering = 0, TERROR = 2, EOF = 1;
    this.lexer.setInput(input);
    this.lexer.yy = this.yy;
    this.yy.lexer = this.lexer;
    this.yy.parser = this;
    if (typeof this.lexer.yylloc == 'undefined') {
        this.lexer.yylloc = {};
    }
    var yyloc = this.lexer.yylloc;
    lstack.push(yyloc);
    var ranges = this.lexer.options && this.lexer.options.ranges;
    if (typeof this.yy.parseError === 'function') {
        this.parseError = this.yy.parseError;
    } else {
        this.parseError = Object.getPrototypeOf(this).parseError;
    }
    function popStack(n) {
        stack.length = stack.length - 2 * n;
        vstack.length = vstack.length - n;
        lstack.length = lstack.length - n;
    }
    function lex() {
        var token;
        token = self.lexer.lex() || EOF;
        if (typeof token !== 'number') {
            token = self.symbols_[token] || token;
        }
        return token;
    }
    var symbol, preErrorSymbol, state, action, a, r, yyval = {}, p, len, newState, expected;
    while (true) {
        state = stack[stack.length - 1];
        if (this.defaultActions[state]) {
            action = this.defaultActions[state];
        } else {
            if (symbol === null || typeof symbol == 'undefined') {
                symbol = lex();
            }
            action = table[state] && table[state][symbol];
        }
                    if (typeof action === 'undefined' || !action.length || !action[0]) {
                var errStr = '';
                expected = [];
                for (p in table[state]) {
                    if (this.terminals_[p] && p > TERROR) {
                        expected.push('\'' + this.terminals_[p] + '\'');
                    }
                }
                if (this.lexer.showPosition) {
                    errStr = 'Parse error on line ' + (yylineno + 1) + ':\n' + this.lexer.showPosition() + '\nExpecting ' + expected.join(', ') + ', got \'' + (this.terminals_[symbol] || symbol) + '\'';
                } else {
                    errStr = 'Parse error on line ' + (yylineno + 1) + ': Unexpected ' + (symbol == EOF ? 'end of input' : '\'' + (this.terminals_[symbol] || symbol) + '\'');
                }
                this.parseError(errStr, {
                    text: this.lexer.match,
                    token: this.terminals_[symbol] || symbol,
                    line: this.lexer.yylineno,
                    loc: yyloc,
                    expected: expected
                });
            }
        if (action[0] instanceof Array && action.length > 1) {
            throw new Error('Parse Error: multiple actions possible at state: ' + state + ', token: ' + symbol);
        }
        switch (action[0]) {
        case 1:
            stack.push(symbol);
            vstack.push(this.lexer.yytext);
            lstack.push(this.lexer.yylloc);
            stack.push(action[1]);
            symbol = null;
            if (!preErrorSymbol) {
                yyleng = this.lexer.yyleng;
                yytext = this.lexer.yytext;
                yylineno = this.lexer.yylineno;
                yyloc = this.lexer.yylloc;
                if (recovering > 0) {
                    recovering--;
                }
            } else {
                symbol = preErrorSymbol;
                preErrorSymbol = null;
            }
            break;
        case 2:
            len = this.productions_[action[1]][1];
            yyval.$ = vstack[vstack.length - len];
            yyval._$ = {
                first_line: lstack[lstack.length - (len || 1)].first_line,
                last_line: lstack[lstack.length - 1].last_line,
                first_column: lstack[lstack.length - (len || 1)].first_column,
                last_column: lstack[lstack.length - 1].last_column
            };
            if (ranges) {
                yyval._$.range = [
                    lstack[lstack.length - (len || 1)].range[0],
                    lstack[lstack.length - 1].range[1]
                ];
            }
            r = this.performAction.call(yyval, yytext, yyleng, yylineno, this.yy, action[1], vstack, lstack);
            if (typeof r !== 'undefined') {
                return r;
            }
            if (len) {
                stack = stack.slice(0, -1 * len * 2);
                vstack = vstack.slice(0, -1 * len);
                lstack = lstack.slice(0, -1 * len);
            }
            stack.push(this.productions_[action[1]][0]);
            vstack.push(yyval.$);
            lstack.push(yyval._$);
            newState = table[stack[stack.length - 2]][stack[stack.length - 1]];
            stack.push(newState);
            break;
        case 3:
            return true;
        }
    }
    return true;
}};


var rules = [];
var args = [];
var re = RuleEngine.getREInst();

/* generated by jison-lex 0.2.0 */
var lexer = (function(){
var lexer = {

EOF:1,

parseError:function parseError(str, hash) {
        if (this.yy.parser) {
            this.yy.parser.parseError(str, hash);
        } else {
            throw new Error(str);
        }
    },

// resets the lexer, sets new input
setInput:function (input) {
        this._input = input;
        this._more = this._backtrack = this.done = false;
        this.yylineno = this.yyleng = 0;
        this.yytext = this.matched = this.match = '';
        this.conditionStack = ['INITIAL'];
        this.yylloc = {
            first_line: 1,
            first_column: 0,
            last_line: 1,
            last_column: 0
        };
        if (this.options.ranges) {
            this.yylloc.range = [0,0];
        }
        this.offset = 0;
        return this;
    },

// consumes and returns one char from the input
input:function () {
        var ch = this._input[0];
        this.yytext += ch;
        this.yyleng++;
        this.offset++;
        this.match += ch;
        this.matched += ch;
        var lines = ch.match(/(?:\r\n?|\n).*/g);
        if (lines) {
            this.yylineno++;
            this.yylloc.last_line++;
        } else {
            this.yylloc.last_column++;
        }
        if (this.options.ranges) {
            this.yylloc.range[1]++;
        }

        this._input = this._input.slice(1);
        return ch;
    },

// unshifts one char (or a string) into the input
unput:function (ch) {
        var len = ch.length;
        var lines = ch.split(/(?:\r\n?|\n)/g);

        this._input = ch + this._input;
        this.yytext = this.yytext.substr(0, this.yytext.length - len - 1);
        //this.yyleng -= len;
        this.offset -= len;
        var oldLines = this.match.split(/(?:\r\n?|\n)/g);
        this.match = this.match.substr(0, this.match.length - 1);
        this.matched = this.matched.substr(0, this.matched.length - 1);

        if (lines.length - 1) {
            this.yylineno -= lines.length - 1;
        }
        var r = this.yylloc.range;

        this.yylloc = {
            first_line: this.yylloc.first_line,
            last_line: this.yylineno + 1,
            first_column: this.yylloc.first_column,
            last_column: lines ?
                (lines.length === oldLines.length ? this.yylloc.first_column : 0)
                 + oldLines[oldLines.length - lines.length].length - lines[0].length :
              this.yylloc.first_column - len
        };

        if (this.options.ranges) {
            this.yylloc.range = [r[0], r[0] + this.yyleng - len];
        }
        this.yyleng = this.yytext.length;
        return this;
    },

// When called from action, caches matched text and appends it on next action
more:function () {
        this._more = true;
        return this;
    },

// When called from action, signals the lexer that this rule fails to match the input, so the next matching rule (regex) should be tested instead.
reject:function () {
        if (this.options.backtrack_lexer) {
            this._backtrack = true;
        } else {
            return this.parseError('Lexical error on line ' + (this.yylineno + 1) + '. You can only invoke reject() in the lexer when the lexer is of the backtracking persuasion (options.backtrack_lexer = true).\n' + this.showPosition(), {
                text: "",
                token: null,
                line: this.yylineno
            });

        }
        return this;
    },

// retain first n characters of the match
less:function (n) {
        this.unput(this.match.slice(n));
    },

// displays already matched input, i.e. for error messages
pastInput:function () {
        var past = this.matched.substr(0, this.matched.length - this.match.length);
        return (past.length > 20 ? '...':'') + past.substr(-20).replace(/\n/g, "");
    },

// displays upcoming input, i.e. for error messages
upcomingInput:function () {
        var next = this.match;
        if (next.length < 20) {
            next += this._input.substr(0, 20-next.length);
        }
        return (next.substr(0,20) + (next.length > 20 ? '...' : '')).replace(/\n/g, "");
    },

// displays the character position where the lexing error occurred, i.e. for error messages
showPosition:function () {
        var pre = this.pastInput();
        var c = new Array(pre.length + 1).join("-");
        return pre + this.upcomingInput() + "\n" + c + "^";
    },

// test the lexed token: return FALSE when not a match, otherwise return token
test_match:function (match, indexed_rule) {
        var token,
            lines,
            backup;

        if (this.options.backtrack_lexer) {
            // save context
            backup = {
                yylineno: this.yylineno,
                yylloc: {
                    first_line: this.yylloc.first_line,
                    last_line: this.last_line,
                    first_column: this.yylloc.first_column,
                    last_column: this.yylloc.last_column
                },
                yytext: this.yytext,
                match: this.match,
                matches: this.matches,
                matched: this.matched,
                yyleng: this.yyleng,
                offset: this.offset,
                _more: this._more,
                _input: this._input,
                yy: this.yy,
                conditionStack: this.conditionStack.slice(0),
                done: this.done
            };
            if (this.options.ranges) {
                backup.yylloc.range = this.yylloc.range.slice(0);
            }
        }

        lines = match[0].match(/(?:\r\n?|\n).*/g);
        if (lines) {
            this.yylineno += lines.length;
        }
        this.yylloc = {
            first_line: this.yylloc.last_line,
            last_line: this.yylineno + 1,
            first_column: this.yylloc.last_column,
            last_column: lines ?
                         lines[lines.length - 1].length - lines[lines.length - 1].match(/\r?\n?/)[0].length :
                         this.yylloc.last_column + match[0].length
        };
        this.yytext += match[0];
        this.match += match[0];
        this.matches = match;
        this.yyleng = this.yytext.length;
        if (this.options.ranges) {
            this.yylloc.range = [this.offset, this.offset += this.yyleng];
        }
        this._more = false;
        this._backtrack = false;
        this._input = this._input.slice(match[0].length);
        this.matched += match[0];
        token = this.performAction.call(this, this.yy, this, indexed_rule, this.conditionStack[this.conditionStack.length - 1]);
        if (this.done && this._input) {
            this.done = false;
        }
        if (token) {
            if (this.options.backtrack_lexer) {
                delete backup;
            }
            return token;
        } else if (this._backtrack) {
            // recover context
            for (var k in backup) {
                this[k] = backup[k];
            }
            return false; // rule action called reject() implying the next rule should be tested instead.
        }
        if (this.options.backtrack_lexer) {
            delete backup;
        }
        return false;
    },

// return next match in input
next:function () {
        if (this.done) {
            return this.EOF;
        }
        if (!this._input) {
            this.done = true;
        }

        var token,
            match,
            tempMatch,
            index;
        if (!this._more) {
            this.yytext = '';
            this.match = '';
        }
        var rules = this._currentRules();
        for (var i = 0; i < rules.length; i++) {
            tempMatch = this._input.match(this.rules[rules[i]]);
            if (tempMatch && (!match || tempMatch[0].length > match[0].length)) {
                match = tempMatch;
                index = i;
                if (this.options.backtrack_lexer) {
                    token = this.test_match(tempMatch, rules[i]);
                    if (token !== false) {
                        return token;
                    } else if (this._backtrack) {
                        match = false;
                        continue; // rule action called reject() implying a rule MISmatch.
                    } else {
                        // else: this is a lexer rule which consumes input without producing a token (e.g. whitespace)
                        return false;
                    }
                } else if (!this.options.flex) {
                    break;
                }
            }
        }
        if (match) {
            token = this.test_match(match, rules[index]);
            if (token !== false) {
                return token;
            }
            // else: this is a lexer rule which consumes input without producing a token (e.g. whitespace)
            return false;
        }
        if (this._input === "") {
            return this.EOF;
        } else {
            return this.parseError('Lexical error on line ' + (this.yylineno + 1) + '. Unrecognized text.\n' + this.showPosition(), {
                text: "",
                token: null,
                line: this.yylineno
            });
        }
    },

// return next match that has a token
lex:function lex() {
        var r = this.next();
        if (r) {
            return r;
        } else {
            return this.lex();
        }
    },

// activates a new lexer condition state (pushes the new lexer condition state onto the condition stack)
begin:function begin(condition) {
        this.conditionStack.push(condition);
    },

// pop the previously active lexer condition state off the condition stack
popState:function popState() {
        var n = this.conditionStack.length - 1;
        if (n > 0) {
            return this.conditionStack.pop();
        } else {
            return this.conditionStack[0];
        }
    },

// produce the lexer rule set which is active for the currently active lexer condition state
_currentRules:function _currentRules() {
        if (this.conditionStack.length && this.conditionStack[this.conditionStack.length - 1]) {
            return this.conditions[this.conditionStack[this.conditionStack.length - 1]].rules;
        } else {
            return this.conditions["INITIAL"].rules;
        }
    },

// return the currently active lexer condition state; when an index argument is provided it produces the N-th previous condition state, if available
topState:function topState(n) {
        n = this.conditionStack.length - 1 - Math.abs(n || 0);
        if (n >= 0) {
            return this.conditionStack[n];
        } else {
            return "INITIAL";
        }
    },

// alias for begin(condition)
pushState:function pushState(condition) {
        this.begin(condition);
    },

// return the number of states currently on the stack
stateStackSize:function stateStackSize() {
        return this.conditionStack.length;
    },
options: {},
performAction: function anonymous(yy,yy_,$avoiding_name_collisions,YY_START) {

var YYSTATE=YY_START;
switch($avoiding_name_collisions) {
case 0:/* skip whitespace */
break;
case 1:return 26;
break;
case 2:return 26;
break;
case 3:return 'DIGIT';
break;
case 4:return 18;
break;
case 5:return 25;
break;
case 6:return 'LOWCASELETTER';
break;
case 7:return 'UPPERCASELETTER';
break;
case 8:return 19;
break;
case 9:return 21;
break;
case 10:return 13;
break;
case 11:return 10;
break;
case 12:return 11;
break;
case 13:return 22;
break;
case 14:return 24;
break;
case 15:return 15;
break;
case 16:return 17;
break;
case 17:return 5;
break;
case 18:return 'INVALID';
break;
}
},
rules: [/^(?:\s+)/,/^(?:"(\\["]|[^"])*")/,/^(?:'(\\[']|[^'])*')/,/^(?:[0-9])/,/^(?:[A-Z_]\w*)/,/^(?:[a-z_]\w*)/,/^(?:[a-z])/,/^(?:[A-Z])/,/^(?:==)/,/^(?:=)/,/^(?:,)/,/^(?:\.)/,/^(?::-)/,/^(?:!)/,/^(?:\?-)/,/^(?:\()/,/^(?:\))/,/^(?:$)/,/^(?:.)/],
conditions: {"INITIAL":{"rules":[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18],"inclusive":true}}
};
return lexer;
})();
parser.lexer = lexer;
function Parser () {
  this.yy = {};
}
Parser.prototype = parser;parser.Parser = Parser;
return new Parser;
})();


function setup_re() {
  var re = RuleEngine.getREInst();
  var source = "\
    get_diybio_help(DIYCOMP_HELP):- write('Do you need help with choosing a new computer?'), i(IS_NEED_HELP), \
                      handle_need_help(DIYCOMP_HELP, IS_NEED_HELP).\
    handle_need_help(DIYCOMP_HELP, IS_NEED_HELP):- IS_NEED_HELP == 'yes', write('What is your budget?'), \
                      iradiobtn(COMP_AREA, '\{\"Under 500$\":\"budget\", \"500$-1000$\":\"midrange\"\, \"1000$-1500$\":\"upper\", \"Over 1500$\":\"professional\"}'), \
                      handle_comp_area_choice(DIYCOMP_HELP, COMP_AREA).\
    handle_comp_area_choice(DIYCOMP_HELP, COMP_AREA):- write('Which type of computer are you looking to buy?'), \
                      iradiobtn(PRICE_AREA, '\{\"Laptop\":\"laptop\", \"Desktop\":\"desktop\"\}'), \
                      handle_web_area_choice(DIYCOMP_HELP, PRICE_AREA, COMP_AREA).\
    handle_web_area_choice(DIYCOMP_HELP, PRICE_AREA, COMP_AREA):- PRICE_AREA == 'desktop', COMP_AREA == 'budget', DIYCOMP_HELP='Lenovo IdeaCentre 510A', !. \
    handle_web_area_choice(DIYCOMP_HELP, PRICE_AREA, COMP_AREA):- PRICE_AREA == 'laptop', COMP_AREA =='budget', DIYCOMP_HELP='Asus Chromebook Flip', !.\
    handle_web_area_choice(DIYCOMP_HELP, PRICE_AREA, COMP_AREA):- PRICE_AREA == 'desktop', COMP_AREA == 'midrange', DIYCOMP_HELP='Dell Inspiron Desktop 5680 ', !. \
    handle_web_area_choice(DIYCOMP_HELP, PRICE_AREA, COMP_AREA):- PRICE_AREA == 'laptop', COMP_AREA =='midrange', DIYCOMP_HELP='Microsoft Surface Laptop 2', !.\
    handle_web_area_choice(DIYCOMP_HELP, PRICE_AREA, COMP_AREA):- PRICE_AREA == 'desktop', COMP_AREA == 'upper', DIYCOMP_HELP='Dell OptiPlex 7760 All-in-One', !. \
    handle_web_area_choice(DIYCOMP_HELP, PRICE_AREA, COMP_AREA):- PRICE_AREA == 'laptop', COMP_AREA =='upper', DIYCOMP_HELP='Dell XPS 15 2-in-1', !.\
    handle_web_area_choice(DIYCOMP_HELP, PRICE_AREA, COMP_AREA):- PRICE_AREA == 'desktop', COMP_AREA == 'professional', DIYCOMP_HELP='Apple iMac with Retina 5K Display', !. \
    handle_web_area_choice(DIYCOMP_HELP, PRICE_AREA, COMP_AREA):- PRICE_AREA == 'laptop', COMP_AREA =='professional', DIYCOMP_HELP='Apple MacBook Pro with Touch Bar 13-inch 2018', !.\
    ";
  prolog.parse(source);

  return re;
}
var Util = (function () {
    function Util() { }
    Util.is_in_browser = function is_in_browser() {
        return !(typeof window === 'undefined');
    }
    Util.output = function output(s) {
        if(Util.is_in_browser()) {
            document.getElementById('output_div').innerHTML = s;
        } else {
            console.log(s + '\n');
        }
    }
    Util.inputRadio = function inputRadio(cb, radio_data) {
        RuleEngine.async_hold = true;
        if(is_debug) {
            console.log("_dbg in input");
            console.log("_dbg rule_firing # b_args: " + RuleEngine.rule_firing.b_args.length);
        }
        if(Util.is_in_browser()) {
            var radio_input_div = document.getElementById("radio_input_div");
            var btn_string = '';
            var pre_radio_html = '<input type="radio" name="radio_input" value="';
            for(var k in radio_data) {
                if(radio_data.hasOwnProperty(k)) {
                    console.log('key is: ' + k + ', value is: ' + radio_data[k]);
                    btn_string += pre_radio_html + radio_data[k] + '">' + k + '<br>';
                }
            }
            console.log("_dbg btn_string: " + btn_string);
            $('#radio_input_div').html('\
        <form id="radio_input_form" method="POST" onsubmit="Util.handle_radio_input(); return false;">\
          ' + btn_string + ' \
          <input type="submit" value="Continue" onclick="Util.handle_radio_input();">\
        </form>');
            $('input[type="radio"][name="radio_input"]').click(function () { $closestForm = $(this).parents('form');  $closestForm.submit();});
            $('#radio_input_btn').hide();
            radio_input_div.style.display = "inline";
            RuleEngine.input_cb = cb;
        } else {
            var prompt = require('prompt');
            prompt.start();
            prompt.get([
                'input_str'
            ], function (err, result) {
                if(err) {
                    return onErr(err);
                }
                if(is_debug) {
                    console.log('Command-line input received:');
                    console.log('  input_str: ' + result.input_str);
                }
                cb(result.input_str);
            });
            function onErr(err) {
                console.log(err);
                return 1;
            }
        }
    }
    Util.input = function input(cb) {
        RuleEngine.async_hold = true;
        if(is_debug) {
            console.log("_dbg in input");
            console.log("_dbg rule_firing # b_args: " + RuleEngine.rule_firing.b_args.length);
        }
        if(Util.is_in_browser()) {
            var input_div = document.getElementById("input_div");
            input_div.style.display = "inline";
            RuleEngine.input_cb = cb;
        } else {
            var prompt = require('prompt');
            prompt.start();
            prompt.get([
                'input_str'
            ], function (err, result) {
                if(err) {
                    return onErr(err);
                }
                if(is_debug) {
                    console.log('Command-line input received:');
                    console.log('  input_str: ' + result.input_str);
                }
                cb(result.input_str);
            });
            function onErr(err) {
                console.log(err);
                return 1;
            }
        }
    }
    Util.add_qa_bool_rule = function add_qa_bool_rule() {
        var re = RuleEngine.getREInst();
        var qa_bool_rule = new Rule('qa_bool');
        var qa_question_term = new Term('Q');
        var qa_answer_term = new Term('BOOL_A');
        qa_bool_rule.addArg(qa_question_term);
        qa_bool_rule.addArg(qa_answer_term);
        re.addRule(qa_bool_rule);
        var qa_rule_o1 = new Rule('ov(Q)');
        re.addRule(qa_rule_o1);
        qa_bool_rule.addRule(qa_rule_o1);
        var qa_rule_i1 = new Rule('i(BOOL_A)');
        re.addRule(qa_rule_i1);
        qa_bool_rule.addRule(qa_rule_i1);
    }
    Util.handle_radio_input = function handle_radio_input(document) {
        console.log("_dbg in handle_radio_input");
    
        var radio_val = $("#radio_input_form input[type='radio']:checked").val();
        console.log("_dbg radio_val: " + radio_val);
        $('#radio_input_div').hide();
        $("input:radio[name='radio_input']").each(function () { $(this).prop('checked', false); });
        
        RuleEngine.input_cb(radio_val);
    }
    Util.handle_binary_input = function handle_binary_input(document) {
        console.log("_dbg in handle_binary_input");
        var radio_val = $("#input_form input[type='radio']:checked").val();
        
        console.log("_dbg radio_val: " + radio_val);
        $('#input_div').hide();
        $("input:radio[name='binary_input']").each(function () { $(this).prop('checked', false); });
        
        RuleEngine.input_cb(radio_val);
    }
    Util.create_rule_from_qa_table = function create_rule_from_qa_table(input_rule_name, input_rulehdr_args, qa_t, call_rule_name) {
        var qa_input_rule = Util.create_rule_header(input_rule_name, input_rulehdr_args);
        var call_rule_args = [];
        for(var i = 0; i < qa_t.length; i++) {
            var qa_info = qa_t[i];
            qa_input_rule.rules.push(new Rule('o(' + qa_info[0] + ')'));
            if(qa_info[1] == 'yes_no') {
                qa_input_rule.rules.push(new Rule('i(' + qa_info[2] + ')'));
                call_rule_args.push(new Term(qa_info[2]));
            }
        }
        for(var i = 0; i < input_rulehdr_args.length; i++) {
            call_rule_args.push(new Term(input_rulehdr_args[i].name));
        }
        var call_rule = Util.create_rule_header(call_rule_name, call_rule_args);
        qa_input_rule.rules.push(call_rule);
        return qa_input_rule;
    }
    Util.gen_rules_from_truth_table = function gen_rules_from_truth_table(rule_name, header_args, tt) {
        var generated_rules = [];
        for(var i = 0; i < tt.length; i++) {
            var rule = Util.create_rule_header(rule_name, header_args);
            var body_tt_row = tt[i];
            for(var j = 0; j < body_tt_row.length; j++) {
                if(body_tt_row[j] === undefined) {
                    continue;
                }
                if(j == body_tt_row.length - 2) {
                    if(body_tt_row[j] !== undefined) {
                        rule.addRule(new Rule(header_args[j].name + '=' + body_tt_row[j]));
                    }
                } else {
                    if(j == body_tt_row.length - 1) {
                        if(Object.prototype.toString.call(body_tt_row[j]) == "[object Array]") {
                            var suffix_rules = body_tt_row[j];
                            for(var k = 0; k < suffix_rules.length; k++) {
                                rule.addRule(suffix_rules[k]);
                            }
                        }
                    } else {
                        rule.addRule(new Rule(header_args[j].name + '==' + body_tt_row[j]));
                    }
                }
            }
            generated_rules.push(rule);
        }
        return generated_rules;
    }
    Util.create_rule_header = function create_rule_header(rule_name, header_args) {
        var new_rule = new Rule(rule_name);
        for(var i = 0; i < header_args.length; i++) {
            new_rule.addArg(header_args[i]);
        }
        return new_rule;
    }
    return Util;
})();
var is_debug = true;
var Types = (function () {
    function Types() { }
    Types.types = [];
    Types.registerType = function registerType(clsName, classDcl) {
        Types.types[clsName] = classDcl;
    }
    return Types;
})();
var Atom = (function () {
    function Atom(name) {
        this.name = name;
    }
    Atom.prototype.deepcopy = function () {
        var atom_copy = new Atom(this.name);
        return atom_copy;
    };
    return Atom;
})();
Types.registerType('Atom', Atom);
var Term = (function () {
    function Term(name) {
        this.name = name;
        this.grounded = this;
    }
    Term.prototype.deepcopy = function () {
        var term_copy = new Term(this.name);
        term_copy.name = this.name;
        if(!this.isGrounded()) {
            term_copy.grounded = this.grounded;
        } else {
            term_copy.grounded = term_copy;
        }
        return term_copy;
    };
    Term.prototype.isBoundorAliased = function () {
        var ret_val = false;
        if(RuleEngine.getTypeName(this.grounded) == 'Atom') {
            ret_val = true;
        }
        if(RuleEngine.getTypeName(this.grounded) == 'Term') {
            ret_val = true;
        }
        return ret_val;
    };
    Term.prototype.isFree = function () {
        if(this.grounded == this) {
            return true;
        } else {
            return false;
        }
    };
    Term.prototype.isGrounded = function () {
        var is_grounded = true;
        if(this.grounded == this) {
            is_grounded = false;
        } else {
            if(this.isBoundorAliased()) {
                is_grounded = this.grounded.isGrounded();
            }
        }
        return is_grounded;
    };
    Term.prototype.reset = function () {
        this.grounded = this;
    };
    Term.prototype.unify = function (t) {
        if(is_debug) {
            console.log("_dbg in unify");
            console.log("_dbg this.name: " + this.name);
            console.log("_dbg t type: " + RuleEngine.getTypeName(t));
            if(RuleEngine.getTypeName(t)) {
                console.log("_dbg value: " + t);
            }
        }
        var unified = false;
        if(this.isFree()) {
            if(is_debug) {
                console.log("_dbg term is free, assigning grounded");
                if(RuleEngine.getTypeName(t)) {
                    console.log("_dbg to Term with name: " + t.name);
                }
            }
            this.grounded = t;
            unified = true;
        } else {
            if(this.isBoundorAliased()) {
                unified = this.grounded.unify(t);
            } else {
                unified = false;
            }
        }
        if(unified) {
            RuleEngine.choices.push(this);
        }
        return unified;
    };
    Term.prototype.getGrounded = function () {
        if(is_debug) {
        }
        if(this.isFree()) {
            return this;
        }
        var ret_val = this.grounded;
        if(this.isBoundorAliased()) {
            if(is_debug) {
            }
            ret_val = this.grounded.getGrounded();
            if(is_debug) {
            }
        }
        return ret_val;
    };
    return Term;
})();
Types.registerType('Term', Term);
var Rule = (function () {
    function Rule(name) {
        this.args = [];
        this.b_args = [];
        this.rules = [];
        this.is_context_change = false;
        this.non_call_regex = /=|fail|!|o\(|ov\(|i\(|write\(|iradiobtn\(/;
        this.solutions = [];
        this.name = name;
        this.proven = false;
    }
    Rule.prototype.deepcopy = function () {
        if(is_debug) {
            console.log("_dbg in Rule.deepcopy");
        }
        var rule_copy = new Rule(this.name);
        for(var i = 0; i < this.args.length; i++) {
            if(is_debug) {
                console.log("_dbg about to call deepcopy on arg with name: " + this.args[i].name);
            }
            rule_copy.args.push(this.args[i].deepcopy());
        }
        for(var r = 0; r < this.rules.length; r++) {
            var call_copy = new Rule(this.rules[r].name);
            for(var a = 0; a < this.rules[r].args.length; a++) {
                call_copy.args.push(this.rules[r].args[a].deepcopy());
            }
            for(var b = 0; b < this.rules[r].b_args.length; b++) {
                call_copy.b_args.push(this.rules[r].b_args[b].deepcopy());
            }
            rule_copy.rules.unshift(call_copy);
        }
        rule_copy.proven = this.proven;
        rule_copy.solutions = this.solutions;
        rule_copy.is_context_change = this.is_context_change;
        if(is_debug) {
            console.log("_dbg about to return from Rule.deepcopy");
        }
        return rule_copy;
    };
    Rule.prototype.addArg = function (arg) {
        this.args.push(arg);
    };
    Rule.prototype.addBarg = function (b_arg) {
        this.b_args.push(b_arg);
    };
    Rule.prototype.addRule = function (rule) {
        this.rules.push(rule);
    };
    Rule.prototype.is_query = function () {
        return !this.rules.length && !this.non_call_regex.test(this.name);
    };
    Rule.prototype.is_non_call = function () {
        return !this.rules.length && !this.args.length && this.non_call_regex.test(this.name);
    };
    return Rule;
})();
Types.registerType('Rule', Rule);
var Choice = (function () {
    function Choice(query, rule, body_rules) {
        this.body_rules = [];
        this.query = query;
        this.rule = rule;
        this.body_rules = body_rules;
    }
    return Choice;
})();
Types.registerType('Choice', Choice);
var RuleEngine = (function () {
    function RuleEngine() { }
    RuleEngine.re_inst = 0;
    RuleEngine.rules = [];
    RuleEngine.gnd_val_only = false;
    RuleEngine.no_solutions_found_txt = '';
    RuleEngine.choices = [];
    RuleEngine.body_rules = [];
    RuleEngine.rule_firing = undefined;
    RuleEngine.body_rule_firing = undefined;
    RuleEngine.async_hold = false;
    RuleEngine.base_query = undefined;
    RuleEngine.input_cb = undefined;
    RuleEngine.finished_cb = undefined;
    RuleEngine.more_solutions_prompt = true;
    RuleEngine.reset = function reset() {
        RuleEngine.re_inst = 0;
        RuleEngine.rules = [];
        RuleEngine.choices = [];
        RuleEngine.body_rules = [];
        RuleEngine.async_hold = false;
        RuleEngine.input_cb = 0;
        RuleEngine.base_query = 0;
        RuleEngine.rule_firing = 0;
        RuleEngine.body_rule_firing = 0;
        RuleEngine.finished_cb = 0;
        RuleEngine.more_solutions_prompt = true;
    }
    RuleEngine.prototype.isFinished = function () {
        var is_finished = false;
        if(RuleEngine.body_rules.length == 0) {
            if(RuleEngine.choices.length == 0) {
                is_finished = true;
            }
        }
        return is_finished;
    };
    RuleEngine.prototype.handleBaseQueryFinish = function () {
        if(RuleEngine.async_hold) {
            return;
        }
        if(is_debug) {
            console.log("_dbg in handleBaseQueryFinish");
        }
        if(RuleEngine.body_rules.length == 0) {
            if(is_debug) {
                console.log("_dbg base_query == r, about to call handleQueryResult");
            }
            if(this.isFinished()) {
                if(RuleEngine.finished_cb) {
                    RuleEngine.finished_cb();
                }
            }
            this.handleQueryResult();
        }
    };
    RuleEngine.getREInst = function getREInst() {
        if(!RuleEngine.re_inst) {
            RuleEngine.re_inst = new RuleEngine();
        }
        return RuleEngine.re_inst;
    }
    RuleEngine.prototype.addBodyRule = function (rule) {
        RuleEngine.body_rules.push(rule);
    };
    RuleEngine.prototype.addRule = function (rule) {
        RuleEngine.rules.push(rule);
        for(var i = 0; i < rule.rules.length; i++) {
            if(!rule.is_query()) {
                var foundRules = this.searchRules(RuleEngine.rules, rule.name, rule.args);
                if(foundRules.length == 1) {
                    for(var j = 0; j < foundRules.length; j++) {
                        var found_bodyRule = foundRules[j];
                        if(found_bodyRule.is_non_call()) {
                            rule.rules[i] = found_bodyRule;
                        }
                    }
                }
            }
        }
    };
    RuleEngine.getTypeName = function getTypeName(inst) {
        var typeName = undefined;
        for(var clsName in Types.types) {
            if(inst instanceof Types.types[clsName]) {
                typeName = clsName;
            }
        }
        return typeName;
    }
    RuleEngine.findArg = function findArg(name, args) {
        for(var i = 0; i < args.length; i++) {
            if(name == args[i].name) {
                return args[i];
            }
        }
    }
    RuleEngine.prototype.backTrack = function () {
        var is_backTracked = false;
        var found_choice = undefined;
        RuleEngine.rule_firing = undefined;
        RuleEngine.body_rule_firing = undefined;
        RuleEngine.body_rules = [];
        if(is_debug) {
            console.log("_dbg in backTrack");
            console.log("_dbg RuleEngine.choices.length: " + RuleEngine.choices.length);
            RuleEngine.dump_choices();
        }
        while(!is_backTracked) {
            if(RuleEngine.choices.length > 0) {
                var choice_or_term = RuleEngine.choices.pop();
                if(RuleEngine.getTypeName(choice_or_term) == 'Term') {
                    choice_or_term.reset();
                } else {
                    if(RuleEngine.getTypeName(choice_or_term) == 'Choice') {
                        found_choice = choice_or_term;
                        is_backTracked = true;
                    } else {
                        throw new TypeError("BackTrack error, unknown type popped from choice point stack");
                    }
                }
            } else {
                is_backTracked = true;
            }
        }
        if(found_choice) {
            RuleEngine.body_rules = found_choice.body_rules;
            var rule_copy = this.prepareToFire(found_choice.query, found_choice.rule, false);
            if(is_debug) {
                if(!this.check_copy_args_link_to_base_args(rule_copy)) {
                    console.log("_dbg 6 rule_copy: " + rule_copy.name + " args not found in alias chain of base query args after unifying with query: " + found_choice.query.name);
                } else {
                    console.log("_dbg 6 rule_copy: " + rule_copy.name + " args found in alias chain of base query args after unifying with query: " + found_choice.query.name);
                }
            }
            this.fireRule(rule_copy);
        } else {
            if(is_debug) {
                console.log("_dbg backTrack found no choices to fire on choice point stack");
            }
        }
    };
    RuleEngine.prototype.isArgsMatch = function (args1, args2) {
        var match = true;
        if(args1.length == args2.length) {
            if(is_debug) {
                console.log("_dbg num args match\n");
            }
        } else {
            match = false;
        }
        return match;
    };
    RuleEngine.prototype.searchRules = function (rules, name, args) {
        if(is_debug) {
            console.log("_dbg in searchRules\n");
            console.log("_dbg num rules searching: " + rules.length + "\n");
            console.log("_dbg searching for name: " + name + "\n");
        }
        var foundRules = [];
        for(var i in rules) {
            if(is_debug) {
                console.log("_dbg rule name: " + rules[i].name + "\n");
            }
            if(name == rules[i].name) {
                if(is_debug) {
                    console.log("_dbg found rule\n");
                }
                if(this.isArgsMatch(rules[i].args, args)) {
                    if(is_debug) {
                        console.log("_dbg args match\n");
                    }
                    foundRules.push(rules[i]);
                } else {
                    console.log("_dbg args do not match\n");
                }
            }
        }
        return foundRules;
    };
    RuleEngine.prototype.unifyHeaderBodyArgsToQueryArgs = function (query) {
        if(is_debug) {
            console.log("_dbg in unifyHeaderBodyArgsToQueryArgs");
        }
        var header = RuleEngine.rule_firing;
        for(var i = 0; i < header.b_args.length; i++) {
            for(var j = 0; j < query.args.length; j++) {
                if(is_debug) {
                    console.log("_dbg cmp b arg name: " + query.args[j].name + " with  header arg name" + header.b_args[i].name);
                }
                if(query.args[j].name == header.b_args[i].name) {
                    query.args[j] = header.b_args[i];
                }
            }
        }
    };
    RuleEngine.prototype.unifyHeaderArgsToBodyCallArgs = function (rule) {
        var header = rule;
        var body_rules = rule.rules;
        for(var k = 0; k < body_rules.length; k++) {
            var body_rule = body_rules[k];
            if(is_debug) {
                console.log("_dbg in unifyHeaderArgsToBodyCallArgs h: " + header.name + " b: " + body_rule.name);
            }
            for(var i = 0; i < header.args.length; i++) {
                for(var j = 0; j < body_rule.args.length; j++) {
                    if(is_debug) {
                        console.log("_dbg cmp b arg name: " + body_rule.args[j].name + " with  header arg name" + header.args[i].name);
                    }
                    if(body_rule.args[j].name == header.args[i].name) {
                        body_rule.args[j] = header.args[i];
                    }
                }
            }
        }
    };
    RuleEngine.prototype.unifyRuleHeaders = function (r1, r2) {
        if(is_debug) {
            console.log("_dbg in unifyRuleHeaders");
            console.log("_dbg unifying r1: " + r1.name);
            RuleEngine.dump_rule(r1);
            console.log("_dbg with r2: " + r2.name);
            RuleEngine.dump_rule(r2);
        }
        for(var i = 0; i < r1.args.length; i++) {
            if(RuleEngine.getTypeName(r1.args[i]) == 'Term' && !r1.args[i].isGrounded()) {
                if(is_debug) {
                    console.log("_dbg calling unify on arg: " + r1.args[i].name + " v: " + r1.args[i].getGrounded() + " with arg: " + r2.args[i].name + " v: " + r2.args[i].getGrounded());
                }
                r1.args[i].unify(r2.args[i]);
                if(is_debug) {
                    console.log("_dbg unifying " + r2.args[i].name + " with " + r1.args[i].name);
                    console.log("_dbg r1.args[i].grounded type: " + RuleEngine.getTypeName(r1.args[i].grounded));
                    RuleEngine.dump_term_alias_chain(r1.args[i]);
                    if(r2.args[i].name == 'X1') {
                        var bq_hd_arg = RuleEngine.base_query.args[0];
                        console.log("\n_dbg dumping base query arg name: " + bq_hd_arg.name);
                        if(RuleEngine.is_term_in_alias_chain(r1.args[i], bq_hd_arg)) {
                            console.log("_dbg 2 term: " + r1.args[i].name + " is in alias chain of base query arg: " + bq_hd_arg.name);
                        } else {
                            console.log("_dbg 2 term: " + r1.args[i].name + " is not in alias chain of base query arg: " + bq_hd_arg.name);
                        }
                        if(RuleEngine.is_term_in_alias_chain(r2.args[i], bq_hd_arg)) {
                            console.log("_dbg term: " + r2.args[i].name + " is in alias chain of base query arg: " + bq_hd_arg.name);
                        } else {
                            console.log("_dbg term: " + r2.args[i].name + " is not in alias chain of base query arg: " + bq_hd_arg.name);
                        }
                        RuleEngine.dump_term_alias_chain(bq_hd_arg);
                        console.log("_dbg r1.args[i].getGrounded: " + r1.args[i].getGrounded());
                    }
                }
            } else {
                if(RuleEngine.getTypeName(r2.args[i]) == 'Term' && !r2.args[i].isGrounded()) {
                    if(is_debug) {
                        console.log("_dbg calling unify on r2");
                    }
                    if(is_debug) {
                        console.log("_dbg unifying " + r1.args[i].name + " with " + r2.args[i].name);
                    }
                    r2.args[i].unify(r1.args[i]);
                }
            }
            if((RuleEngine.getTypeName(r2.args[i]) == 'Term' && r2.args[i].isGrounded()) && (RuleEngine.getTypeName(r2.args[i]) == 'Term' && !r2.args[i].isGrounded())) {
                throw "cannot unify two grounded header args";
            }
        }
        if(is_debug) {
            console.log("_dbg exiting unifyRuleHeaders");
        }
    };
    RuleEngine.prototype.handleFoundRules = function (query, foundRules) {
        if(is_debug) {
            console.log("_dbg in handleFoundRules");
            console.log("_dbg # foundRules: " + foundRules.length);
            console.log("_dbg # RuleEngine.body_rules: " + RuleEngine.body_rules.length);
            if(!this.check_copy_args_link_to_base_args(query)) {
                console.log("_dbg 5 rule: " + query.name + " args not found in alias chain of base query args!");
            } else {
                console.log("_dbg 5 rule: " + query.name + " args found in alias chain of base query args!");
            }
        }
        if(is_debug) {
            console.log("_dbg choices before adding: ");
            RuleEngine.dump_choices();
        }
        for(var i = foundRules.length - 1; i > 0; i--) {
            var body_rules_copy = [];
            for(var j = 0; j < RuleEngine.body_rules.length; j++) {
                body_rules_copy.unshift(RuleEngine.body_rules[j].deepcopy());
            }
            var choice = new Choice(query, foundRules[i], body_rules_copy);
            if(is_debug) {
                console.log("_dbg adding choice: " + foundRules[i].name);
            }
            RuleEngine.choices.push(choice);
        }
        if(is_debug) {
            console.log("_dbg choices after adding: ");
            RuleEngine.dump_choices();
        }
    };
    RuleEngine.prototype.check_copy_args_link_to_base_args = function (rule_copy) {
        var bq_args = RuleEngine.base_query.args;
        for(var i = 0; i < bq_args.length; i++) {
            for(var j = 0; j < rule_copy.args.length; j++) {
                if(RuleEngine.is_term_in_alias_chain(rule_copy.args[j], bq_args[i])) {
                    return true;
                }
            }
        }
        return false;
    };
    RuleEngine.prototype.prepareToFire = function (query, rule, is_body_rule) {
        if(is_debug) {
            console.log("_dbg in prepareToFire");
            var rule_check = query;
            if(!this.check_copy_args_link_to_base_args(rule_check)) {
                console.log("_dbg 4 rule: " + rule_check.name + " args not found in alias chain of base query args!");
            } else {
                console.log("_dbg 4 rule: " + rule_check.name + " args found in alias chain of base query args!");
            }
        }
        var rule_copy = rule.deepcopy();
        if(rule_copy.rules.length > 0) {
            this.unifyHeaderArgsToBodyCallArgs(rule_copy);
        }
        if(is_debug) {
            if(query.name == 'choose_hd') {
                console.log("_dbg choose_hd call: ");
                RuleEngine.dump_rule(query);
            }
        }
        if(!is_body_rule) {
            this.unifyRuleHeaders(query, rule_copy);
        } else {
            RuleEngine.rule_firing.is_context_change = true;
            if(is_debug) {
                console.log("_dbg adding RuleEngine.rule_firing.is_context_change = true: " + RuleEngine.rule_firing.name);
            }
            this.addBodyRule(RuleEngine.rule_firing);
            this.addHeaderBodyArgs(query);
            this.unifyHeaderBodyArgsToQueryArgs(query);
            this.unifyRuleHeaders(query, rule_copy);
        }
        if(is_debug) {
            if(query.name == 'choose_hd') {
                console.log("_dbg choose_hd call after unifyRuleHeaders: ");
                RuleEngine.dump_rule(rule_copy);
            }
        }
        if(is_debug) {
            if(!this.check_copy_args_link_to_base_args(rule_copy)) {
                console.log("_dbg 3 rule_copy: " + rule_copy.name + " args not found in alias chain of base query args after unifying with query: " + query.name);
            } else {
                console.log("_dbg 3 rule_copy: " + rule_copy.name + " args found in alias chain of base query args after unifying with query: " + query.name);
            }
        }
        if(is_debug) {
            if(query.name == 'choose_hd') {
                console.log("_dbg choose_hd rule after unifying header and body args: ");
                RuleEngine.dump_rule(rule_copy);
            }
        }
        return rule_copy;
    };
    RuleEngine.prototype.popBodyRule = function () {
        if(!RuleEngine.async_hold) {
            if(is_debug) {
                console.log("_dbg about to pop a body rule");
                console.log("_dbg RuleEngine.body_rules.length: " + RuleEngine.body_rules.length);
            }
            var popped_body_rule = RuleEngine.body_rules.pop();
            if(popped_body_rule && popped_body_rule.is_context_change) {
                if(is_debug) {
                    console.log("_dbg context change to RuleEngine.rule_firing: " + popped_body_rule.name);
                }
                RuleEngine.rule_firing = popped_body_rule;
                this.popBodyRule();
            } else {
                RuleEngine.body_rule_firing = popped_body_rule;
            }
        }
    };
    RuleEngine.prototype.handleAsyncInput = function (input_str) {
        if(is_debug) {
            console.log("_dbg in handleAsyncInput");
        }
        RuleEngine.async_hold = false;
        var re = RuleEngine.getREInst();
        var header = RuleEngine.rule_firing;
        var bodyRule = RuleEngine.body_rule_firing;
        if(bodyRule) {
            if(is_debug) {
                console.log("_dbg bodyRule is defined");
            }
        } else {
            if(is_debug) {
                console.log("_dbg bodyRule is not defined");
            }
        }
        if(is_debug) {
            console.log("_dbg bodyRule.name: " + bodyRule.name);
        }
        if(bodyRule.name.indexOf('i(') > -1) {
            var r = /^i\((.*)\)/;
            var arg_name = bodyRule.name.match(r)[1];
        }
        if(bodyRule.name.indexOf('iradiobtn(') > -1) {
            var r = /^iradiobtn\((.*)\)/;
            var radio_exp = bodyRule.name.match(r)[1];
            var arg_name = radio_exp.substr(0, radio_exp.indexOf(','));
        }
        console.log("\n");
        var arg = RuleEngine.findArg(arg_name, header.args);
        if(!arg) {
            arg = RuleEngine.findArg(arg_name, header.b_args);
        }
        if(!arg) {
            if(is_debug) {
                console.log("_dbg Term variable with name: " + arg_name + " not found, creating and adding to body args of header rule name:" + header.name);
            }
            arg = new Term(arg_name);
            header.addBarg(arg);
        }
        if(is_debug) {
            console.log("_dbg unifying value: " + input_str + " with arg name: " + arg.name);
        }
        arg.unify(input_str);
        if(is_debug) {
            console.log("_dbg num header.b_args: " + header.b_args.length);
        }
        re.handleNonCallAsync(false);
    };
    RuleEngine.prototype.addHeaderBodyArgs = function (call_rule) {
        if(is_debug) {
            console.log("_dbg in addHeaderBodyArgs");
        }
        var header = RuleEngine.rule_firing;
        for(var i = 0; i < call_rule.args.length; i++) {
            var arg = RuleEngine.findArg(call_rule.args[i].name, header.args);
            if(!arg) {
                if(is_debug) {
                }
                arg = RuleEngine.findArg(call_rule.args[i].name, header.b_args);
            }
            if(!arg) {
                if(is_debug) {
                    console.log("_dbg adding term to header body args: " + call_rule.args[i].name);
                }
                arg = new Term(call_rule.args[i].name);
                header.addBarg(arg);
            }
        }
    };
    RuleEngine.prototype.handleNonCallBodyRule = function (header, bodyRule) {
        if(is_debug) {
            console.log("_dbg in handleNonCallBodyRule\n");
            console.log("_dbg header.name: " + header.name);
        }
        var is_fail = false;
        if(bodyRule.name.indexOf('==') > -1) {
            var n = bodyRule.name.split('==');
            if(is_debug) {
                console.log("_dbg n: " + JSON.stringify(n) + "\n");
                console.log("_dbg num header args: " + header.args.length + "\n");
                console.log("_dbg num header b_args: " + header.b_args.length + "\n");
            }
            var arg = RuleEngine.findArg(n[0], header.args);
            if(!arg) {
                if(is_debug) {
                    console.log("_dbg arg name: " + n[0] + " not found in header.args");
                    console.log("_dbg header.b_args: " + JSON.stringify(header.b_args) + "\n");
                }
                arg = RuleEngine.findArg(n[0], header.b_args);
            }
            if(is_debug) {
                if(arg) {
                    console.log("_dbg arg name: " + arg.name + "\n");
                    console.log("_dbg == arg.getGrounded(): " + arg.getGrounded() + "\n");
                } else {
                    console.log("_dbg arg name: " + n[0] + " not found in header.b_args:" + JSON.stringify(header.b_args) + "\n");
                }
            }
            if(arg.getGrounded() != n[1]) {
                is_fail = true;
            }
        } else {
            if(bodyRule.name.indexOf('=') > -1) {
                var n = bodyRule.name.split('=');
                var arg = RuleEngine.findArg(n[0], header.args);
                if(!arg) {
                    arg = RuleEngine.findArg(n[0], header.b_args);
                }
                if(!arg) {
                    if(is_debug) {
                        console.log("_dbg Term variable with name: " + n[0] + " not found, creating and adding to body args of header rule");
                    }
                    arg = new Term(n[0]);
                    header.addBarg(arg);
                }
                if(is_debug) {
                    console.log("_dbg about to unity value: " + n[1] + " with arg name: " + arg.name + "\n");
                }
                arg.unify(n[1]);
                if(is_debug) {
                    RuleEngine.dump_term_alias_chain(arg);
                    var bq_hd_arg = RuleEngine.base_query.args[0];
                    RuleEngine.dump_term_alias_chain(bq_hd_arg);
                }
            } else {
                if(bodyRule.name == '!') {
                    console.log("_dbg executing cut, emptying choice points");
                    RuleEngine.choices = [];
                } else {
                    if(bodyRule.name.indexOf('fail') > -1) {
                        if(is_debug) {
                            console.log("_dbg executing fail");
                        }
                        is_fail = true;
                    } else {
                        if(bodyRule.name.indexOf('write(') > -1) {
                            var r = /^write\((.*)\)/;
                            Util.output(bodyRule.name.match(r)[1]);
                        } else {
                            if(bodyRule.name.indexOf('o(') > -1) {
                                var r = /^o\((.*)\)/;
                                Util.output(bodyRule.name.match(r)[1]);
                            } else {
                                if(bodyRule.name.indexOf('i(') > -1) {
                                    var r = /^i\((.*)\)/;
                                    var arg_name = bodyRule.name.match(r)[1];
                                    if(is_debug) {
                                        console.log("_dbg input into varible: " + arg_name);
                                    }
                                    Util.input(this.handleAsyncInput);
                                } else {
                                    if(bodyRule.name.indexOf('iradiobtn(') > -1) {
                                        var r = /^iradiobtn\((.*)\)/;
                                        var radio_exp = bodyRule.name.match(r)[1];
                                        var arg_name = radio_exp.substr(0, radio_exp.indexOf(','));
                                        var radio_info = radio_exp.substr(radio_exp.indexOf(',') + 1, radio_exp.length);
                                        if(is_debug) {
                                            console.log("_dbg input radio button into variable: " + arg_name);
                                            console.log("_dbg input radio button info: " + radio_info);
                                        }
                                        var radio_data = JSON.parse(radio_info);
                                        Util.inputRadio(this.handleAsyncInput, radio_data);
                                    } else {
                                        if(bodyRule.name.indexOf('ov(') > -1) {
                                            if(is_debug) {
                                                console.log("_dbg in ov body rule");
                                            }
                                            var r = /^ov\((.*)\)/;
                                            var arg_name = bodyRule.name.match(r)[1];
                                            if(is_debug) {
                                                console.log("_dbg arg_name: " + arg_name);
                                            }
                                            var arg = RuleEngine.findArg(arg_name, header.args);
                                            if(!arg) {
                                                arg = RuleEngine.findArg(arg_name, header.b_args);
                                            }
                                            Util.output(arg.getGrounded());
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
        if(is_debug) {
            console.log("_dbg about to call handleNonCallAsync");
            console.log("_dbg is_fail: " + is_fail);
        }
        this.handleNonCallAsync(is_fail);
    };
    RuleEngine.prototype.prepareToCall = function (call_rule, is_body_rule) {
        if (typeof is_body_rule === "undefined") { is_body_rule = false; }
        if(is_debug) {
            console.log("_dbg processing rule name: " + call_rule.name + " in prepareToCall");
            RuleEngine.dump_rule(call_rule);
            console.log("_dbg   RuleEngine.rule_firing.name: " + RuleEngine.rule_firing.name);
        }
        this.addHeaderBodyArgs(call_rule);
        var foundRules = this.searchRules(RuleEngine.rules, call_rule.name, call_rule.args);
        if(is_debug) {
            console.log("_dbg num rules found: " + foundRules.length + "\n");
        }
        this.handleFoundRules(call_rule, foundRules);
        if(foundRules.length) {
            if(is_debug) {
                console.log("_dbg foundRules[0].rules.length: " + foundRules[0].rules.length);
            }
            var rule_copy = this.prepareToFire(call_rule, foundRules[0], is_body_rule);
            if(is_debug) {
                console.log("_dbg copy of rule name: " + call_rule.name);
                RuleEngine.dump_rule(rule_copy);
            }
            return rule_copy;
        }
    };
    RuleEngine.prototype.handleNonCallAsync = function (is_fail) {
        if(is_debug) {
            console.log("_dbg in handleNonCallAsync");
            console.log("_dbg RuleEngine.async_hold: " + RuleEngine.async_hold);
            if(RuleEngine.rule_firing.b_args.length > 0) {
                console.log("RuleEngine.rule_firing.b_args[0].getGrounded(): " + RuleEngine.rule_firing.b_args[0].getGrounded());
            }
        }
        if(RuleEngine.async_hold) {
            return;
        }
        if(!RuleEngine.async_hold) {
            if(is_debug) {
                console.log("_dbg RuleEngine.async_hold is false");
            }
            if(is_fail) {
                if(is_debug) {
                    console.log("_dbg fail detected in bodyRules execution, backtracking...");
                }
                this.backTrack();
            }
            this.popBodyRule();
            if(is_debug) {
                console.log("_dbg about to call handleBodyRule");
            }
            this.handleBodyRule();
            this.handleBaseQueryFinish();
        }
    };
    RuleEngine.prototype.handleBodyRule = function () {
        if(is_debug) {
            console.log("_dbg in handleBodyRule");
        }
        if(RuleEngine.async_hold) {
            if(is_debug) {
                console.log("_dbg async_hold is true not executing body rule");
            }
            return;
        }
        var header = RuleEngine.rule_firing;
        var bodyRule = RuleEngine.body_rule_firing;
        if(!bodyRule) {
            return;
        }
        if(!bodyRule.is_non_call()) {
            if(is_debug) {
                console.log("_dbg bodyRule name: " + bodyRule.name + " is not non_call");
            }
            var is_body_rule = true;
            var rule_copy = this.prepareToCall(bodyRule, is_body_rule);
            this.fireRule(rule_copy);
        } else {
            if(is_debug) {
                console.log("_dbg processing non call rule name: " + bodyRule.name);
            }
            this.handleNonCallBodyRule(header, bodyRule);
        }
    };
    RuleEngine.prototype.fireRule = function (r) {
        RuleEngine.rule_firing = r;
        if(is_debug) {
            console.log("_dbg firing rule: " + r.name + "\n");
            console.log("_dbg r.rules.length: " + r.rules.length);
            console.log("_dbg r.args[0]: " + RuleEngine.getTypeName(r.args[0]));
        }
        if(r.is_query()) {
            if(is_debug) {
                console.log("_dbg executing query rule: " + r.name + "\n");
            }
            var rule_copy = this.prepareToCall(r);
            if(is_debug) {
                console.log("_dbg about to fire rule_copy with name: " + rule_copy.name);
                console.log("_dbg  rule_copy.rules.length: " + rule_copy.rules.length);
            }
            this.fireRule(rule_copy);
            if(RuleEngine.async_hold) {
                return;
            }
        } else {
            if(is_debug) {
                console.log("_dbg rule: " + r.name + " is not a query\n");
            }
        }
        for(var l in r.rules) {
            var bodyRule = r.rules[l];
            this.addBodyRule(bodyRule);
        }
        if(is_debug && r.rules.length) {
            console.log("_dbg # body rules of rule " + r.name + ": " + r.rules.length);
            console.log("_dbg added all body rules of rule: " + bodyRule.name);
            RuleEngine.dump_body_rules();
        }
        this.popBodyRule();
        if(is_debug) {
            console.log("_dbg current rule firing: " + RuleEngine.rule_firing.name);
            if(RuleEngine.body_rule_firing) {
                console.log("_dbg body rule processing after popBodyRule: " + RuleEngine.body_rule_firing.name);
            }
        }
        this.handleBodyRule();
        if(is_debug) {
            console.log("_dbg after handleBodyRule in fireRule");
        }
    };
    RuleEngine.is_term_in_alias_chain = function is_term_in_alias_chain(needle, haystack) {
        var done = false;
        var found = false;
        while(!done) {
            if(needle == haystack) {
                found = true;
                done = true;
            }
            if(RuleEngine.getTypeName(haystack.grounded) != 'Term' || haystack.isFree()) {
                done = true;
            } else {
                haystack = haystack.grounded;
            }
        }
        return found;
    }
    RuleEngine.dump_term_alias_chain = function dump_term_alias_chain(t) {
        return;
        if(RuleEngine.getTypeName(t.grounded) != 'Term') {
            console.log("_dbg t.name: " + t.name + " is bound to value" + t.grounded);
            return;
        }
        console.log("_dbg dumping alias chain:");
        console.log("_dbg t.name: " + t.name);
        if(t.isFree()) {
            console.log("_dbg t.name: " + t.name + " is free");
            return;
        }
        if(t.isBoundorAliased()) {
            if(RuleEngine.getTypeName(t.grounded) == 'Term') {
                console.log("_dbg t.name: " + t.name + " is aliased to " + t.grounded.name);
                RuleEngine.dump_term_alias_chain(t.grounded);
            } else {
                console.log("_dbg t.name: " + t.name + " is bound to value" + t.grounded);
            }
        }
    }
    RuleEngine.dump_rule = function dump_rule(r1) {
        console.log("_dbg rule name: " + r1.name);
        if(r1.args.length > 0) {
            for(var i = 0; i < r1.args.length; i++) {
                console.log("_dbg   args[" + i + "]: " + r1.args[i].name + " value: " + r1.args[i].getGrounded());
            }
        } else {
            console.log("_dbg   has no args");
        }
        if(r1.rules.length > 0) {
        } else {
            console.log("_dbg   has no body rules");
        }
    }
    RuleEngine.dump_body_rules = function dump_body_rules() {
        if(is_debug) {
            console.log("_dbg dumping body rules");
            for(var i = 0; i < RuleEngine.body_rules.length; i++) {
                var bodyRule = RuleEngine.body_rules[i];
                console.log("_dbg position: " + i + " bodyRule name: " + bodyRule.name + " is_context_change: " + bodyRule.is_context_change);
            }
        }
    }
    RuleEngine.dump_choices = function dump_choices() {
        if(is_debug) {
            console.log("_dbg dumping choices");
            for(var i = 0; i < RuleEngine.choices.length; i++) {
                var choice = RuleEngine.choices[i];
                if(RuleEngine.getTypeName(choice) == 'Choice') {
                    console.log("_dbg position: " + i + " query name: " + choice.query.name + " rule name: " + choice.rule.name + " type: " + RuleEngine.getTypeName(choice));
                    console.log("_dbg choice.body_rules: ");
                    for(var l = 0; l < choice.body_rules.length; l++) {
                        var bodyRule = choice.body_rules[l];
                        console.log("_dbg position: " + l + " bodyRule name: " + bodyRule.name + " is_context_change: " + bodyRule.is_context_change);
                    }
                } else {
                    console.log("_dbg position: " + i + " choice name: " + choice.name + " type: " + RuleEngine.getTypeName(choice));
                }
            }
        }
    }
    RuleEngine.prototype.isQuerySolved = function (r_args) {
        var is_solved = true;
        if(is_debug) {
            console.log("_dbg in isQuerySolved");
        }
        for(var i = 0; i < r_args.length; i++) {
            if(!r_args[i].isGrounded()) {
                if(is_debug) {
                    console.log("_dbg term: " + r_args[i].name + " is not grounded");
                }
                is_solved = false;
            } else {
                if(is_debug) {
                    console.log("_dbg term: " + r_args[i].name + " is grounded with value: " + r_args[i].getGrounded());
                }
            }
        }
        return is_solved;
    };
    RuleEngine.prototype.handleFindMoreSolutions = function (input_str) {
        if(is_debug) {
            console.log("_dbg in handleFireMoreSolutions");
        }
        RuleEngine.async_hold = false;
        if(input_str == 'yes') {
            var re = RuleEngine.getREInst();
            re.backTrack();
        } else {
            if(RuleEngine.finished_cb) {
                RuleEngine.finished_cb();
            }
        }
    };
    RuleEngine.prototype.handleQueryResult = function () {
        if(is_debug) {
            console.log("_dbg in handleQueryResult");
        }
        var q = RuleEngine.base_query;
        var output_ar = [];
        if(this.isQuerySolved(q.args)) {
           if(RuleEngine.gnd_val_only) {
             for(var i = 0; i < q.args.length; i++) {
               output_ar.push(q.args[i].getGrounded());
             }
           } else {
            output_ar.push('Query: ' + q.name + ' was solved, solutions are: \n');
            for(var i = 0; i < q.args.length; i++) {
                output_ar.push('arg name: ' + q.args[i].name + ' ground val: ' + q.args[i].getGrounded() + '\n');
            }
           }
        } else {
          if(RuleEngine.no_solutions_found_txt != '') {
              output_ar.push(RuleEngine.no_solutions_found_txt);
            } else {
              output_ar.push('Query: ' + q.name + ' was not solved\n');
            }  
        }
        if(RuleEngine.choices.length > 0) {
            if(is_debug) {
                console.log("_dbg RuleEngine.choices.length: " + RuleEngine.choices.length);
            }
            output_ar.push('There are other possible solutions, should the solver continue?\n');
            Util.output(output_ar.join(''));
            output_ar = [];
            RuleEngine.dump_choices();
            if(RuleEngine.more_solutions_prompt == true) {
                Util.input(this.handleFindMoreSolutions);
            }
        }
        if(output_ar.length > 0) {
            Util.output(output_ar.join(''));
        }
    };
    return RuleEngine;
})();