<!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 do it yourself biology needs, please email diybio@googlegroups.com for more assistance, thank you.';
    


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

  <body>
  
    <header>
      <h1>DIYBIO 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 diybio helper tool.<br /> Find answers to your diybio questions in seconds.</b>
          <br /><br />
            <form name="control_form" method="POST" onSubmit="return false;">
              <input type="submit" value="Start DIYBIO Helper" 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(DIYBIO_HELP):- write('Do you need help with do it yourself biology?'), i(IS_NEED_HELP), \
                      handle_need_help(DIYBIO_HELP, IS_NEED_HELP).\
    handle_need_help(DIYBIO_HELP, IS_NEED_HELP):- IS_NEED_HELP == 'yes', write('What area of biology do you need help with?'), \
                      iradiobtn(BIO_AREA, '\{\"Lab Wetware\":\"wetlab\", \"Programming\":\"programming\"\, \"Simulation\":\"simulation\"\}'), \
                      handle_bio_area_choice(DIYBIO_HELP, BIO_AREA).\
    handle_bio_area_choice(DIYBIO_HELP, BIO_AREA):- BIO_AREA == 'wetlab', write('What area in the wet lab do you need help with?'), \
                      iradiobtn(WET_AREA, '\{\"DNA cloning\":\"dna_clone\", \"Expressing a gene in a cell\":\"gene_express\"\}'), \
                      handle_web_area_choice(DIYBIO_HELP, WET_AREA).\
    handle_web_area_choice(DIYBIO_HELP, WET_AREA):- WET_AREA == 'dna_clone', DIYBIO_HELP='Use pcr to duplicate DNA', !. \
    handle_web_area_choice(DIYBIO_HELP, WET_AREA):- WET_AREA == 'gene_express', DIYBIO_HELP='Create a gene promotor, put it into the petri dish, then use an antibiotic to kill all cells that did not accept the gene promotor, then incubate cells to get protein that is being expressed in cells'.\
    handle_web_area_choice(DIYBIO_HELP, WET_AREA):- WET_AREA == 'gene_express', DIYBIO_HELP='Use www blueheronbio com, give them the desired protein, and they will mail you the cell expressing the gene to create the desired protein', !.\
    ";
  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;
})();