text: /*jshint unused:false*/
  /*
      Utilities for Alasql.js
  
      @todo Review the list of utilities
      @todo Find more effective utilities
  */
  
  /**
      Alasql utility functions
      @type {object}
   */
  var utils = (alasql.utils = {});
  
  /**
      Convert NaN to undefined
      @function
      @param {string} s JavaScript string to be modified
      @return {string} Covered expression
  
      @example
  
      123         => 123
      undefined   => undefined
      NaN         => undefined
  
  */
  function n2u(s) {
  	return '(y=' + s + ',y===y?y:undefined)';
  }
  
  /**
      Return undefined if s undefined
      @param {string} s JavaScript string to be modified
      @return {string} Covered expression
  
      @example
  
      123,a       => a
      undefined,a => undefined
      NaN,a       => undefined
  
  */
  function und(s, r) {
  	return '(y=' + s + ',typeof y=="undefined"?undefined:' + r + ')';
  }
  
  /**
      Return always true. Stub for non-ecisting WHERE clause, because is faster then if(whenrfn) whenfn()
      @function
      @return {boolean} Always true
  */
  function returnTrue() {
  	return true;
  }
  
  /**
      Return undefined. Stub for non-ecisting WHERE clause, because is faster then if(whenrfn) whenfn()
      @function
      @return {undefined} Always undefined
  */
  function returnUndefined() {}
  
  /**
      Escape string
      @function
      @param {string} s Source string
      @return {string} Escaped string
      @example
  
      Pit\er's => Pit\\er\'s
  
  */
  // based on joliss/js-string-escape
  var escapeq = (utils.escapeq = function(s) {
  	//    console.log(s);
  	return ('' + s).replace(/["'\\\n\r\u2028\u2029]/g, function(character) {
  		// Escape all characters not included in SingleStringCharacters and
  		// DoubleStringCharacters on
  		// http://www.ecma-international.org/ecma-262/5.1/#sec-7.8.4
  		switch (character) {
  			case '"':
  			case "'":
  			case '\\':
  				return '\\' + character;
  			// Four possible LineTerminator characters need to be escaped:
  			case '\n':
  				return '\\n';
  			case '\r':
  				return '\\r';
  			case '\u2028':
  				return '\\u2028';
  			case '\u2029':
  				return '\\u2029';
  		}
  	});
  });
  
  /**
      Double quotes for SQL statements
      @param {string} s Source string
      @return {string} Escaped string
  
      @example
  
      Piter's => Piter''s
  
   */
  var escapeqq = (utils.undoubleq = function(s) {
  	return s.replace(/(\')/g, "''");
  });
  
  /**
      Replace double quotes with single quote
      @param {string} s Source string
      @return {string} Replaced string
      @example
  
      Piter''s => Piter's
  
   */
  var doubleq = (utils.doubleq = function(s) {
  	return s.replace(/(\'\')/g, "\\'");
  });
  
  /**
      Replace sigle quote to escaped single quote
      @param {string} s Source string
      @return {string} Replaced string
  
      @todo Chack this functions
  
  */
  var doubleqq = (utils.doubleqq = function(s) {
  	return s.replace(/\'/g, "'");
  });
  
  /**
      Cut BOM first character for UTF-8 files (for merging two files)
      @param {string} s Source string
      @return {string} Replaced string
  */
  
  var cutbom = function(s) {
  	if (s[0] === String.fromCharCode(65279)) {
  		s = s.substr(1);
  	}
  	return s;
  };
  
  /**
      Get the global scope
      Inspired by System.global
      @return {object} The global scope
  */
  utils.global = (function() {
  	if (typeof self !== 'undefined') {
  		return self;
  	}
  	if (typeof window !== 'undefined') {
  		return window;
  	}
  	if (typeof global !== 'undefined') {
  		return global;
  	}
  	return Function('return this')();
  })();
  
  /**
      Find out if a function is native to the enviroment
      @param {function} Function to check
      @return {boolean} True if function is native
  */
  var isNativeFunction = (utils.isNativeFunction = function(fn) {
  	return typeof fn === 'function' && !!~fn.toString().indexOf('[native code]');
  });
  
  /**
      Find out if code is running in a web worker enviroment
      @return {boolean} True if code is running in a web worker enviroment
  */
  utils.isWebWorker = (function() {
  	try {
  		var importScripts = utils.global.importScripts;
  		return utils.isNativeFunction(importScripts);
  	} catch (e) {
  		return false;
  	}
  })();
  
  /**
      Find out if code is running in a node enviroment
      @return {boolean} True if code is running in a node enviroment
  */
  utils.isNode = (function() {
  	try {
  		return utils.isNativeFunction(utils.global.process.reallyExit);
  	} catch (e) {
  		return false;
  	}
  })();
  
  /**
      Find out if code is running in a browser enviroment
      @return {boolean} True if code is running in a browser enviroment
  */
  utils.isBrowser = (function() {
  	try {
  		return utils.isNativeFunction(utils.global.location.reload);
  	} catch (e) {
  		return false;
  	}
  })();
  
  /**
      Find out if code is running in a browser with a browserify setup
      @return {boolean} True if code is running in a browser with a browserify setup
  */
  utils.isBrowserify = (function() {
  	return utils.isBrowser && typeof process !== 'undefined' && process.browser;
  })();
  
  /**
      Find out if code is running in a browser with a requireJS setup
      @return {boolean} True if code is running in a browser with a requireJS setup
  */
  utils.isRequireJS = (function() {
  	return (
  		utils.isBrowser && typeof require === 'function' && typeof require.specified === 'function'
  	);
  })();
  
  /**
      Find out if code is running with Meteor in the enviroment
      @return {boolean} True if code is running with Meteor in the enviroment
  
      @todo Find out if this is the best way to do this
  */
  utils.isMeteor = (function() {
  	return typeof Meteor !== 'undefined' && Meteor.release;
  })();
  
  /**
      Find out if code is running on a Meteor client
      @return {boolean} True if code is running on a Meteor client
  */
  utils.isMeteorClient = utils.isMeteorClient = (function() {
  	return utils.isMeteor && Meteor.isClient;
  })();
  
  /**
      Find out if code is running on a Meteor server
      @return {boolean} True if code is running on a Meteor server
  */
  utils.isMeteorServer = (function() {
  	return utils.isMeteor && Meteor.isServer;
  })();
  
  /**
      Find out code is running in a cordovar enviroment
      @return {boolean} True if code is running in a web worker enviroment
  
      @todo Find out if this is the best way to do this
  */
  utils.isCordova = (function() {
  	return typeof cordova === 'object';
  })();
  
  utils.isReactNative = (function() {
  	var isReact = false;
  	//*not-for-browser/*
  	try {
  		if (typeof require('react-native') === 'object') {
  			isReact = true;
  		}
  	} catch (e) {
  		void 0;
  	}
  	//*/
  	return isReact;
  })();
  
  utils.hasIndexedDB = (function() {
  	return !!utils.global.indexedDB;
  })();
  
  utils.isArray = function(obj) {
  	return '[object Array]' === Object.prototype.toString.call(obj);
  };
  /**
      Load text file from anywhere
      @param {string|object} path File path or HTML event
      @param {boolean} asy True - async call, false - sync call
      @param {function} success Success function
      @param {function} error Error function
      @return {string} Read data
  
      @todo Define Event type
      @todo Smaller if-else structures.
  */
  var loadFile = (utils.loadFile = function(path, asy, success, error) {
  	var data, fs;
  	if (utils.isNode || utils.isMeteorServer) {
  		//*not-for-browser/*
  		if (utils.isMeteor) {
  			fs = Npm.require('fs');
  		} else {
  			fs = require('fs');
  		}
  
  		// If path is empty, than read data from stdin (for Node)
  		if (typeof path === 'undefined') {
  			var buff = '';
  			process.stdin.setEncoding('utf8');
  			process.stdin.on('readable', function() {
  				var chunk = process.stdin.read();
  				if (chunk !== null) {
  					buff += chunk.toString();
  				}
  			});
  			process.stdin.on('end', function() {
  				success(cutbom(buff));
  			});
  		} else {
  			if (/^[a-z]+:\/\//i.test(path)) {
  				var request = require('request');
  				request(path, function(err, response, body) {
  					if (err) {
  						throw err;
  					}
  					success(cutbom(body.toString()));
  				});
  			} else {
  				//If async callthen call async
  				if (asy) {
  					fs.readFile(path, function(err, data) {
  						if (err) {
  							throw err;
  						}
  						success(cutbom(data.toString()));
  					});
  				} else {
  					// Call sync version
  					data = fs.readFileSync(path);
  					success(cutbom(data.toString()));
  				}
  			}
  		}
  	} else if (utils.isReactNative) {
  		// If ReactNative
  		var RNFS = require('react-native-fs');
  		RNFS.readFile(path, 'utf8')
  			.then(function(contents) {
  				success(cutbom(contents));
  			})
  			.catch(function(err) {
  				throw err;
  			});
  		//*/
  	} else if (utils.isCordova) {
  		/* If Cordova */
  		utils.global.requestFileSystem(LocalFileSystem.PERSISTENT, 0, function(fileSystem) {
  			fileSystem.root.getFile(path, {create: false}, function(fileEntry) {
  				fileEntry.file(function(file) {
  					var fileReader = new FileReader();
  					fileReader.onloadend = function(e) {
  						success(cutbom(this.result));
  					};
  					fileReader.readAsText(file);
  				});
  			});
  		});
  
  		/** @todo Check eliminated code below */
  
  		/*/*
  
          var paths = path.split('/');
          var filename = paths[paths.length-1];
          var dirpath = path.substr(0,path.length-filename.length);
   //       console.log('CORDOVA',filename,dirpath);
   //return success('[{"a":"'+filename+'"}]');
  
          window.resolveLocalFileSystemURL(dirpath, function(dir) {
              dir.getFile(filename, null, function(file) {
                  file.file(function(file) {
                      var reader = new FileReader();
   //                   console.log('READ FILE 2');
                      reader.onloadend = function(e) {
  //                    console.log('READ FILE 3',this.result);
                          success(this.result);
                      };
                      reader.readAsText(file);
                  });
              });
          });
  */
  	} else {
  		/* For string */
  		if (typeof path === 'string') {
  			// For browser read from tag
  			/*
                  SELECT * FROM TXT('#one') -- read data from HTML element with id="one"
              */
  			if (path.substr(0, 1) === '#' && typeof document !== 'undefined') {
  				data = document.querySelector(path).textContent;
  				success(data);
  			} else {
  				/*
                      Simply read file from HTTP request, like:
                      SELECT * FROM TXT('http://alasql.org/README.md');
                  */
  				var xhr = new XMLHttpRequest();
  				xhr.onreadystatechange = function() {
  					if (xhr.readyState === 4) {
  						if (xhr.status === 200) {
  							if (success) {
  								success(cutbom(xhr.responseText));
  							}
  						} else if (error) {
  							error(xhr);
  						}
  						// Todo: else...?
  					}
  				};
  				xhr.open('GET', path, asy); // Async
  				xhr.responseType = 'text';
  				xhr.send();
  			}
  		} else if (path instanceof Event) {
  			/*
                  For browser read from files input element
                  <input type="files" onchange="readFile(event)">
                  <script>
                      function readFile(event) {
                          alasql('SELECT * FROM TXT(?)',[event])
                      }
                  </script>
              */
  			/** @type {array} List of files from <input> element */
  			var files = path.target.files;
  			/** type {object} */
  			var reader = new FileReader();
  			/** type {string} */
  			var name = files[0].name;
  			reader.onload = function(e) {
  				var data = e.target.result;
  				success(cutbom(data));
  			};
  			reader.readAsText(files[0]);
  		}
  	}
  });
  
  /**
    @function Load binary file from anywhere
    @param {string} path File path
    @param {boolean} asy True - async call, false - sync call
    @param {function} success Success function
    @param {function} error Error function
    @return 1 for Async, data - for sync version
  
    @todo merge functionality from loadFile and LoadBinaryFile
  */
  
  var loadBinaryFile = (utils.loadBinaryFile = function(path, asy, success, error) {
  	var fs;
  	if (utils.isNode || utils.isMeteorServer) {
  		//*not-for-browser/*
  		if (utils.isMeteorServer) {
  			fs = Npm.require('fs'); // For Meteor
  		} else {
  			fs = require('fs');
  		}
  
  		if (/^[a-z]+:\/\//i.test(path)) {
  			var request = require('request');
  			request({url: path, encoding: null}, function(err, response, data) {
  				if (err) {
  					throw err;
  				}
  				var arr = [];
  				for (var i = 0; i < data.length; ++i) {
  					arr[i] = String.fromCharCode(data[i]);
  				}
  				success(arr.join(''));
  			});
  		} else {
  			if (asy) {
  				fs.readFile(path, function(err, data) {
  					if (err) {
  						throw err;
  					}
  					var arr = [];
  					for (var i = 0; i < data.length; ++i) {
  						arr[i] = String.fromCharCode(data[i]);
  					}
  					success(arr.join(''));
  				});
  			} else {
  				var data = fs.readFileSync(path);
  				var arr = [];
  				for (var i = 0; i < data.length; ++i) {
  					arr[i] = String.fromCharCode(data[i]);
  				}
  				success(arr.join(''));
  			}
  		}
  	} else if (utils.isReactNative) {
  		// If ReactNative
  		//var RNFS = require('react-native-fs');
  		var RNFetchBlob = require('react-native-fetch-blob').default;
  		var dirs = RNFetchBlob.fs.dirs;
  		//should use readStream instead if the file is large
  		RNFetchBlob.fs.readFile(path, 'base64').then(function(data) {
  			//RNFetchBlob.base64.decode(data) //need more test on excel
  			success(data);
  		});
  		//*/
  	} else {
  		if (typeof path === 'string') {
  			// For browser
  			var xhr = new XMLHttpRequest();
  			xhr.open('GET', path, asy); // Async
  			xhr.responseType = 'arraybuffer';
  			xhr.onload = function() {
  				var data = new Uint8Array(xhr.response);
  				var arr = [];
  				for (var i = 0; i < data.length; ++i) {
  					arr[i] = String.fromCharCode(data[i]);
  				}
  				success(arr.join(''));
  			};
  			// xhr.responseType = "blob";
  			xhr.send();
  		} else if (path instanceof Event) {
  			// console.log("event");
  			var files = path.target.files;
  			var reader = new FileReader();
  			var name = files[0].name;
  			reader.onload = function(e) {
  				var data = e.target.result;
  				success(data);
  			};
  			reader.readAsArrayBuffer(files[0]);
  		} else if (path instanceof Blob) {
  			success(path);
  		}
  	}
  });
  
  var removeFile = (utils.removeFile = function(path, cb) {
  	if (utils.isNode) {
  		//*not-for-browser/*
  		var fs = require('fs');
  		fs.remove(path, cb);
  	} else if (utils.isCordova) {
  		utils.global.requestFileSystem(LocalFileSystem.PERSISTENT, 0, function(fileSystem) {
  			fileSystem.root.getFile(
  				path,
  				{create: false},
  				function(fileEntry) {
  					fileEntry.remove(cb);
  					cb && cb(); // jshint ignore:line
  				},
  				function() {
  					cb && cb(); // jshint ignore:line
  				}
  			);
  		});
  	} else if (utils.isReactNative) {
  		// If ReactNative
  		var RNFS = require('react-native-fs');
  		RNFS.unlink(path)
  			.then(function() {
  				cb && cb();
  			})
  			.catch(function(err) {
  				throw err;
  			});
  		//*/
  	} else {
  		throw new Error('You can remove files only in Node.js and Apache Cordova');
  	}
  });
  
  // Todo: check if it makes sense to support cordova and Meteor server
  var deleteFile = (utils.deleteFile = function(path, cb) {
  	//*not-for-browser/*
  	if (utils.isNode) {
  		var fs = require('fs');
  		fs.unlink(path, cb);
  	} else if (utils.isReactNative) {
  		// If ReactNative
  		var RNFS = require('react-native-fs');
  		RNFS.unlink(path)
  			.then(function() {
  				cb && cb();
  			})
  			.catch(function(err) {
  				throw err;
  			});
  	}
  	//*/
  });
  
  utils.autoExtFilename = function(filename, ext, config) {
  	config = config || {};
  	if (
  		typeof filename !== 'string' ||
  		filename.match(/^[A-z]+:\/\/|\n|\..{2,4}$/) ||
  		config.autoExt === 0 ||
  		config.autoExt === false
  	) {
  		return filename;
  	}
  	return filename + '.' + ext;
  };
  
  var fileExists = (utils.fileExists = function(path, cb) {
  	if (utils.isNode) {
  		//*not-for-browser/*
  		var fs = require('fs');
  		fs.exists(path, cb);
  	} else if (utils.isCordova) {
  		utils.global.requestFileSystem(LocalFileSystem.PERSISTENT, 0, function(fileSystem) {
  			fileSystem.root.getFile(
  				path,
  				{create: false},
  				function(fileEntry) {
  					cb(true);
  				},
  				function() {
  					cb(false);
  				}
  			);
  		});
  	} else if (utils.isReactNative) {
  		// If ReactNative
  		var RNFS = require('react-native-fs');
  		RNFS.exists(path)
  			.then(function(yes) {
  				cb && cb(yes);
  			})
  			.catch(function(err) {
  				throw err;
  			});
  		//*/
  	} else {
  		// TODO Cordova, etc.
  		throw new Error('You can use exists() only in Node.js or Apach Cordova');
  	}
  });
  
  /**
    Save text file from anywhere
    @param {string} path File path
    @param {array} data Data object
    @param {function} cb Callback
    @param {object=} opts
  */
  
  var saveFile = (utils.saveFile = function(path, data, cb, opts) {
  	var res = 1;
  	if (path === undefined) {
  		//
  		// Return data into result variable
  		// like: alasql('SELECT * INTO TXT() FROM ?',[data]);
  		//
  		res = data;
  		if (cb) {
  			res = cb(res);
  		}
  	} else {
  		if (utils.isNode) {
  			//*not-for-browser/*
  			var fs = require('fs');
  			data = fs.writeFileSync(path, data);
  			if (cb) {
  				res = cb(res);
  			}
  		} else if (utils.isReactNative) {
  			var RNFS = require('react-native-fs');
  			RNFS.writeFile(path, data)
  				.then(function(success) {
  					//, 'utf8'
  					if (cb) res = cb(res);
  				})
  				.catch(function(err) {
  					console.error(err.message);
  				});
  		} else if (utils.isCordova) {
  			utils.global.requestFileSystem(LocalFileSystem.PERSISTENT, 0, function(fileSystem) {
  				//                alasql.utils.removeFile(path,function(){
  				fileSystem.root.getFile(path, {create: true}, function(fileEntry) {
  					fileEntry.createWriter(function(fileWriter) {
  						fileWriter.onwriteend = function() {
  							if (cb) {
  								res = cb(res);
  							}
  						};
  						fileWriter.write(data);
  					});
  				});
  			});
  			//*/
  
  			/*/*
          } else if((typeof cordova == 'object') && cordova.file) {
  //            console.log('saveFile 1');
          // Cordova
              var paths = path.split('/');
              var filename = paths[paths.length-1];
              var dirpath = path.substr(0,path.length-filename.length);
       //       console.log('CORDOVA',filename,dirpath);
       //return success('[{"a":"'+filename+'"}]');
  
              window.resolveLocalFileSystemURL(dirpath, function(dir) {
  //            console.log('saveFile 2');
  
                  dir.getFile(filename, {create:true}, function(file) {
  //            console.log('saveFile 3');
  
  //                    file.file(function(file) {
  //            console.log('saveFile 4');
  
                          file.createWriter(function(fileWriter) {
  
  //        fileWriter.seek(fileWriter.length);
  
                              var blob = new Blob([data], {type:'text/plain'});
                              fileWriter.write(blob);
                              fileWriter.onwriteend = function(){
                                  if(cb) cb();
                              };
  //                        console.log("ok, in theory i worked");
                          });
  */
  			/*/*
                          // Corodva
                          function writeFinish() {
                              // ... your done code here...
                              return cb()
                          };
                          var written = 0;
                            var BLOCK_SIZE = 1*1024*1024; // write 1M every time of write
                            function writeNext(cbFinish) {
                              var sz = Math.min(BLOCK_SIZE, data.length - written);
                              var sub = data.slice(written, written+sz);
                              writer.write(sub);
                              written += sz;
                              writer.onwrite = function(evt) {
                                if (written < data.length)
                                  writeNext(cbFinish);
                                else
                                  cbFinish();
                              };
                            }
                            writeNext(writeFinish);
                          }
  */
  			//                     });
  			//                });
  			//            });
  		} else {
  			if (isIE() === 9) {
  				// Solution was taken from
  				// http://megatuto.com/formation-JAVASCRIPT.php?JAVASCRIPT_Example=Javascript+Save+CSV+file+in+IE+8/IE+9+without+using+window.open()+Categorie+javascript+internet-explorer-8&category=&article=7993
  				//				var URI = 'data:text/plain;charset=utf-8,';
  
  				// Prepare data
  				var ndata = data.replace(/\r\n/g, '&#A;&#D;');
  				ndata = ndata.replace(/\n/g, '&#D;');
  				ndata = ndata.replace(/\t/g, '&#9;');
  				var testlink = utils.global.open('about:blank', '_blank');
  				testlink.document.write(ndata); //fileData has contents for the file
  				testlink.document.close();
  				testlink.document.execCommand('SaveAs', false, path);
  				testlink.close();
  			} else {
  				var opt = {
  					disableAutoBom: false,
  				};
  				alasql.utils.extend(opt, opts);
  				var blob = new Blob([data], {type: 'text/plain;charset=utf-8'});
  				saveAs(blob, path, opt.disableAutoBom);
  				if (cb) {
  					res = cb(res);
  				}
  			}
  		}
  	}
  
  	return res;
  });
  
  /**
      @function Is this IE9
      @return {boolean} True for IE9 and false for other browsers
  
      For IE9 compatibility issues
  */
  function isIE() {
  	var myNav = navigator.userAgent.toLowerCase();
  	return myNav.indexOf('msie') !== -1 ? parseInt(myNav.split('msie')[1]) : false;
  }
  
  //  For LOAD
  //  var saveBinaryFile = utils.saveFile = function(path, data, cb) {
  //     if(utils.isNode) {
  //         // For Node.js
  //         var fs = require('fs');
  //         var data = fs.writeFileSync(path,data);
  //     } else {
  //         var blob = new Blob([data], {type: "text/plain;charset=utf-8"});
  //         saveAs(blob, path);
  //     }
  //  };
  
  /**
    @function Hash a string to signed integer
    @param {string} source string
    @return {integer} hash number
  */
  
  // FNV-1a inspired hashing
  var hash = (utils.hash = function(str) {
  	var hash = 0x811c9dc5,
  		i = str.length;
  	while (i) {
  		hash = hash ^ str.charCodeAt(--i);
  		hash += (hash << 1) + (hash << 4) + (hash << 7) + (hash << 8) + (hash << 24);
  	}
  	return hash;
  });
  
  /**
      Union arrays
      @function
      @param {array} a
      @param {array} b
      @return {array}
  */
  var arrayUnion = (utils.arrayUnion = function(a, b) {
  	var r = b.slice(0);
  	a.forEach(function(i) {
  		if (r.indexOf(i) < 0) {
  			r.push(i);
  		}
  	});
  	return r;
  });
  
  /**
   Array Difference
   */
  var arrayDiff = (utils.arrayDiff = function(a, b) {
  	return a.filter(function(i) {
  		return b.indexOf(i) < 0;
  	});
  });
  
  /**
    Arrays deep intersect (with records)
   */
  var arrayIntersect = (utils.arrayIntersect = function(a, b) {
  	var r = [];
  	a.forEach(function(ai) {
  		var found = false;
  
  		b.forEach(function(bi) {
  			found = found || ai === bi;
  		});
  
  		if (found) {
  			r.push(ai);
  		}
  	});
  	return r;
  });
  
  /**
    Arrays deep union (with records)
   */
  var arrayUnionDeep = (utils.arrayUnionDeep = function(a, b) {
  	var r = b.slice(0);
  	a.forEach(function(ai) {
  		var found = false;
  
  		r.forEach(function(ri) {
  			//            found = found || equalDeep(ai, ri, true);
  			found = found || deepEqual(ai, ri);
  		});
  
  		if (!found) {
  			r.push(ai);
  		}
  	});
  	return r;
  });
  
  /**
    Arrays deep union (with records)
   */
  var arrayExceptDeep = (utils.arrayExceptDeep = function(a, b) {
  	var r = [];
  	a.forEach(function(ai) {
  		var found = false;
  
  		b.forEach(function(bi) {
  			//            found = found || equalDeep(ai, bi, true);
  			found = found || deepEqual(ai, bi);
  		});
  
  		if (!found) {
  			r.push(ai);
  		}
  	});
  	return r;
  });
  
  /**
    Arrays deep intersect (with records)
   */
  var arrayIntersectDeep = (utils.arrayIntersectDeep = function(a, b) {
  	var r = [];
  	a.forEach(function(ai) {
  		var found = false;
  
  		b.forEach(function(bi) {
  			//            found = found || equalDeep(ai, bi, true);
  			found = found || deepEqual(ai, bi, true);
  		});
  
  		if (found) {
  			r.push(ai);
  		}
  	});
  	return r;
  });
  
  /**
    Deep clone objects
   */
  var cloneDeep = (utils.cloneDeep = function cloneDeep(obj) {
  	if (null === obj || typeof obj !== 'object') {
  		return obj;
  	}
  
  	if (obj instanceof Date) {
  		return new Date(obj);
  	}
  
  	var temp = obj.constructor(); // changed
  
  	for (var key in obj) {
  		if (obj.hasOwnProperty(key)) {
  			temp[key] = cloneDeep(obj[key]);
  		}
  	}
  	return temp;
  });
  
  /**
    Check equality of objects
  */
  
  /*/*
  var equalDeep = utils.equalDeep = function equalDeep (x, y, deep) {
      if (deep) {
          if (x === y){
              return true;
          }
  
          var p;
          for (p in y) {
              if (typeof (x[p]) === 'undefined') { return false; }
          }
  
          for (p in y) {
              if (y[p]) {
                  switch (typeof (y[p])) {
                      case 'object':
                          if (!equalDeep(y[p],x[p])) { return false; } break;
                      case 'function':
                          if (
                                  typeof (x[p]) === 'undefined' ||
                                  (p !== 'equals' && y[p].toString() !== x[p].toString())
                              ){
                                  return false;
                              }
                          break;
                      default:
                          if (y[p] !== x[p]) { return false; }
                  }
              } else {
                  if (x[p]){
                      return false;
                  }
              }
          }
  
          for (p in x) {
              if (typeof (y[p]) === 'undefined') { return false; }
          }
  
          return true;
      }
      return x === y;
  };
  */
  
  /**
    Compare two objects in deep
   */
  var deepEqual = (utils.deepEqual = function(x, y) {
  	if (x === y) {
  		return true;
  	}
  
  	if (typeof x === 'object' && null !== x && (typeof y === 'object' && null !== y)) {
  		if (Object.keys(x).length !== Object.keys(y).length) {
  			return false;
  		}
  		for (var prop in x) {
  			if (!deepEqual(x[prop], y[prop])) {
  				return false;
  			}
  		}
  		return true;
  	}
  
  	return false;
  });
  /**
      Array with distinct records
      @param {array} data
      @return {array}
  */
  var distinctArray = (utils.distinctArray = function(data) {
  	var uniq = {};
  	// TODO: Speedup, because Object.keys is slow
  	for (var i = 0, ilen = data.length; i < ilen; i++) {
  		var uix;
  		if (typeof data[i] === 'object') {
  			uix = Object.keys(data[i])
  				.sort()
  				.map(function(k) {
  					return k + '`' + data[i][k];
  				})
  				.join('`');
  		} else {
  			uix = data[i];
  		}
  		uniq[uix] = data[i];
  	}
  	var res = [];
  	for (var key in uniq) {
  		res.push(uniq[key]);
  	}
  	return res;
  });
  
  /**
      Extend object a with properties of b
      @function
      @param {object} a
      @param {object} b
      @return {object}
  */
  var extend = (utils.extend = function extend(a, b) {
  	a = a || {};
  	for (var key in b) {
  		if (b.hasOwnProperty(key)) {
  			a[key] = b[key];
  		}
  	}
  	return a;
  });
  
  /**
     Flat array by first row
   */
  var flatArray = (utils.flatArray = function(a) {
  	//console.log(684,a);
  	if (!a || 0 === a.length) {
  		return [];
  	}
  
  	// For recordsets
  	if (typeof a === 'object' && a instanceof alasql.Recordset) {
  		return a.data.map(function(ai) {
  			return ai[a.columns[0].columnid];
  		});
  	}
  	// Else for other arrays
  	var key = Object.keys(a[0])[0];
  	if (key === undefined) {
  		return [];
  	}
  	return a.map(function(ai) {
  		return ai[key];
  	});
  });
  
  /**
    Convert array of objects to array of arrays
   */
  var arrayOfArrays = (utils.arrayOfArrays = function(a) {
  	return a.map(function(aa) {
  		var ar = [];
  		for (var key in aa) {
  			ar.push(aa[key]);
  		}
  		return ar;
  	});
  });
  
  if (!Array.isArray) {
  	Array.isArray = function(arg) {
  		return Object.prototype.toString.call(arg) === '[object Array]';
  	};
  }
  
  /**
      Excel:convert number to Excel column, like 1 => 'A'
      @param {integer} i Column number, starting with 0
      @return {string} Column name, starting with 'A'
  */
  
  var xlsnc = (utils.xlsnc = function(i) {
  	var addr = String.fromCharCode(65 + i % 26);
  	if (i >= 26) {
  		i = ((i / 26) | 0) - 1;
  		addr = String.fromCharCode(65 + i % 26) + addr;
  		if (i > 26) {
  			i = ((i / 26) | 0) - 1;
  			addr = String.fromCharCode(65 + i % 26) + addr;
  		}
  	}
  	return addr;
  });
  
  /**
      Excel:conver Excel column name to number
      @param {string} s Column number, like 'A' or 'BE'
      @return {string} Column name, starting with 0
  */
  var xlscn = (utils.xlscn = function(s) {
  	var n = s.charCodeAt(0) - 65;
  	if (s.length > 1) {
  		n = (n + 1) * 26 + s.charCodeAt(1) - 65;
  		//        console.log(n, s.charCodeAt(0)-65, s.charCodeAt(1)-65);
  		if (s.length > 2) {
  			n = (n + 1) * 26 + s.charCodeAt(2) - 65;
  		}
  	}
  	return n;
  });
  
  var domEmptyChildren = (utils.domEmptyChildren = function(container) {
  	var len = container.childNodes.length;
  	while (len--) {
  		container.removeChild(container.lastChild);
  	}
  });
  
  /**
      SQL LIKE emulation
      @parameter {string} pattern Search pattern
      @parameter {string} value Searched value
      @parameter {string} escape Escape character (optional)
      @return {boolean} If value LIKE pattern ESCAPE escape
  */
  
  var like = (utils.like = function(pattern, value, escape) {
  	// Verify escape character
  	if (!escape) escape = '';
  
  	var i = 0;
  	var s = '^';
  
  	while (i < pattern.length) {
  		var c = pattern[i],
  			c1 = '';
  		if (i < pattern.length - 1) c1 = pattern[i + 1];
  
  		if (c === escape) {
  			s += '\\' + c1;
  			i++;
  		} else if (c === '[' && c1 === '^') {
  			s += '[^';
  			i++;
  		} else if (c === '[' || c === ']') {
  			s += c;
  		} else if (c === '%') {
  			s += '.*';
  		} else if (c === '_') {
  			s += '.';
  		} else if ('/.*+?|(){}'.indexOf(c) > -1) {
  			s += '\\' + c;
  		} else {
  			s += c;
  		}
  		i++;
  	}
  
  	s += '$';
  	//    if(value == undefined) return false;
  	//console.log(s,value,(value||'').search(RegExp(s))>-1);
  	return ('' + (value || '')).toUpperCase().search(RegExp(s.toUpperCase())) > -1;
  });
  
  utils.glob = function(value, pattern) {
  	var i = 0;
  	var s = '^';
  
  	while (i < pattern.length) {
  		var c = pattern[i],
  			c1 = '';
  		if (i < pattern.length - 1) c1 = pattern[i + 1];
  
  		if (c === '[' && c1 === '^') {
  			s += '[^';
  			i++;
  		} else if (c === '[' || c === ']') {
  			s += c;
  		} else if (c === '*') {
  			s += '.*';
  		} else if (c === '?') {
  			s += '.';
  		} else if ('/.*+?|(){}'.indexOf(c) > -1) {
  			s += '\\' + c;
  		} else {
  			s += c;
  		}
  		i++;
  	}
  
  	s += '$';
  	return ('' + (value || '')).toUpperCase().search(RegExp(s.toUpperCase())) > -1;
  };
  
  /**
  	Get path of alasql.js
  	@todo Rewrite and simplify the code. Review, is this function is required separately
  */
  utils.findAlaSQLPath = function() {
  	/** type {string} Path to alasql library and plugins */
  
  	if (utils.isWebWorker) {
  		return '';
  		/** @todo Check how to get path in worker */
  	} else if (utils.isMeteorClient) {
  		return '/packages/dist/';
  	} else if (utils.isMeteorServer) {
  		return 'assets/packages/dist/';
  	} else if (utils.isNode) {
  		return __dirname;
  	} else if (utils.isBrowser) {
  		var sc = document.getElementsByTagName('script');
  
  		for (var i = 0; i < sc.length; i++) {
  			if (sc[i].src.substr(-16).toLowerCase() === 'alasql-worker.js') {
  				return sc[i].src.substr(0, sc[i].src.length - 16);
  			} else if (sc[i].src.substr(-20).toLowerCase() === 'alasql-worker.min.js') {
  				return sc[i].src.substr(0, sc[i].src.length - 20);
  			} else if (sc[i].src.substr(-9).toLowerCase() === 'alasql.js') {
  				return sc[i].src.substr(0, sc[i].src.length - 9);
  			} else if (sc[i].src.substr(-13).toLowerCase() === 'alasql.min.js') {
  				return sc[i].src.substr(0, sc[i].src.length - 13);
  			}
  		}
  	}
  	return '';
  };
  
  var getXLSX = function() {
  	var XLSX = null;
  	/* If require() shuold be supported else take from global scope */
  	if (utils.isNode || utils.isBrowserify || utils.isMeteorServer) {
  		//*not-for-browser/*
  		XLSX = require('xlsx') || null;
  		//*/
  	} else {
  		XLSX = utils.global.XLSX || null;
  	}
  
  	if (null === XLSX) {
  		throw new Error('Please include the xlsx.js library');
  	}
  
  	return XLSX;
  };
  
  // set AlaSQl path
  alasql.path = alasql.utils.findAlaSQLPath();
  