<commit_before>var util = {};
var Error = require(__dirname+"/../error.js");

var Minval = require(__dirname+"/../minval.js");
var Maxval = require(__dirname+"/../maxval.js");
var Asc = require(__dirname+"/../asc.js");
var Desc = require(__dirname+"/../desc.js");
var Literal = require(__dirname+"/../literal.js");
var ReqlGeometry;
var ReqlDate = require(__dirname+"/../date.js");
var protodef = require(__dirname+"/../protodef.js");
var constants = require(__dirname+"/../constants.js");
var GeographicLib = require(__dirname+"/../../external/geographiclib.js");
var termTypes = protodef.Term.TermType;
// Lazily loaded
// TODO Fix the dependencies
var Sequence;
var Table;
var Database;
var Selection;
var Document;
var MissingDoc;
var Group;
var Changes;


util.log = function(message) {
  if (constants.DEBUG === true) {
    console.log.apply(console.log, Array.prototype.slice.call(arguments, 0));
  }
}

util.isBuggy = function(query) {
  throw new Error.ReqlRuntimeError("Reqlite is buggy, this code should not be reached", query.frames)
}
util.notAvailable = function(message, query) {
  if (message == null) {
    throw new Error.ReqlRuntimeError("This method has not been implemented (yet) in Reqlite", query.frames)
  }
  else {
    throw new Error.ReqlRuntimeError(message, query.frames)
  }
}

util.isPlainObject = function isPlainObject(value) {
  return Object.prototype.toString.call(value) === '[object Object]';
}

util.typeOf = function(value, query) {
  //TODO Properly handle circular references
  if (Sequence === undefined) { Sequence = require(__dirname+"/../sequence.js"); }

  if (typeof value === "number") {
    return "NUMBER";
  }
  else if (typeof value === "string") {
    return "STRING";
  }
  else if (typeof value === "boolean") {
    return "BOOL";
  }
  else if (value === null) {
    return "NULL";
  }
  else if (typeof value.typeOf === "function") {
    // Covers TABLE, GROUP
    return value.typeOf();
  }
  else if (util.isDate(value)) {
    return "PTYPE<TIME>";
  }
  else if (util.isBinary(value)) {
    return "PTYPE<BINARY>";
  }
  else if (util.isSequence(value)) {
    return value.type; // Can be "ARRAY", "STREAM", etc.
  }
  else if (util.isDocument(value)) {
    return "SELECTION<OBJECT>";
  }
  else if (util.isObject(value)) {
    return "OBJECT";
  }
  else if (typeof value === 'function') {
    return "FUNCTION";
  }
  throw new Error.ReqlRuntimeError("Server is buggy, unknown type", query.frames)

}

util.assertJavaScriptResult = function(value, query) {
  if ((value !== undefined) && ((value === null)
      || util.isTable(value)
      || util.isSequence(value)
      || util.isSelection(value)
      || util.isDate(value)
      || util.isDocument(value)
      || util.isBinary(value)
      || util.isGeometry(value)
      || util.isAsc(value)
      || util.isDesc(value))) {
    return;
  }
  else if (util.isPlainObject(value)) {
    var keys = Object.keys(value);
    for(var i=0; i<keys.length; i++) {
     util.assertJavaScriptResult(value[keys[i]], query);
    }
  }
  else if (Array.isArray(value)) {
    for(var i=0; i<value.length; i++) {
     util.assertJavaScriptResult(value[i], query);
    }
  }
  else {
    util.assertFinite(value, query);
    util.assertDefined(value, query);
  }
}

util.assertFinite = function(value, query) {
  if ((value === Infinity)
      || (value === -Infinity)
      || (value !== value)) {
    throw new Error.ReqlRuntimeError('Number return value is not finite', query.frames)
  }
}

util.assertDefined = function(value, query) {
  if (value === undefined) {
    throw new Error.ReqlRuntimeError('Cannot convert javascript `undefined` to ql::datum_t', query.frames)
  }
  else if (value instanceof RegExp) {
    throw new Error.ReqlRuntimeError('Cannot convert RegExp to ql::datum_t', query.frames)
  }
}


util.nestedWriteForbidden = function(query) {
  throw new Error.ReqlRuntimeError('Cannot nest writes or meta ops in stream operations.  Use FOR_EACH instead', query.frames)
}

util.nonDeterministicOp = function(query) {
  // RethinkDB doesn't seem to keep track of the frames here..
  throw new Error.ReqlRuntimeError('Could not prove function deterministic.  Maybe you want to use the non_atomic flag?', [])
}

util.assertType = function assertType(value, type, query) {
  var typeValue;
  var finalError;

  typeValue = util.getType(value);
  if (Array.isArray(type)) {
    var valid = false;
    for(var i=0; i<type.length; i++) {
      var _type = type[i];
      try {
        util.assertTypeutil(_type, value, typeValue, query);
        valid = true;
        break;
      }
      catch(err) {
        finalError = err;
        // Ignore error, we will throw at the end of this if statement.
      }
    }
    if (valid !== true) {
      //TODO Hack for date.sub, are there other cases?
      //throw finalError;

      for(var i=0; i<type.length; i++) {
        if (type[i] === 'PTYPE<BINARY>') {
          type[i] = 'BINARY';
        }
      }
      throw new Error.ReqlRuntimeError('Expected '+type.join(' or ')+', but found '+typeValue, query.frames)
    }
  }
  else {
    util.assertTypeutil(type, value, typeValue, query)
  }
}

util.assertTime = function(date, query, backquote) {
  if (!(date instanceof ReqlDate)) {
    if (backquote === true) { // RethinkDB doesn't have consistent errors -_-
      throw new Error.ReqlRuntimeError('Not a TIME pseudotype: `'+JSON.stringify(date, null, 4)+'`', query.frames);
    }
    else {
      throw new Error.ReqlRuntimeError('Not a TIME pseudotype: '+JSON.stringify(date, null, 4), query.frames);
    }
  }
}

util.assertAttributes = function(value, attributes, query) {
  for(var i=0; i<attributes.length; i++) {
    if (value[attributes[i]] === undefined) {
      throw new Error.ReqlRuntimeError("No attribute `"+attributes[i]+"` in object:\n"+JSON.stringify(value, null, 4), query.frames)
    }
  }
}
util.assertCoordinatesLength = function(coordinates, query) {
  if (coordinates.length < 2) {
    throw new Error.ReqlRuntimeError('Too few coordinates.  Need at least longitude and latitude', query.frames)
  }
  else if (coordinates.length > 2) {
    throw new Error.ReqlRuntimeError('Too many coordinates.  GeoJSON position should have no more than three coordinates, but got '+coordinates.length, query.frames)
  }
}
util.assertPointCoordinates = function(value, query) {
  util.assertType(value, 'ARRAY', this);
  if (value.length !== 2) {
    throw new Error.ReqlRuntimeError("Expected point coordinate pair.  Got "+value.length+" element array instead of a 2 element one", query.frames)
  }
  util.assertType(value.get(0), 'NUMBER', query);
  util.assertType(value.get(1), 'NUMBER', query);
}

util.assertArity = function(expected, args, query) {
  var len;
  if (Array.isArray(args)) {
    len = args.length;
  }
  else {
    len = 0;
  }
  if (expected !== len) {
    util.arityError(expected, len, query);
  }
}
util.assertArityRange = function(min, max, args, query) {
  var len;
  if (Array.isArray(args)) {
    len = args.length;
  }
  else {
    len = 0;
  }

  if ((len < min) || (len > max)) {
    util.arityRangeError(min, max, len, query);
  }
}

util.arityError = function(expected, found, query) {
  if (expected === 1) {
    throw new Error.ReqlRuntimeError('Expected '+expected+' argument but found '+found, query.frames);
  }
  else {
    throw new Error.ReqlRuntimeError('Expected '+expected+' arguments but found '+found, query.frames);
  }
}

util.arityRangeError = function(min, max, found, query) {
  if (max === Infinity) {
    throw new Error.ReqlRuntimeError('Expected '+min+' or more arguments but found '+found, query.frames);
  }
  else {
    throw new Error.ReqlRuntimeError('Expected between '+min+' and '+max+' arguments but found '+found, query.frames);
  }
}

util.cannotPerformOp = function(operation, value, query) {
  throw new Error.ReqlRuntimeError('Cannot perform '+operation+' on a non-object non-sequence `'+JSON.stringify(value, null, 4)+'`', query.frames)
}


util.assertPreDatum = function(value, query) {
  if (util.isFunction(value)) {
    throw new Error.ReqlRuntimeError("Expected type DATUM but found FUNCTION:\nVALUE FUNCTION", query.frames)
  }
}
util.assertTypeutil = function(type, value, typeValue, query) {
  //TODO use util.getType
  if (type === 'DATUM') {
    if (!util.isDatum(value)) {
      if (util.isTable(value)) {
        throw new Error.ReqlRuntimeError('Expected type DATUM but found TABLE:\ntable("'+value.name+'")', query.frames)
      }
      else if (util.isSelection(value)) {
        throw new Error.ReqlRuntimeError('Expected type DATUM but found SELECTION:\nSELECTION ON table('+value.table.name+')', query.frames)
      }
      else if (util.isChanges(value)) {
        throw new Error.ReqlRuntimeError('Expected type DATUM but found SEQUENCE:\nVALUE SEQUENCE', query.frames)
      }
      else {
        throw new Error.ReqlRuntimeError("Expected type DATUM but found "+typeValue, query.frames)
      }

    }
  }
  else if (type === 'STRING') {
    if (typeof value !== 'string') {
      throw new Error.ReqlRuntimeError("Expected type STRING but found "+typeValue, query.frames)
    }
  }
  else if (type === 'NUMBER') {
    if (typeof value !== 'number') {
      throw new Error.ReqlRuntimeError("Expected type NUMBER but found "+typeValue, query.frames)
    }
  }
  else if (type === 'BOOL') {
    if (typeof value !== 'boolean') {
      throw new Error.ReqlRuntimeError("Expected type BOOL but found "+typeValue, query.frames)
    }
  }
  else if (type === "PTYPE<TIME>") {
    if (!util.isDate(value)) {
      throw new Error.ReqlRuntimeError("Expected type PTYPE<TIME> but found "+typeValue, query.frames)
    }
  }
  else if (type === "GEOMETRY") {
    if (!util.isGeometry(value)) {
      throw new Error.ReqlRuntimeError("Not a GEOMETRY pseudotype: `"+JSON.stringify(value, null, 4)+"`", query.frames)
    }
  }
  else if (type === "POLYGON") {
    if (!util.isGeometry(value)) {
      throw new Error.ReqlRuntimeError("Not a GEOMETRY pseudotype: `"+JSON.stringify(value, null, 4)+"`", query.frames)
    }
    else if (value.type !== 'Polygon') {
      throw new Error.ReqlRuntimeError("Expected geometry of type `Polygon` but found `"+value.type+"`", query.frames)
    }
  }
  else if (type === 'FUNCTION') {
    if (!util.isFunction(value)) {
      if (util.isDatum(value)) {
        typeValue = 'DATUM';
      }
      throw new Error.ReqlRuntimeError("Expected type FUNCTION but found "+typeValue+":\n"+JSON.stringify(util.toDatum(value)), query.frames)
    }
  }
  else if (type === 'GROUP') {
    if (!util.isGroup(value)) {
      if (util.isDatum(value)) {
        typeValue = 'DATUM';
      }
      throw new Error.ReqlRuntimeError("Expected type GROUPED_DATA but found "+typeValue+":\n"+JSON.stringify(util.toDatum(value), null, 4), query.frames)
    }
  }
  else if (type === "DATABASE") {
    if (Database === undefined) { Database = require(__dirname+"/../database.js"); }
    if (!(value instanceof Database)) {
      if (util.isDatum(value)) {
        typeValue = 'DATUM';
      }
      throw new Error.ReqlRuntimeError("Expected type DATABASE but found "+typeValue+":\n"+JSON.stringify(util.toDatum(value)), query.frames)
    }
  }
  else if (type === "TABLE_SLICE") {
    if (Table === undefined) { Table = require(__dirname+"/../table.js"); }
    if (Selection === undefined) { Selection = require(__dirname+"/../selection.js"); }
    if (!util.isTable(value) && !util.isSelection(value)) {
      if (util.isDatum(value)) {
        typeValue = 'DATUM';
      }
      throw new Error.ReqlRuntimeError("Expected type TABLE_SLICE but found "+typeValue+":\n"+JSON.stringify(util.toDatum(value)), query.frames)
    }

  }
  else if (type === "TABLE") {
    if (!util.isTable(value)) {
      if (util.isDatum(value)) {
        typeValue = 'DATUM';
      }
      throw new Error.ReqlRuntimeError("Expected type TABLE but found "+typeValue+":\n"+JSON.stringify(util.toDatum(value)), query.frames)
    }
  }
  else if (type === "SELECTION") {
    if (!util.isTable(value) && !util.isSelection(value) && !util.isDocument(value)) {
      if (util.isDatum(value)) {
        typeValue = 'DATUM';
      }
      throw new Error.ReqlRuntimeError("Expected type SELECTION but found "+typeValue+":\n"+JSON.stringify(util.toDatum(value), null, 4), query.frames)
    }
  }
  else if (type === "ARRAY") {
    if (!util.isSequence(value)) {
      throw new Error.ReqlRuntimeError("Expected type ARRAY but found "+typeValue, query.frames)
    }
  }
  else if (type === "PTYPE<BINARY>") {
    if (!util.isBinary(value)) {
      throw new Error.ReqlRuntimeError("Expected type BINARY but found "+typeValue, query.frames)
    }
  }
  else if (type === "INT") {
    var floored = Math.floor(value);
    if (floored !== value) {
      throw new Error.ReqlRuntimeError("Number not an integer: "+value, query.frames)
    }
  }
  else if (type === "OBJECT") {
    if (!util.isObject(value)) {
      throw new Error.ReqlRuntimeError("Expected type OBJECT but found "+typeValue, query.frames)
    }
  }
}

util.getType = function(value) {
  if (Sequence === undefined) { Sequence = require(__dirname+"/../sequence.js"); }
  if (Table === undefined) { Table = require(__dirname+"/../table.js"); }

  if (value instanceof Table) {
    return "TABLE";
  }
  else if (value instanceof Sequence) {
    return "ARRAY";
  }
  else if (value === null) {
    return "NULL"
  }
  else if (util.isDate(value)) {
    return "PTYPE<TIME>";
  }
  else if (util.isBinary(value)) {
    return "PTYPE<BINARY>";
  }
  else if (value instanceof Maxval) {
    return "MAXVAL";
  }
  else if (value instanceof Minval) {
    return "MINVAL";
  }
  else if (util.isPlainObject(value)) {
    return "OBJECT";
  }
  else {
    return (typeof value).toUpperCase();
  }
}

util.assertOptions = function(options, allowedKeys, query) {
  var isAllowedKey = {};
  for(var i=0; i<allowedKeys.length; i++) {
    isAllowedKey[allowedKeys[i]] = true;
  }

  var keys = Object.keys(options);
  for(var i=0; i<keys.length; i++) {
    if (isAllowedKey[keys[i]] !== true) {
      throw new Error.ReqlRuntimeError('Unrecognized optional argument `'+keys[i]+'`', query.frames)
    }
  }
}

util.assertNoSpecialChar = function assertNoSpecialChar(value, type, query) {
  if (!value.match(/^[0-9a-zA-Z]+$/)) {
    throw new Error.ReqlRuntimeError(type+" name `"+value+"` invalid (Use A-Za-z0-9_ only)", query.frames)
  }
}

util.s4 = function s4() {
  return Math.floor((1+Math.random())*0x10000).toString(16).substring(1);
};

util.uuid = function uuid() {
  return util.s4()+util.s4()+"-"+util.s4()+"-"+util.s4()+"-"+util.s4()+"-"+util.s4()+util.s4()+util.s4();
}

util.writeResult = function writeResult() {
  return {
    deleted: 0,
    errors: 0,
    inserted: 0,
    replaced: 0,
    skipped: 0,
    unchanged: 0
  }
}

// Merging write result in place
util.mergeWriteResult = function(left, right) {
  left.deleted += right.deleted;
  left.errors += right.errors;
  left.inserted += right.inserted;
  left.replaced += right.replaced;
  left.skipped += right.skipped;
  left.unchanged += right.unchanged;
  if (left.generated_keys !== undefined || right.generated_keys !== undefined) {
    if (Array.isArray(left.generated_keys) && Array.isArray(right.generated_keys)) {
      left.generated_keys = left.generated_keys.concat(right.generated_keys);
    }
    else if (Array.isArray(right.generated_keys)) {
      left.generated_keys = right.generated_keys;
    }
  }
  if ((left.first_error === undefined) && (right.first_error !== undefined)) {
    left.first_error = right.first_error;
  }
  if (util.isSequence(right.changes) && right.changes.length > 0) {
    if (!util.isSequence(left.changes)) {
      left.changes = new Sequence();
    }
    for(var i=0; i<right.changes.length; i++) {
      left.changes.push(right.changes.get(i))
    }
  }
}

util.merge = function(self, toMerge, query, internalOptions) {
  var varId;
  if (util.isFunction(toMerge)) {
    varId = util.getVarId(toMerge);
    query.context[varId] = self;
  }

  toMerge = query.evaluate(toMerge, internalOptions);

  if (varId !== undefined) {
    delete query.context[varId];
  }
  return util.mergeDatum(self, toMerge);
}
util.mergeDatum = function merge(self, obj) {
  if (!util.isPlainObject(obj)) {
    return obj;
  }
  if (!util.isPlainObject(self)) {
    return obj;
  }

  //A non in place merge, used for the ReQL `merge` command
  var result = util.deepCopy(self);
  for(var key in obj) {
    // Recursively merge only if both fields are objects, else we'll overwrite the field
    if (obj[key] instanceof Literal) {
      result[key] = obj[key].value;
    }
    else if (util.isPlainObject(obj[key]) && util.isPlainObject(result[key])) {
      result[key] = util.mergeDatum(result[key], obj[key]);
    }
    else {
      result[key] = obj[key];
    }
  }
  return result;
}
util._merge = function _merge(self, obj) {
  // Inplace merge (behave like the `update` command.
  // Return whether `self` has been changed
  var changed = false;
  for(var key in obj) {
    // Recursively merge only if both fields are objects, else we'll overwrite the field
    if (obj[key] instanceof Literal) {
      changed = true;
      if (util.eq(self[key], obj[key].value)) {
        changed = false;
      }
      self[key] = obj[key].value;
    }
    else if (util.isPlainObject(obj[key]) && util.isPlainObject(self[key])) {
      changed = util._merge.call(self[key], obj[key]) || changed;
    }
    else {
      if (Array.isArray(self[key])) {
        if (Array.isArray(obj[key])) {
          if (self[key].length !== obj[key].length) {
            changed = true;
          }
          else {
            for(var i=0; i<self[key].length; i++) {
              if ((util.isPlainObject(self[key][i])) && (util.isPlainObject(obj[key][i]))) {
                changed = util._merge(self[key][i], obj[key][i]) || changed;
              }
              else {
                changed = true;
                self[key][i] = util.deepCopy(obj[key][i]);
              }
            }
          }
        }
        else {
          changed = true;
        }
      }
      else if (self[key] !== obj[key]) {
        changed = true;
      }
      self[key] = obj[key];
    }
  }
  return changed
}

util._replace = function replace(self, obj) {
  // Inplace replace (behave like the `replace` command.
  // Return whether `self` has been changed
  var changed = false;
  for(var key in self) {
    if (obj[key] === undefined) {
      delete self[key];
      changed = true;
    }
  }
  for(var key in obj) {
    // Recursively merge only if both fields are objects, else we'll overwrite the field
    if (util.isPlainObject(obj[key]) && util.isPlainObject(self[key])) {
      changed = util.replaceecall(self[key], obj[key]) || changed;
    }
    else {
      if (Array.isArray(self[key])) {
        if (Array.isArray(obj[key])) {
          if (self[key].length !== obj[key].length) {
            changed = true;
          }
          else {
            for(var i=0; i<self[key].length; i++) {
              //TODO If self[ley][i] is not an object, it will break
              if ((util.isPlainObject(self[key][i])) && (util.isPlainObject(obj[key][i]))) {
                changed = util.replace(self[key][i], obj[key][i]) || changed;
              }
              else if (util.isPlainObject(self[key][i])) {
                changed = true;
                self[key][i];
              }
            }
          }
        }
        else {
          changed = true;
        }
      }
      else if (self[key] !== obj[key]) {
        changed = true;
      }
      self[key] = obj[key];
    }
  }
  return changed
}

util.makeInternalPk = function(value) {
  // Build the internal prinary key we use in the hash table
  if (typeof value === 'string') {
    return "string_"+value
  }
  else if (typeof value === 'number') {
    return "number_"+value
  }
  else if (typeof value === 'boolaen') {
    return "boolean_"+value
  }
  else if (util.isDate(value)) {
    util.validDate(value);
    return "date_"+value.epoch_time+"_"+value.timezone;
  }
  else if (util.isBinary(value)) {
    return "binary_"+value.data
  }
  else {
    var type = util.typeOf(value);
    throw new Error.ReqlRuntimeError("Primary keys must be either a number, string, bool, pseudotype or array (got type "+type+"):\n"+JSON.stringify(value, null, 2));
  }

}
util.validDate = function(date) {
  //TODO use moment
  // `date` should be an object with the field $reql_type$ mapping to "TIME"
  if (typeof date.epoch_time !== "number") {
    throw new Error.ReqlRuntimeError("RqlRuntimeError: Invalid time object constructed (no field `epoch_time`):\n"+JSON.stringify(date, null, 2))
  }
  else if (date.timezone.match(/^(?:Z|[+-](?:2[0-3]|[01][0-9]):[0-5][0-9])$/) == null) {
    throw new Error.ReqlRuntimeError("RqlRuntimeError: Invalid time object constructed (no field `timezone`):\n"+JSON.stringify(date, null, 2))
  }
}
//TODO CamelCase the thing
util.dateToString = function(date) {
  var timezone = date.timezone;
  
  // Extract data from the timezone
  var timezone_array = date.timezone.split(':');
  var sign = timezone_array[0][0]; // Keep the sign
  timezone_array[0] = timezone_array[0].slice(1); // Remove the sign

  // Save the timezone in minutes
  var timezone_int = (parseInt(timezone_array[0], 10)*60+parseInt(timezone_array[1], 10))*60;
  if (sign === '-') {
    timezone_int = -1*timezone_int;
  }

  // d = real date with user's timezone
  var d = new Date(date.epoch_time*1000);

  // Add the user local timezone
  timezone_int += d.getTimezoneOffset()*60;

  // d_shifted = date shifted with the difference between the two timezones
  // (user's one and the one in the ReQL object)<commit_msg>Making the validation of identifier names match the spec and docs<commit_after>var util = {};
var Error = require(__dirname+"/../error.js");

var Minval = require(__dirname+"/../minval.js");
var Maxval = require(__dirname+"/../maxval.js");
var Asc = require(__dirname+"/../asc.js");
var Desc = require(__dirname+"/../desc.js");
var Literal = require(__dirname+"/../literal.js");
var ReqlGeometry;
var ReqlDate = require(__dirname+"/../date.js");
var protodef = require(__dirname+"/../protodef.js");
var constants = require(__dirname+"/../constants.js");
var GeographicLib = require(__dirname+"/../../external/geographiclib.js");
var termTypes = protodef.Term.TermType;
// Lazily loaded
// TODO Fix the dependencies
var Sequence;
var Table;
var Database;
var Selection;
var Document;
var MissingDoc;
var Group;
var Changes;


util.log = function(message) {
  if (constants.DEBUG === true) {
    console.log.apply(console.log, Array.prototype.slice.call(arguments, 0));
  }
}

util.isBuggy = function(query) {
  throw new Error.ReqlRuntimeError("Reqlite is buggy, this code should not be reached", query.frames)
}
util.notAvailable = function(message, query) {
  if (message == null) {
    throw new Error.ReqlRuntimeError("This method has not been implemented (yet) in Reqlite", query.frames)
  }
  else {
    throw new Error.ReqlRuntimeError(message, query.frames)
  }
}

util.isPlainObject = function isPlainObject(value) {
  return Object.prototype.toString.call(value) === '[object Object]';
}

util.typeOf = function(value, query) {
  //TODO Properly handle circular references
  if (Sequence === undefined) { Sequence = require(__dirname+"/../sequence.js"); }

  if (typeof value === "number") {
    return "NUMBER";
  }
  else if (typeof value === "string") {
    return "STRING";
  }
  else if (typeof value === "boolean") {
    return "BOOL";
  }
  else if (value === null) {
    return "NULL";
  }
  else if (typeof value.typeOf === "function") {
    // Covers TABLE, GROUP
    return value.typeOf();
  }
  else if (util.isDate(value)) {
    return "PTYPE<TIME>";
  }
  else if (util.isBinary(value)) {
    return "PTYPE<BINARY>";
  }
  else if (util.isSequence(value)) {
    return value.type; // Can be "ARRAY", "STREAM", etc.
  }
  else if (util.isDocument(value)) {
    return "SELECTION<OBJECT>";
  }
  else if (util.isObject(value)) {
    return "OBJECT";
  }
  else if (typeof value === 'function') {
    return "FUNCTION";
  }
  throw new Error.ReqlRuntimeError("Server is buggy, unknown type", query.frames)

}

util.assertJavaScriptResult = function(value, query) {
  if ((value !== undefined) && ((value === null)
      || util.isTable(value)
      || util.isSequence(value)
      || util.isSelection(value)
      || util.isDate(value)
      || util.isDocument(value)
      || util.isBinary(value)
      || util.isGeometry(value)
      || util.isAsc(value)
      || util.isDesc(value))) {
    return;
  }
  else if (util.isPlainObject(value)) {
    var keys = Object.keys(value);
    for(var i=0; i<keys.length; i++) {
     util.assertJavaScriptResult(value[keys[i]], query);
    }
  }
  else if (Array.isArray(value)) {
    for(var i=0; i<value.length; i++) {
     util.assertJavaScriptResult(value[i], query);
    }
  }
  else {
    util.assertFinite(value, query);
    util.assertDefined(value, query);
  }
}

util.assertFinite = function(value, query) {
  if ((value === Infinity)
      || (value === -Infinity)
      || (value !== value)) {
    throw new Error.ReqlRuntimeError('Number return value is not finite', query.frames)
  }
}

util.assertDefined = function(value, query) {
  if (value === undefined) {
    throw new Error.ReqlRuntimeError('Cannot convert javascript `undefined` to ql::datum_t', query.frames)
  }
  else if (value instanceof RegExp) {
    throw new Error.ReqlRuntimeError('Cannot convert RegExp to ql::datum_t', query.frames)
  }
}


util.nestedWriteForbidden = function(query) {
  throw new Error.ReqlRuntimeError('Cannot nest writes or meta ops in stream operations.  Use FOR_EACH instead', query.frames)
}

util.nonDeterministicOp = function(query) {
  // RethinkDB doesn't seem to keep track of the frames here..
  throw new Error.ReqlRuntimeError('Could not prove function deterministic.  Maybe you want to use the non_atomic flag?', [])
}

util.assertType = function assertType(value, type, query) {
  var typeValue;
  var finalError;

  typeValue = util.getType(value);
  if (Array.isArray(type)) {
    var valid = false;
    for(var i=0; i<type.length; i++) {
      var _type = type[i];
      try {
        util.assertTypeutil(_type, value, typeValue, query);
        valid = true;
        break;
      }
      catch(err) {
        finalError = err;
        // Ignore error, we will throw at the end of this if statement.
      }
    }
    if (valid !== true) {
      //TODO Hack for date.sub, are there other cases?
      //throw finalError;

      for(var i=0; i<type.length; i++) {
        if (type[i] === 'PTYPE<BINARY>') {
          type[i] = 'BINARY';
        }
      }
      throw new Error.ReqlRuntimeError('Expected '+type.join(' or ')+', but found '+typeValue, query.frames)
    }
  }
  else {
    util.assertTypeutil(type, value, typeValue, query)
  }
}

util.assertTime = function(date, query, backquote) {
  if (!(date instanceof ReqlDate)) {
    if (backquote === true) { // RethinkDB doesn't have consistent errors -_-
      throw new Error.ReqlRuntimeError('Not a TIME pseudotype: `'+JSON.stringify(date, null, 4)+'`', query.frames);
    }
    else {
      throw new Error.ReqlRuntimeError('Not a TIME pseudotype: '+JSON.stringify(date, null, 4), query.frames);
    }
  }
}

util.assertAttributes = function(value, attributes, query) {
  for(var i=0; i<attributes.length; i++) {
    if (value[attributes[i]] === undefined) {
      throw new Error.ReqlRuntimeError("No attribute `"+attributes[i]+"` in object:\n"+JSON.stringify(value, null, 4), query.frames)
    }
  }
}
util.assertCoordinatesLength = function(coordinates, query) {
  if (coordinates.length < 2) {
    throw new Error.ReqlRuntimeError('Too few coordinates.  Need at least longitude and latitude', query.frames)
  }
  else if (coordinates.length > 2) {
    throw new Error.ReqlRuntimeError('Too many coordinates.  GeoJSON position should have no more than three coordinates, but got '+coordinates.length, query.frames)
  }
}
util.assertPointCoordinates = function(value, query) {
  util.assertType(value, 'ARRAY', this);
  if (value.length !== 2) {
    throw new Error.ReqlRuntimeError("Expected point coordinate pair.  Got "+value.length+" element array instead of a 2 element one", query.frames)
  }
  util.assertType(value.get(0), 'NUMBER', query);
  util.assertType(value.get(1), 'NUMBER', query);
}

util.assertArity = function(expected, args, query) {
  var len;
  if (Array.isArray(args)) {
    len = args.length;
  }
  else {
    len = 0;
  }
  if (expected !== len) {
    util.arityError(expected, len, query);
  }
}
util.assertArityRange = function(min, max, args, query) {
  var len;
  if (Array.isArray(args)) {
    len = args.length;
  }
  else {
    len = 0;
  }

  if ((len < min) || (len > max)) {
    util.arityRangeError(min, max, len, query);
  }
}

util.arityError = function(expected, found, query) {
  if (expected === 1) {
    throw new Error.ReqlRuntimeError('Expected '+expected+' argument but found '+found, query.frames);
  }
  else {
    throw new Error.ReqlRuntimeError('Expected '+expected+' arguments but found '+found, query.frames);
  }
}

util.arityRangeError = function(min, max, found, query) {
  if (max === Infinity) {
    throw new Error.ReqlRuntimeError('Expected '+min+' or more arguments but found '+found, query.frames);
  }
  else {
    throw new Error.ReqlRuntimeError('Expected between '+min+' and '+max+' arguments but found '+found, query.frames);
  }
}

util.cannotPerformOp = function(operation, value, query) {
  throw new Error.ReqlRuntimeError('Cannot perform '+operation+' on a non-object non-sequence `'+JSON.stringify(value, null, 4)+'`', query.frames)
}


util.assertPreDatum = function(value, query) {
  if (util.isFunction(value)) {
    throw new Error.ReqlRuntimeError("Expected type DATUM but found FUNCTION:\nVALUE FUNCTION", query.frames)
  }
}
util.assertTypeutil = function(type, value, typeValue, query) {
  //TODO use util.getType
  if (type === 'DATUM') {
    if (!util.isDatum(value)) {
      if (util.isTable(value)) {
        throw new Error.ReqlRuntimeError('Expected type DATUM but found TABLE:\ntable("'+value.name+'")', query.frames)
      }
      else if (util.isSelection(value)) {
        throw new Error.ReqlRuntimeError('Expected type DATUM but found SELECTION:\nSELECTION ON table('+value.table.name+')', query.frames)
      }
      else if (util.isChanges(value)) {
        throw new Error.ReqlRuntimeError('Expected type DATUM but found SEQUENCE:\nVALUE SEQUENCE', query.frames)
      }
      else {
        throw new Error.ReqlRuntimeError("Expected type DATUM but found "+typeValue, query.frames)
      }

    }
  }
  else if (type === 'STRING') {
    if (typeof value !== 'string') {
      throw new Error.ReqlRuntimeError("Expected type STRING but found "+typeValue, query.frames)
    }
  }
  else if (type === 'NUMBER') {
    if (typeof value !== 'number') {
      throw new Error.ReqlRuntimeError("Expected type NUMBER but found "+typeValue, query.frames)
    }
  }
  else if (type === 'BOOL') {
    if (typeof value !== 'boolean') {
      throw new Error.ReqlRuntimeError("Expected type BOOL but found "+typeValue, query.frames)
    }
  }
  else if (type === "PTYPE<TIME>") {
    if (!util.isDate(value)) {
      throw new Error.ReqlRuntimeError("Expected type PTYPE<TIME> but found "+typeValue, query.frames)
    }
  }
  else if (type === "GEOMETRY") {
    if (!util.isGeometry(value)) {
      throw new Error.ReqlRuntimeError("Not a GEOMETRY pseudotype: `"+JSON.stringify(value, null, 4)+"`", query.frames)
    }
  }
  else if (type === "POLYGON") {
    if (!util.isGeometry(value)) {
      throw new Error.ReqlRuntimeError("Not a GEOMETRY pseudotype: `"+JSON.stringify(value, null, 4)+"`", query.frames)
    }
    else if (value.type !== 'Polygon') {
      throw new Error.ReqlRuntimeError("Expected geometry of type `Polygon` but found `"+value.type+"`", query.frames)
    }
  }
  else if (type === 'FUNCTION') {
    if (!util.isFunction(value)) {
      if (util.isDatum(value)) {
        typeValue = 'DATUM';
      }
      throw new Error.ReqlRuntimeError("Expected type FUNCTION but found "+typeValue+":\n"+JSON.stringify(util.toDatum(value)), query.frames)
    }
  }
  else if (type === 'GROUP') {
    if (!util.isGroup(value)) {
      if (util.isDatum(value)) {
        typeValue = 'DATUM';
      }
      throw new Error.ReqlRuntimeError("Expected type GROUPED_DATA but found "+typeValue+":\n"+JSON.stringify(util.toDatum(value), null, 4), query.frames)
    }
  }
  else if (type === "DATABASE") {
    if (Database === undefined) { Database = require(__dirname+"/../database.js"); }
    if (!(value instanceof Database)) {
      if (util.isDatum(value)) {
        typeValue = 'DATUM';
      }
      throw new Error.ReqlRuntimeError("Expected type DATABASE but found "+typeValue+":\n"+JSON.stringify(util.toDatum(value)), query.frames)
    }
  }
  else if (type === "TABLE_SLICE") {
    if (Table === undefined) { Table = require(__dirname+"/../table.js"); }
    if (Selection === undefined) { Selection = require(__dirname+"/../selection.js"); }
    if (!util.isTable(value) && !util.isSelection(value)) {
      if (util.isDatum(value)) {
        typeValue = 'DATUM';
      }
      throw new Error.ReqlRuntimeError("Expected type TABLE_SLICE but found "+typeValue+":\n"+JSON.stringify(util.toDatum(value)), query.frames)
    }

  }
  else if (type === "TABLE") {
    if (!util.isTable(value)) {
      if (util.isDatum(value)) {
        typeValue = 'DATUM';
      }
      throw new Error.ReqlRuntimeError("Expected type TABLE but found "+typeValue+":\n"+JSON.stringify(util.toDatum(value)), query.frames)
    }
  }
  else if (type === "SELECTION") {
    if (!util.isTable(value) && !util.isSelection(value) && !util.isDocument(value)) {
      if (util.isDatum(value)) {
        typeValue = 'DATUM';
      }
      throw new Error.ReqlRuntimeError("Expected type SELECTION but found "+typeValue+":\n"+JSON.stringify(util.toDatum(value), null, 4), query.frames)
    }
  }
  else if (type === "ARRAY") {
    if (!util.isSequence(value)) {
      throw new Error.ReqlRuntimeError("Expected type ARRAY but found "+typeValue, query.frames)
    }
  }
  else if (type === "PTYPE<BINARY>") {
    if (!util.isBinary(value)) {
      throw new Error.ReqlRuntimeError("Expected type BINARY but found "+typeValue, query.frames)
    }
  }
  else if (type === "INT") {
    var floored = Math.floor(value);
    if (floored !== value) {
      throw new Error.ReqlRuntimeError("Number not an integer: "+value, query.frames)
    }
  }
  else if (type === "OBJECT") {
    if (!util.isObject(value)) {
      throw new Error.ReqlRuntimeError("Expected type OBJECT but found "+typeValue, query.frames)
    }
  }
}

util.getType = function(value) {
  if (Sequence === undefined) { Sequence = require(__dirname+"/../sequence.js"); }
  if (Table === undefined) { Table = require(__dirname+"/../table.js"); }

  if (value instanceof Table) {
    return "TABLE";
  }
  else if (value instanceof Sequence) {
    return "ARRAY";
  }
  else if (value === null) {
    return "NULL"
  }
  else if (util.isDate(value)) {
    return "PTYPE<TIME>";
  }
  else if (util.isBinary(value)) {
    return "PTYPE<BINARY>";
  }
  else if (value instanceof Maxval) {
    return "MAXVAL";
  }
  else if (value instanceof Minval) {
    return "MINVAL";
  }
  else if (util.isPlainObject(value)) {
    return "OBJECT";
  }
  else {
    return (typeof value).toUpperCase();
  }
}

util.assertOptions = function(options, allowedKeys, query) {
  var isAllowedKey = {};
  for(var i=0; i<allowedKeys.length; i++) {
    isAllowedKey[allowedKeys[i]] = true;
  }

  var keys = Object.keys(options);
  for(var i=0; i<keys.length; i++) {
    if (isAllowedKey[keys[i]] !== true) {
      throw new Error.ReqlRuntimeError('Unrecognized optional argument `'+keys[i]+'`', query.frames)
    }
  }
}

util.assertNoSpecialChar = function assertNoSpecialChar(value, type, query) {
  if (!value.match(/^[A-Za-z0-9_]+$/)) {
    throw new Error.ReqlRuntimeError(type+" name `"+value+"` invalid (Use A-Za-z0-9_ only)", query.frames)
  }
}

util.s4 = function s4() {
  return Math.floor((1+Math.random())*0x10000).toString(16).substring(1);
};

util.uuid = function uuid() {
  return util.s4()+util.s4()+"-"+util.s4()+"-"+util.s4()+"-"+util.s4()+"-"+util.s4()+util.s4()+util.s4();
}

util.writeResult = function writeResult() {
  return {
    deleted: 0,
    errors: 0,
    inserted: 0,
    replaced: 0,
    skipped: 0,
    unchanged: 0
  }
}

// Merging write result in place
util.mergeWriteResult = function(left, right) {
  left.deleted += right.deleted;
  left.errors += right.errors;
  left.inserted += right.inserted;
  left.replaced += right.replaced;
  left.skipped += right.skipped;
  left.unchanged += right.unchanged;
  if (left.generated_keys !== undefined || right.generated_keys !== undefined) {
    if (Array.isArray(left.generated_keys) && Array.isArray(right.generated_keys)) {
      left.generated_keys = left.generated_keys.concat(right.generated_keys);
    }
    else if (Array.isArray(right.generated_keys)) {
      left.generated_keys = right.generated_keys;
    }
  }
  if ((left.first_error === undefined) && (right.first_error !== undefined)) {
    left.first_error = right.first_error;
  }
  if (util.isSequence(right.changes) && right.changes.length > 0) {
    if (!util.isSequence(left.changes)) {
      left.changes = new Sequence();
    }
    for(var i=0; i<right.changes.length; i++) {
      left.changes.push(right.changes.get(i))
    }
  }
}

util.merge = function(self, toMerge, query, internalOptions) {
  var varId;
  if (util.isFunction(toMerge)) {
    varId = util.getVarId(toMerge);
    query.context[varId] = self;
  }

  toMerge = query.evaluate(toMerge, internalOptions);

  if (varId !== undefined) {
    delete query.context[varId];
  }
  return util.mergeDatum(self, toMerge);
}
util.mergeDatum = function merge(self, obj) {
  if (!util.isPlainObject(obj)) {
    return obj;
  }
  if (!util.isPlainObject(self)) {
    return obj;
  }

  //A non in place merge, used for the ReQL `merge` command
  var result = util.deepCopy(self);
  for(var key in obj) {
    // Recursively merge only if both fields are objects, else we'll overwrite the field
    if (obj[key] instanceof Literal) {
      result[key] = obj[key].value;
    }
    else if (util.isPlainObject(obj[key]) && util.isPlainObject(result[key])) {
      result[key] = util.mergeDatum(result[key], obj[key]);
    }
    else {
      result[key] = obj[key];
    }
  }
  return result;
}
util._merge = function _merge(self, obj) {
  // Inplace merge (behave like the `update` command.
  // Return whether `self` has been changed
  var changed = false;
  for(var key in obj) {
    // Recursively merge only if both fields are objects, else we'll overwrite the field
    if (obj[key] instanceof Literal) {
      changed = true;
      if (util.eq(self[key], obj[key].value)) {
        changed = false;
      }
      self[key] = obj[key].value;
    }
    else if (util.isPlainObject(obj[key]) && util.isPlainObject(self[key])) {
      changed = util._merge.call(self[key], obj[key]) || changed;
    }
    else {
      if (Array.isArray(self[key])) {
        if (Array.isArray(obj[key])) {
          if (self[key].length !== obj[key].length) {
            changed = true;
          }
          else {
            for(var i=0; i<self[key].length; i++) {
              if ((util.isPlainObject(self[key][i])) && (util.isPlainObject(obj[key][i]))) {
                changed = util._merge(self[key][i], obj[key][i]) || changed;
              }
              else {
                changed = true;
                self[key][i] = util.deepCopy(obj[key][i]);
              }
            }
          }
        }
        else {
          changed = true;
        }
      }
      else if (self[key] !== obj[key]) {
        changed = true;
      }
      self[key] = obj[key];
    }
  }
  return changed
}

util._replace = function replace(self, obj) {
  // Inplace replace (behave like the `replace` command.
  // Return whether `self` has been changed
  var changed = false;
  for(var key in self) {
    if (obj[key] === undefined) {
      delete self[key];
      changed = true;
    }
  }
  for(var key in obj) {
    // Recursively merge only if both fields are objects, else we'll overwrite the field
    if (util.isPlainObject(obj[key]) && util.isPlainObject(self[key])) {
      changed = util.replaceecall(self[key], obj[key]) || changed;
    }
    else {
      if (Array.isArray(self[key])) {
        if (Array.isArray(obj[key])) {
          if (self[key].length !== obj[key].length) {
            changed = true;
          }
          else {
            for(var i=0; i<self[key].length; i++) {
              //TODO If self[ley][i] is not an object, it will break
              if ((util.isPlainObject(self[key][i])) && (util.isPlainObject(obj[key][i]))) {
                changed = util.replace(self[key][i], obj[key][i]) || changed;
              }
              else if (util.isPlainObject(self[key][i])) {
                changed = true;
                self[key][i];
              }
            }
          }
        }
        else {
          changed = true;
        }
      }
      else if (self[key] !== obj[key]) {
        changed = true;
      }
      self[key] = obj[key];
    }
  }
  return changed
}

util.makeInternalPk = function(value) {
  // Build the internal prinary key we use in the hash table
  if (typeof value === 'string') {
    return "string_"+value
  }
  else if (typeof value === 'number') {
    return "number_"+value
  }
  else if (typeof value === 'boolaen') {
    return "boolean_"+value
  }
  else if (util.isDate(value)) {
    util.validDate(value);
    return "date_"+value.epoch_time+"_"+value.timezone;
  }
  else if (util.isBinary(value)) {
    return "binary_"+value.data
  }
  else {
    var type = util.typeOf(value);
    throw new Error.ReqlRuntimeError("Primary keys must be either a number, string, bool, pseudotype or array (got type "+type+"):\n"+JSON.stringify(value, null, 2));
  }

}
util.validDate = function(date) {
  //TODO use moment
  // `date` should be an object with the field $reql_type$ mapping to "TIME"
  if (typeof date.epoch_time !== "number") {
    throw new Error.ReqlRuntimeError("RqlRuntimeError: Invalid time object constructed (no field `epoch_time`):\n"+JSON.stringify(date, null, 2))
  }
  else if (date.timezone.match(/^(?:Z|[+-](?:2[0-3]|[01][0-9]):[0-5][0-9])$/) == null) {
    throw new Error.ReqlRuntimeError("RqlRuntimeError: Invalid time object constructed (no field `timezone`):\n"+JSON.stringify(date, null, 2))
  }
}
//TODO CamelCase the thing
util.dateToString = function(date) {
  var timezone = date.timezone;

  // Extract data from the timezone
  var timezone_array = date.timezone.split(':');
  var sign = timezone_array[0][0]; // Keep the sign
  timezone_array[0] = timezone_array[0].slice(1); // Remove the sign

  // Save the timezone in minutes
  var timezone_int = (parseInt(timezone_array[0], 10)*60+parseInt(timezone_array[1], 10))*60;
  if (sign === '-') {
    timezone_int = -1*timezone_int;
  }

  // d = real date with user's timezone
  var d = new Date(date.epoch_time*1000);

  // Add the user local timezone
  timezone_int += d.getTimezoneOffset()*60;

  // d_shifted = date shifted with the difference between the two timezones
  // (user's one and the one in the ReQL object)
{'fraction_non_alphanumeric': 0.15536340909612165, 'fraction_numerical': 0.004357698217930781, 'mean_word_length': 3.124680730299877, 'pattern_counts': {'":': 12, '<': 45, '<?xml version=': 0, '>': 27, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1953179', 'n_tokens_mistral': 14433, 'n_tokens_neox': 14240, 'n_words': 3477}