1 define(['require', "GrammarTreeObjects", "CONSTANT", 'Collections','ExpressionTreeHelper','EvaluationHelper'],
  2 function(require, TREEObjects, CONSTANT, Collection, expressionTreeHelper, evaluationHelper) {
  3 		/**
  4 	 * @description A Class contains all the unique rules can be applied combinely to any expressions, using the Rule Engine.
  5 	 * @name FormulaGrammarRule
  6 	 * @class FormulaGrammarRule
  7 	 * @example require('FormulaGrammarRule')
  8 	 * @exports FormulaGrammarRule
  9 	 * @version 1.0
 10 	 * @module FormulaGrammarRule
 11 	 * @augments TREEObjects
 12 	 **/
 13 	var FormulaGrammarRule = {
 14 		//TODO Implement the Rule Evaluation for Nexcel Runtime Expressions in Excel Expressions
 15 		/**
 16 		 * @name RULE_1
 17 		 * @memberOf FormulaGrammarRule
 18 		 * @method 
 19 		 * @return {Rule}
 20 		 * @description Once the rule executes, it ensures All Clause - All LHS Fact should have same number of Variables.
 21 		 * @function
 22 		 */
 23 		RULE_1 : function() {
 24 			var desc = "All Clause - All LHS Fact should have same number of Variables";
 25 			var rule = {
 26 				execute : function(programs) {
 27 					require('FormulaGrammarRule').UTIL.validateClauseFactVariableCount(programs);
 28 				},
 29 
 30 				desc : function() {
 31 					return desc;
 32 				}
 33 			};
 34 			return rule;
 35 		},
 36 		/**
 37 		 * @name RULE_2
 38 		 * @memberOf FormulaGrammarRule
 39 		 * @method 
 40 		 * @return {Rule}
 41 		 * @description Once the rule executes, it ensures In expression the body part should be available. 'group(A,B).' is a valid expression, but cant be calculated, so It should have a body..
 42 		 * @function
 43 		 */
 44 		RULE_2:function(){
 45 			var desc = "In expression the body part should be available. 'group(A,B).' is a valid expression, but cant be calculated, so It should have a body.";
 46 			var util = require('FormulaGrammarRule').UTIL;
 47 			var rule = {
 48 				execute : function(programs) {
 49 					for (var i = 0; i < programs.length; i++) {
 50 						if (programs[i].type() == ProgramType.EXCEL || programs[i].type() ==ProgramType.RUNTIME_TABLE)
 51 							continue;
 52 						var prog = programs[i].getProgram();
 53 						if(!prog.getBody()){
 54 							var fact = prog.getFact();
 55 							var factName = fact.getName();
 56 							throw programs[i]+" doesnt have a body part, please correct the expression of the form <br/>"+factName+"Copy("+fact.getVariables().join(',')+"):-<br/>     "+fact.toString()+".";
 57 						}
 58 					}
 59 				},
 60 
 61 				desc : function() {
 62 					return desc;
 63 				}
 64 			};
 65 			return rule;
 66 		},
 67 		/**
 68 		 * @name RULE_3
 69 		 * @memberOf FormulaGrammarRule
 70 		 * @method 
 71 		 * @return {Rule}
 72 		 * @description Once the rule executes, it ensures All Clause - Fact Variable should be available Either in the variables of Ranges or in the LHS of Constraint Equal.
 73 		 * @function
 74 		 */
 75 		RULE_3 : function() {
 76 			var desc = "All Clause - Fact Variable should be available Either in the variables of Ranges or in the LHS of Constraint Equal.";
 77 			var util = require('FormulaGrammarRule').UTIL;
 78 			var rule = {
 79 				execute : function(programs) {
 80 					for (var i = 0; i < programs.length; i++) {
 81 						if (programs[i].type() == ProgramType.EXCEL)
 82 							continue;
 83 						if(programs[i].type() ==ProgramType.RUNTIME_TABLE){
 84 							var runtimePrograms = programs[i].getProgram().getRTPrograms();
 85 							for(var j=0; j<runtimePrograms.length; j++)
 86 								util.validateClauseFactVariableAvailabilityInClauseBody(runtimePrograms[j].getProgram());
 87 						}else{
 88 							util.validateClauseFactVariableAvailabilityInClauseBody(programs[i].getProgram());
 89 						}
 90 					}
 91 				},
 92 
 93 				desc : function() {
 94 					return desc;
 95 				}
 96 			};
 97 			return rule;
 98 		},
 99 		
100 		
101 		/**
102 		 * @name RULE_4
103 		 * @memberOf FormulaGrammarRule
104 		 * @method 
105 		 * @return {Rule}
106 		 * @description Once the rule executes, it ensures Range - Variables Should not be Modified using Constr Equal Ex:   student(Id, Name, Age), Id =  Age + 10.
107 		 * @function
108 		 */
109 		RULE_4 : function() {
110 			var desc = "Range - Variables Should not be Modified using Constr Equal Ex:   student(Id, Name, Age), Id =  Age + 10.";
111 			var util = require('FormulaGrammarRule').UTIL;
112 			var rule = {
113 				execute : function(programs) {
114 					for (var i = 0; i < programs.length; i++) {
115 						if (programs[i].type() == ProgramType.EXCEL)
116 							continue;
117 						if(programs[i].type() ==ProgramType.RUNTIME_TABLE){
118 							var runtimePrograms = programs[i].getProgram().getRTPrograms();
119 							for(var j=0; j<runtimePrograms.length; j++)
120 								util.factVariableShouldNotBeTheLhsOfConstrEqual(runtimePrograms[j].getProgram());
121 						}else{
122 							util.factVariableShouldNotBeTheLhsOfConstrEqual(programs[i].getProgram());
123 						}
124 					}
125 				},
126 
127 				desc : function() {
128 					return desc;
129 				}
130 			};
131 			return rule;
132 		},
133 		
134 		/**
135 		 * @name RULE_5
136 		 * @memberOf FormulaGrammarRule
137 		 * @method 
138 		 * @return {Rule}
139 		 * @description Once the rule executes, it ensures Binary Relation - Variables Should be Avaialable either in Ranges Variable or in Constraint Equal Ex:    p(X) :- q(Y), r(Z), Y = X + Z.      It will be valid only if p(X) :- q(Y), r(Z), X = Y - Z.
140 		 * @function
141 		 */
142 		
143 		RULE_5 : function() {
144 			var desc = "Binary Relation - Variables Should be Avaialable either in Ranges Variable or in Constraint Equal Ex:    p(X) :- q(Y), r(Z), Y = X + Z.      It will be valid only if p(X) :- q(Y), r(Z), X = Y - Z.";
145 			var util = require('FormulaGrammarRule').UTIL;
146 			var rule = {
147 				execute : function(programs) {
148 					for (var i = 0; i < programs.length; i++) {
149 						if (programs[i].type() == ProgramType.EXCEL)
150 							continue;
151 						if(programs[i].type() ==ProgramType.RUNTIME_TABLE){
152 							var runtimePrograms = programs[i].getProgram().getRTPrograms();
153 							for(var j=0; j<runtimePrograms.length; j++)
154 								util.binRelVariableShouldAvailableInFactVariablesOrConstrEqLhs(runtimePrograms[j].getProgram());
155 						}else{
156 							util.binRelVariableShouldAvailableInFactVariablesOrConstrEqLhs(programs[i].getProgram());
157 						}
158 					}
159 				},
160 
161 				desc : function() {
162 					return desc;
163 				}
164 			};
165 			return rule;
166 		},
167 		/**
168 		 * @name RULE_6
169 		 * @memberOf FormulaGrammarRule
170 		 * @method 
171 		 * @return {Rule}
172 		 * @description Once the rule executes, it ensures In Recursive Expressions, RHS Should not have Dynamically Modifiable Variables - =indirectKM(F,T,Km) :- directKm(F,T,Km).indirectKM(F,T,Km) :-	indirectKM(F,K,Km1),directKm(K,T,Km2),	Km=Km1+Km2.
173 		 * @function
174 		 */
175 		RULE_6 : function() {
176 			var desc = "In Recursive Expressions, RHS Should not have Dynamically Modifiable Variables - =indirectKM(F,T,Km) :- directKm(F,T,Km).indirectKM(F,T,Km) :-	indirectKM(F,K,Km1),directKm(K,T,Km2),	Km=Km1+Km2.";
177 			var util = require('FormulaGrammarRule').UTIL;
178 			var rule = {
179 				execute : function(programs) {
180 					for (var i = 0; i < programs.length; i++) {
181 						if (programs[i].type() == ProgramType.EXCEL)
182 							continue;
183 						var program = programs[i].getProgram();
184 						if(programs[i].type() ==ProgramType.RUNTIME_TABLE){
185 							var runtimePrograms = program.getRTPrograms();
186 							for(var j=0; j<runtimePrograms.length; j++)
187 								this.validateProgram(runtimePrograms[j].getProgram());
188 						}else{
189 							this.validateProgram(program);
190 						}
191 					}
192 				},
193 				validateProgram:function(program){
194 					var bodyParts = expressionTreeHelper.getProgramBodyParts(program);
195 					var programClauseFact = program.getFact();
196 					var bodyFactList = bodyParts.get(ObjectType.FACT_LIST);
197 					var constrEqList = bodyParts.get(ObjectType.CONSTR_EQ_LIST);
198 					if (evaluationHelper.isRecursive(programClauseFact, bodyFactList)) {
199 						util.validateFactDontHaveDynamicModifiableVariables(programClauseFact, constrEqList);
200 					}
201 				},
202 				desc : function() {
203 					return desc;
204 				}
205 			};
206 			return rule;
207 		},
208 		/**
209 		 * @name RULE_7
210 		 * @memberOf FormulaGrammarRule
211 		 * @method 
212 		 * @return {Rule}
213 		 * @description Once the rule executes, it ensures In expression no variable named '_' allowed  and _ cant be used as a variable in ConstrRel.
214 		 * @function
215 		 */
216 		RULE_7:function(){
217 			var desc = "In expression no variable named '_' allowed  and _ cant be used as a variable in ConstrRel.";
218 			var util = require('FormulaGrammarRule').UTIL;
219 			var rule = {
220 				execute : function(programs) {
221 					for (var i = 0; i < programs.length; i++) {
222 						if (programs[i].type() == ProgramType.EXCEL)
223 							continue;
224 						var program = programs[i].getProgram();
225 						if(programs[i].type() ==ProgramType.RUNTIME_TABLE){
226 							var runtimePrograms = program.getRTPrograms();
227 							for(var j=0; j<runtimePrograms.length; j++)
228 								util.validateIsUnderscoreUsedAsAVariable(runtimePrograms[j].getProgram());
229 						}else
230 							util.validateIsUnderscoreUsedAsAVariable(program);
231 					}
232 				},
233 
234 				desc : function() {
235 					return desc;
236 				}
237 			};
238 			return rule;
239 		},
240 		//TODO All the negative range variables should be used in the positive range variables.
241 		/**
242 		 * @name RULE_8
243 		 * @memberOf FormulaGrammarRule
244 		 * @method 
245 		 * @return {Rule}
246 		 * @description Once the rule executes, it ensures In expression all the negative range variables should be used in the positive range variables.
247 		 * @function
248 		 */
249 		RULE_8:function(){
250 			var desc = "In expression all the negative range variables should be used in the positive range variables.";
251 			var util = require('FormulaGrammarRule').UTIL;
252 			var rule = {
253 				execute : function(programs) {
254 					for (var i = 0; i < programs.length; i++) {
255 						if (programs[i].type() == ProgramType.EXCEL)
256 							continue;
257 						var program = programs[i].getProgram();
258 						if(programs[i].type() ==ProgramType.RUNTIME_TABLE){
259 							var runtimePrograms = program.getRTPrograms();
260 							for(var j=0; j<runtimePrograms.length; j++)
261 								util.validateIsUnderscoreUsedAsAVariable(runtimePrograms[j].getProgram());
262 						}else
263 							util.validateIsAllNegativeNodeVariablesIsThereinPositiveNodes(program);
264 					}
265 				},
266 
267 				desc : function() {
268 					return desc;
269 				}
270 			};
271 			return rule;
272 		},
273 		/**
274 		 * @name RULE_9
275 		 * @memberOf FormulaGrammarRule
276 		 * @method 
277 		 * @return {Rule}
278 		 * @description Once the rule executes, it ensures All ConstrEq: Variable OpEQ ConstrOp - LHS Variable should not be used in the RHS.
279 		 * @function
280 		 */
281 		
282 		RULE_9 : function() {
283 			var desc = "All ConstrEq: Variable OpEQ ConstrOp - LHS Variable should not be used in the RHS.";
284 			var util = require('FormulaGrammarRule').UTIL;
285 			var rule = {
286 				execute : function(programs) {
287 					for (var i = 0; i < programs.length; i++) {
288 						if (programs[i].type() == ProgramType.EXCEL)
289 							continue;
290 						if(programs[i].type() ==ProgramType.RUNTIME_TABLE){
291 							var runtimePrograms = program.getRTPrograms();
292 							for(var j=0; j<runtimePrograms.length; j++)
293 								util.validateConstrEqVariableNotAvailableInRHS(runtimePrograms[j].getProgram());
294 						}else{
295 							util.validateConstrEqVariableNotAvailableInRHS(programs[i].getProgram());
296 						}
297 					}
298 				},
299 
300 				desc : function() {
301 					return desc;
302 				}
303 			};
304 			return rule;
305 		},
306 		
307 		/**
308 		 * Object types provided by Collections 
309 		 * @name UTIL
310 		 * @class UTIL
311 		 * @exports UTIL
312 		 * @version 1.0
313 		 * @module FormulaGrammarRule
314 		 * @memberOf FormulaGrammarRule
315 		 * @field
316 		 **/
317 		UTIL :  {
318 			/**
319 			 * @name validateClauseFactVariableCount
320 			 * @memberOf FormulaGrammarRule.UTIL
321 			 * @method 
322 			 * @return {Void}
323 			 * @param programs	The programs Object Array
324 			 * @description The method ensures All the program fact variable count should be same, To ensure all the facts projecting same number of datas to avoid data conflict.
325 			 * @function
326 			 * @exports FormulaGrammarRule.UTIL
327 			 */
328 
329 			validateClauseFactVariableCount : function(programs) {
330 				var prevFact = [];
331 				var prevVarsCount = 0;
332 				var nexcelPrograms =0;
333 				var progFactVarCount=[];
334 				for (var i = 0; i < programs.length; i++) {
335 					if (programs[i].type() == ProgramType.EXCEL)
336 						continue;
337 					var program = programs[i].getProgram();
338 					if(programs[i].type() ==ProgramType.RUNTIME_TABLE){
339 						var runtimePrograms = program.getRTPrograms();
340 						this.validateClauseFactVariableCount(runtimePrograms);
341 					}else{
342 						var fact = program.getFact();
343 						//CLAUSE - FACT
344 						var varsCount = fact.getVariables().length;
345 						if(!progFactVarCount[fact.getName()])
346 							progFactVarCount[fact.getName()] = fact.getVariables().length;
347 						else if(progFactVarCount[fact.getName()]!= fact.getVariables().length){
348 						//if (nexcelPrograms > 0 && prevVarsCount != varsCount) {
349 							throw "Invalid Number of Variable Found. 1." + fact.toString() + " \n\tAND \n2." + prevFact[fact.getName()].toString();
350 						}
351 						prevFact[fact.getName()] = fact;
352 						//prevVarsCount = varsCount;
353 						//nexcelPrograms++;
354 					}
355 				}
356 			},
357 			/**
358 			 * @name validateClauseFactVariableAvailabilityInClauseBody
359 			 * @memberOf FormulaGrammarRule.UTIL
360 			 * @method 
361 			 * @return {Void}
362 			 * @param programs	The programs Object Array
363 			 * @description The method ensures All the program fact variable are available in the Body of the expression, So that the formula can project the result as expected.
364 			 * @function
365 			 * @exports FormulaGrammarRule.UTIL
366 			 */
367 			validateClauseFactVariableAvailabilityInClauseBody : function(program) {
368 				var fact = program.getFact();
369 				//CLAUSE - FACT
370 				var body = program.getBody();
371 				// CLAUSE - BODY
372 				var factVariables = fact.getVariables();
373 				var Collection = require('Collections');
374 				var factVarSet = Collection.Set();
375 				factVarSet.addAll(factVariables);
376 				if(factVarSet.contains('_'))
377 					throw "'_' cant be used as a variable in the Fact :\n" + fact.toString();
378 				var bodyOutputVariables = this.getBodyOutputVariables(body);
379 				
380 				var factVars = Collection.Set();
381 				factVars.addAll(factVariables);
382 				var opVars = Collection.Set();
383 				opVars.addAll(bodyOutputVariables);
384 				console.log('Expected Variables :' + factVariables + ' \n Available Variables :' + bodyOutputVariables);
385 				if (!opVars.containsAll(factVariables))
386 					throw factVars.minus(opVars).getElements() + " Variables are missing in Clause \n" + program.toString();
387 			},
388 			/**
389 			 * @name validateConstrEqVariableNotAvailableInRHS
390 			 * @memberOf FormulaGrammarRule.UTIL
391 			 * @method 
392 			 * @return {Void}
393 			 * @param programs	The programs Object Array
394 			 * @description The function ensures that no ConstrEqual variable available in the RHS of the Expressions.
395 			 * @function
396 			 * @exports FormulaGrammarRule.UTIL
397 			 */
398 			validateConstrEqVariableNotAvailableInRHS : function(program) {
399 				var body = program.getBody();
400 				// CLAUSE - BODY
401 				for (var i = 0; i < body.length; i++) {
402 					if (body[i].type() == ObjectType.OPER_EQUAL) {
403 						var lhsVariable = body[i].variable();
404 						var constrOpVariables = new Collection.Set();
405 						var rhsVariables = expressionTreeHelper.getConstrOpVariables(body[i].getRhs(), constrOpVariables);
406 						if (rhsVariables.contains(lhsVariable))
407 							throw body[i] + " LHS Variable is Reffered in RHS, Which cause Ambiguity in Expression \n";
408 					}
409 				}
410 			},
411 			/**
412 			 * @name binRelVariableShouldAvailableInFactVariablesOrConstrEqLhs
413 			 * @memberOf FormulaGrammarRule.UTIL
414 			 * @method 
415 			 * @return {Void}
416 			 * @param programs	The programs Object Array
417 			 * @description The binary Relation variables must be weither available in the Ranges or in the ConstrEqual LHS.
418 			 * @function
419 			 * @exports FormulaGrammarRule.UTIL
420 			 */
421 			binRelVariableShouldAvailableInFactVariablesOrConstrEqLhs : function(program) {
422 				var Collection = require('Collections');
423 				var binRelVariables = Collection.Set();
424 				var bodyOutPutVariables = this.getBodyOutputVariables(program.getBody());
425 				var bodyParts = expressionTreeHelper.getProgramBodyParts(program);
426 				var binRelList = bodyParts.get(ObjectType.BIN_REL_LIST);
427 				for (var i = 0; i < binRelList.size(); i++) {
428 					binRelVariables = expressionTreeHelper.getBinRelVariables(binRelList.get(i), binRelVariables);
429 				}
430 				var bodyOutPutVariableSet = Collection.Set();
431 				bodyOutPutVariableSet.addAll(bodyOutPutVariables);
432 				if (!bodyOutPutVariableSet.containsAll(binRelVariables.getElements()))
433 					throw binRelVariables.minus(bodyOutPutVariableSet).getElements() + " Variable Not Found.";
434 			},
435 			/**
436 			 * @name getBodyOutputVariables
437 			 * @memberOf FormulaGrammarRule.UTIL
438 			 * @method 
439 			 * @return {Void}
440 			 * @param programs	The programs Object Array
441 			 * @description Returns teh Collection Set of the entire variable projected from FACT-RANGE and Constr-EQ
442 			 * @function
443 			 * @exports FormulaGrammarRule.UTIL
444 			 */
445 			getBodyOutputVariables : function(body) {
446 				var Collection = require('Collections');
447 				var bodyOutputVariables = Collection.Set();
448 				for (var i = 0; i < body.length; i++) {
449 					if (body[i].type() == ObjectType.FACT) {
450 						var fact = body[i];
451 						bodyOutputVariables.addAll(fact.getVariables());
452 						// Fact (variables)
453 					} else if (body[i].type() == ObjectType.OPER_EQUAL) {// BODY - CONSTR - EQUAL
454 						var constrEq = body[i];
455 						bodyOutputVariables.add(constrEq.variable());
456 						// ConstrEq LHS Variable.
457 					}
458 				}
459 				return bodyOutputVariables.getElements();
460 			},
461 			/**
462 			 * @name factVariableShouldNotBeTheLhsOfConstrEqual
463 			 * @memberOf FormulaGrammarRule.UTIL
464 			 * @method 
465 			 * @return {Void}
466 			 * @param programs	The programs Object Array
467 			 * @description Method to Validate teh Fact Variable Cannot be Modified, Because it is used as a Range Variable
468 			 * @function
469 			 * @exports FormulaGrammarRule.UTIL
470 			 */
471 			factVariableShouldNotBeTheLhsOfConstrEqual : function(program) {
472 				var Collection = require('Collections');
473 				var bodyParts = expressionTreeHelper.getProgramBodyParts(program);
474 				var factList = bodyParts.get(ObjectType.FACT_LIST);
475 				var constrEqList = bodyParts.get(ObjectType.CONSTR_EQ_LIST);
476 				var factVariables = Collection.Set();
477 				for (var i = 0; i < factList.size(); i++) {
478 					factVariables.addAll(factList.get(i).getVariables());
479 				}
480 				for (var i = 0; i < constrEqList.size(); i++) {
481 					if (factVariables.contains(constrEqList.get(i).variable()))
482 						throw constrEqList.get(i).variable() + " Variable Cannot be Modified, Because it is used as a Range Variable in " + factList.getElements().join(' or ');
483 				}
484 			},
485 			/**
486 			 * @name validateFactDontHaveDynamicModifiableVariables
487 			 * @memberOf FormulaGrammarRule.UTIL
488 			 * @method 
489 			 * @return {Void}
490 			 * @param programs	The programs Object Array
491 			 * @description It ensures that In recursion if any variable is getting calculated, It can lead to infinate recursion. To avoid this this function will throw error.
492 			 * @function
493 			 * @exports FormulaGrammarRule.UTIL
494 			 */
495 			validateFactDontHaveDynamicModifiableVariables : function(fact, constrEqList) {
496 				var Collection = require('Collections');
497 				var factVariables = Collection.Set();
498 				factVariables.addAll(fact.getVariables());
499 				var calculatedVariable = Collection.Set();
500 				for (var i = 0; i < constrEqList.size(); i++) {
501 					constr = constrEqList.get(i);
502 					var rhs = constr.getRhs();
503 					if (rhs.type() == ObjectType.BIN_OP) {
504 						calculatedVariable.add(constr.variable());
505 					}
506 				}
507 				var common = calculatedVariable.intersect(factVariables);
508 				if (common.size() > 0) {
509 					throw common.getElements() + " Variable are Calculated, And is ReUsed in Recursion, It can lead to an Infinite Loop";
510 				}
511 			},
512 			/**
513 			 * @name validateIsUnderscoreUsedAsAVariable
514 			 * @memberOf FormulaGrammarRule.UTIL
515 			 * @method 
516 			 * @return {Void}
517 			 * @param programs	The programs Object Array
518 			 * @description It ensures underscore variable is used only for dont care conditions.
519 			 * @function
520 			 * @exports FormulaGrammarRule.UTIL
521 			 */
522 			validateIsUnderscoreUsedAsAVariable : function(program) {
523 				var body = program.getBody();
524 				// CLAUSE - BODY
525 				for (var i = 0; i < body.length; i++) {
526 					if (body[i].type() == ObjectType.BIN_REL) {
527 						var constrOpVariables = new Collection.Set();
528 						expressionTreeHelper.getBinRelVariables(body[i], constrOpVariables);
529 						if (constrOpVariables.contains('_'))
530 							throw body[i].toString() + " Illegal usage of '_' in the Expression, '_' can be used as 'dont care column' in Expression\n";
531 					}
532 				}
533 			},
534 			/**
535 			 * @name validateIsAllNegativeNodeVariablesIsThereinPositiveNodes
536 			 * @memberOf FormulaGrammarRule.UTIL
537 			 * @method 
538 			 * @return {Void}
539 			 * @param programs	The programs Object Array
540 			 * @description It ensures all the negative node variables are already defined in the positive nodes.
541 			 * @function
542 			 * @exports FormulaGrammarRule.UTIL
543 			 */
544 			validateIsAllNegativeNodeVariablesIsThereinPositiveNodes : function(program) {
545 				var bodyParts = expressionTreeHelper.getProgramBodyParts(program);
546 				var factList = bodyParts.get(ObjectType.FACT_LIST);
547 				// CLAUSE - BODY
548 				var Collection = require('Collections');
549 				var negativeVarSet = new Collection.Set();
550 				var positiveVarSet = new Collection.Set();
551 				for (var i = 0; i < factList.size(); i++) {
552 					var fact = factList.get(i);
553 					if(fact.isNegative()){
554 						negativeVarSet.addAll(fact.getVariables());
555 					}else{
556 						positiveVarSet.addAll(fact.getVariables());
557 					}
558 				}
559 				var diffSet = negativeVarSet.minus(positiveVarSet);
560 				diffSet.remove('_');
561 				if (diffSet.size()>0)
562 					throw "Negative fact variables '"+diffSet.getElements().toString() + "' are not defined in positive facts. \n";
563 			},
564 		}
565 	};
566 	return FormulaGrammarRule;
567 });