/// <summary>
        /// function(obj) {
        ///    if (!_.isObject(obj)) return obj;
        ///    var source, prop;
        ///    for (var i = 1, length = arguments.length; i < length; i++) {
        ///      source = arguments[i];
        ///      for (prop in source) {
        ///        if (hasOwnProperty.call(source, prop)) {
        ///            obj[prop] = source[prop];
        ///        }
        ///      }
        ///    }
        ///    return obj;
        ///  };
        ///
        /// </summary>
        /// <returns></returns>
        private static PatternSpecialization UnderscoreExtendSpecialization() {
            var objParam = new ExpectedParameter(0);
            var sourceVar = new ExpectedVariableDeclaration();
            var propVar = new ExpectedVariableDeclaration();
            var iVar = new ExpectedVariableDeclaration(new ExpectedConstant(1.0));
            var arguments = new ExpectedLookup("arguments");
            var lengthVar = new ExpectedVariableDeclaration(new ExpectedMember(arguments, "length"));
            var retNode = new ExpectedNode(typeof(ReturnNode), objParam);

            return new PatternSpecialization(
                ExtendSpecializationImpl,
                false,
                new ExpectedNode(
                    typeof(IfNode),
                    new ExpectedUnary(
                        JSToken.LogicalNot,
                        new ExpectedCall(
                            new ExpectedMember(
                                new ExpectedLookup("_"),
                                "isObject"
                            ),
                            objParam
                        )
                    ),
                    new ExpectedNode(
                        typeof(Block),
                        retNode
                    )
                ),
                new ExpectedVar(
                    sourceVar,
                    propVar
                ),
                new ExpectedNode(
                    typeof(ForNode),
                    new ExpectedVar(
                        iVar,
                        lengthVar
                    ),
                    new ExpectedBinary(
                        JSToken.LessThan,
                        iVar.Variable,
                        lengthVar.Variable
                    ),
                    new ExpectedUnary(
                        JSToken.Increment,
                        iVar.Variable
                    ),
                    new ExpectedNode(
                        typeof(Block),
                        new ExpectedNode(
                            typeof(ExpressionStatement),
                            new ExpectedBinary(
                                JSToken.Assign,
                                sourceVar.Variable,
                                new ExpectedIndex(
                                    arguments,
                                    iVar.Variable
                                )
                            )
                        ),
                        new ExpectedNode(
                            typeof(ForIn),
                            new ExpectedNode(
                                typeof(ExpressionStatement),
                                propVar.Variable
                            ),
                            sourceVar.Variable,
                            new ExpectedNode(
                                typeof(Block),
                                new ExpectedNode(
                                    typeof(IfNode),
                                    new ExpectedCall(
                                        new ExpectedMember(
                                            new ExpectedLookup("hasOwnProperty"),
                                            "call"
                                        ),
                                        sourceVar.Variable,
                                        propVar.Variable
                                    ),
                                    new ExpectedNode(
                                        typeof(Block),
                                        new ExpectedNode(
                                            typeof(ExpressionStatement),
                                            new ExpectedBinary(
                                                JSToken.Assign,
                                                new ExpectedIndex(
                                                    objParam,
                                                    propVar.Variable
                                                ),
                                                new ExpectedIndex(
                                                    sourceVar.Variable,
                                                    propVar.Variable
                                                )
                                            )
                                        )
                                    )
                                )
                            )
                        )
                    )
                ),
                retNode
            );
        }
        /// <summary>
        /// var assign = function(object, source, guard) {
        ///      var index, iterable = object, result = iterable;
        ///      if (!iterable) return result;
        ///      var args = arguments,
        ///          argsIndex = 0,
        ///          argsLength = typeof guard == 'number' ? 2 : args.length;
        ///      if (argsLength > 3 && typeof args[argsLength - 2] == 'function') {
        ///        var callback = baseCreateCallback(args[--argsLength - 1], args[argsLength--], 2);
        ///      } else if (argsLength > 2 && typeof args[argsLength - 1] == 'function') {
        ///        callback = args[--argsLength];
        ///      }
        ///      while (++argsIndex < argsLength) {
        ///        iterable = args[argsIndex];
        ///        if (iterable && objectTypes[typeof iterable]) {
        ///          var ownIndex = -1,
        ///              ownProps = objectTypes[typeof iterable] && keys(iterable),
        ///              length = ownProps ? ownProps.length : 0;
        ///          
        ///          while (++ownIndex < length) {
        ///            index = ownProps[ownIndex];
        ///            result[index] = callback ? callback(result[index], iterable[index]) : iterable[index];
        ///          }
        ///        }
        ///      }
        ///      return result
        ///    };
        ///
        /// </summary>
        private static PatternSpecialization AssignSpecialization() {
            var object_ = new ExpectedParameter(0);
            var source = new ExpectedParameter(1);
            var guard = new ExpectedParameter(2);
            var index = new ExpectedVariableDeclaration();
            var iterable = new ExpectedVariableDeclaration(object_);
            var result = new ExpectedVariableDeclaration(iterable.Variable);
            var args = new ExpectedVariableDeclaration(new ExpectedLookup("arguments"));
            var argsIndex = new ExpectedVariableDeclaration(new ExpectedConstant(0.0));
            var argsLength = new ExpectedVariableDeclaration(AlwaysMatch.Instance);
            var callback = new ExpectedVariableDeclaration(AlwaysMatch.Instance);
            var objectTypes = new ExpectedLookup("objectTypes");
            var ownIndex = new ExpectedVariableDeclaration(new ExpectedUnary(JSToken.Minus, new ExpectedConstant(1.0)));
            var ownProps = new ExpectedVariableDeclaration(
                new ExpectedBinary(
                    JSToken.LogicalAnd,
                    new ExpectedIndex(
                        objectTypes,
                        new ExpectedUnary(JSToken.TypeOf, iterable.Variable)
                    ),
                    new ExpectedCall(
                        new ExpectedLookup("keys"),
                        iterable.Variable
                    )
                )
            );
            var length = new ExpectedVariableDeclaration(
                new ExpectedNode(
                    typeof(Conditional),
                    ownProps.Variable,
                    new ExpectedMember(ownProps.Variable, "length"),
                    new ExpectedConstant(0.0)
                )
            );

            return new PatternSpecialization(
                ExtendSpecializationImpl,
                new ExpectedVar(index, iterable, result),
                new ExpectedNode(
                    typeof(IfNode),
                    new ExpectedUnary(
                        JSToken.LogicalNot,
                        iterable.Variable
                    ),
                    new ExpectedNode(
                        typeof(Block),
                        new ExpectedNode(
                            typeof(ReturnNode),
                            result.Variable
                        )
                    )
                ),
                new ExpectedVar(args, argsIndex, argsLength),
                new ExpectedNode(
                    typeof(IfNode),
                    new ExpectedBinary(
                        JSToken.LogicalAnd,
                        new ExpectedBinary(
                            JSToken.GreaterThan,
                            argsLength.Variable,
                            new ExpectedConstant(3.0)
                        ),
                        new ExpectedBinary(
                            JSToken.Equal,
                            AlwaysMatch.Instance,
                            new ExpectedConstant("function")
                        )
                    ),
                    ExpectedBlock(callback),
                    ExpectedBlock(
                        new ExpectedNode(
                            typeof(IfNode),
                            new ExpectedBinary(
                                JSToken.LogicalAnd,
                                new ExpectedBinary(
                                    JSToken.GreaterThan,
                                    argsLength.Variable,
                                    new ExpectedConstant(2.0)
                                ),
                                new ExpectedBinary(
                                    JSToken.Equal,
                                    AlwaysMatch.Instance,
                                    new ExpectedConstant("function")
                                )
                            ),
                            ExpectedBlock(
                                new ExpectedNode(
                                    typeof(ExpressionStatement),
                                    new ExpectedBinary(
                                        JSToken.Assign,
                                        callback.Variable,
                                        AlwaysMatch.Instance
                                    )
                                )
                            )
                        )
                    )
                ),
                new ExpectedNode(
                    typeof(WhileNode),
                    new ExpectedBinary(
                        JSToken.LessThan,
                        new ExpectedUnary(
                            JSToken.Increment,
                            argsIndex.Variable
                        ),
                        argsLength.Variable
                    ),
                    ExpectedBlock(
                        ExpectedExprStmt(
                            ExpectedAssign(
                                iterable.Variable, 
                                new ExpectedIndex(args.Variable, argsIndex.Variable)
                            )
                        ),
                        new ExpectedNode(
                            typeof(IfNode),
                            new ExpectedBinary(
                                JSToken.LogicalAnd,
                                iterable.Variable,
                                new ExpectedIndex(
                                    objectTypes, 
                                    new ExpectedUnary(JSToken.TypeOf, iterable.Variable)
                                )
                            ),
                            ExpectedBlock(
                                new ExpectedVar(ownIndex, ownProps, length),
                                new ExpectedNode(
                                    typeof(WhileNode),
                                    new ExpectedBinary(
                                        JSToken.LessThan,
                                        new ExpectedUnary(
                                            JSToken.Increment,
                                            ownIndex.Variable
                                        ),
                                        length.Variable
                                    ),
                                    ExpectedBlock(
                                        ExpectedExprStmt(
                                            ExpectedAssign(
                                                index.Variable,
                                                new ExpectedIndex(
                                                    ownProps.Variable,
                                                    ownIndex.Variable
                                                )
                                            )
                                        ),
                                        ExpectedExprStmt(
                                            ExpectedAssign(
                                                new ExpectedIndex(result.Variable, index.Variable),
                                                new ExpectedNode(
                                                    typeof(Conditional),
                                                    callback.Variable,
                                                    AlwaysMatch.Instance,
                                                    new ExpectedIndex(iterable.Variable, index.Variable)
                                                )
                                            )
                                        )
                                    )
                                )
                            )
                        )
                    )
                ),
                new ExpectedNode(
                    typeof(ReturnNode),
                    result.Variable
                )
            );
        }
        /// <summary>
        /// function extend(protoProps, staticProps) {
        ///   var parent = this;
        ///   var child;
        ///   
        ///   if (protoProps && _.has(protoProps, 'constructor')) {
        ///     child = protoProps.constructor;
        ///   } else {
        ///     child = function(){ return parent.apply(this, arguments); };
        ///   }
        ///
        ///   _.extend(child, parent, staticProps);
        ///   
        ///   var Surrogate = function(){ this.constructor = child; };
        ///   Surrogate.prototype = parent.prototype;
        ///   child.prototype = new Surrogate;
        ///
        ///   if (protoProps) _.extend(child.prototype, protoProps);
        ///
        ///   child.__super__ = parent.prototype;
        ///
        ///   return child;
        /// };
        /// </summary>
        /// <returns></returns>
        private static PatternSpecialization BackboneExtendSpecialization() {
            var protoProps = new ExpectedParameter(0);
            var staticProps = new ExpectedParameter(1);
            var parentVar = new ExpectedVariableDeclaration(new ExpectedNode(typeof(ThisLiteral)));
            var childVar = new ExpectedVariableDeclaration();
            var underscore = new ExpectedLookup("_");
            var thisNode = new ExpectedNode(typeof(ThisLiteral));
            var surrogate = new ExpectedVariableDeclaration(
                new ExpectedFunctionExpr(
                    new ExpectedNode(
                        typeof(Block),
                        new ExpectedNode(
                            typeof(ExpressionStatement),
                            new ExpectedBinary(
                                JSToken.Assign,
                                new ExpectedMember(
                                    thisNode,
                                    "constructor"
                                ),
                                childVar.Variable
                            )
                        )
                    )
                )
            );

            return new PatternSpecialization(
                BackboneExtendSpecializationImpl,
                false,
                parentVar,
                childVar,
                new ExpectedNode(
                    typeof(IfNode),
                    new ExpectedBinary(
                        JSToken.LogicalAnd,
                        protoProps,
                        new ExpectedCall(
                            new ExpectedMember(underscore, "has"),
                            protoProps,
                            new ExpectedConstant("constructor")
                        )
                    ),
                    new ExpectedNode(
                        typeof(Block),
                        new ExpectedNode(
                            typeof(ExpressionStatement),
                            new ExpectedBinary(
                                JSToken.Assign,
                                childVar.Variable,
                                new ExpectedMember(
                                    protoProps,
                                    "constructor"
                                )
                            )
                        )
                    ),
                    new ExpectedNode(
                        typeof(Block),
                        new ExpectedNode(
                            typeof(ExpressionStatement),
                            new ExpectedBinary(
                                JSToken.Assign,
                                childVar.Variable,
                                new ExpectedFunctionExpr(
                                    new ExpectedNode(
                                        typeof(Block),
                                        new ExpectedNode(
                                            typeof(ReturnNode),
                                            new ExpectedCall(
                                                new ExpectedMember(parentVar.Variable, "apply"),
                                                thisNode,
                                                new ExpectedLookup("arguments")
                                            )
                                        )
                                    )
                                )
                            )
                        )
                    )
                ),
                new ExpectedNode(
                    typeof(ExpressionStatement),
                    new ExpectedCall(
                        new ExpectedMember(underscore, "extend"),
                        childVar.Variable,
                        parentVar.Variable,
                        staticProps
                    )
                ),
                surrogate,
                new ExpectedNode(
                    typeof(ExpressionStatement),
                    new ExpectedBinary(
                        JSToken.Assign,
                        new ExpectedMember(surrogate.Variable, "prototype"),
                        new ExpectedMember(parentVar.Variable, "prototype")
                    )
                ),
                new ExpectedNode(
                    typeof(ExpressionStatement),
                    new ExpectedBinary(
                        JSToken.Assign,
                        new ExpectedMember(childVar.Variable, "prototype"),
                        new ExpectedNew(surrogate.Variable)
                    )
                ),
                new ExpectedNode(
                    typeof(IfNode),
                    protoProps,
                    new ExpectedNode(
                        typeof(Block),
                        new ExpectedNode(
                            typeof(ExpressionStatement),
                            new ExpectedCall(
                                new ExpectedMember(underscore, "extend"),
                                new ExpectedMember(childVar.Variable, "prototype"),
                                protoProps
                            )
                        )
                    )
                ),
                new ExpectedNode(
                    typeof(ExpressionStatement),
                    new ExpectedBinary(
                        JSToken.Assign,
                        new ExpectedMember(childVar.Variable, "__super__"),
                        new ExpectedMember(parentVar.Variable, "prototype")
                    )
                ),
                new ExpectedNode(
                    typeof(ReturnNode),
                    childVar.Variable
                )
            );
        }