text: "use strict";
  /**
   * @license
   * Copyright 2018 Google LLC
   *
   * Use of this source code is governed by an MIT-style
   * license that can be found in the LICENSE file or at
   * https://opensource.org/licenses/MIT.
   * =============================================================================
   */
  var __assign = (this && this.__assign) || function () {
      __assign = Object.assign || function(t) {
          for (var s, i = 1, n = arguments.length; i < n; i++) {
              s = arguments[i];
              for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
                  t[p] = s[p];
          }
          return t;
      };
      return __assign.apply(this, arguments);
  };
  Object.defineProperty(exports, "__esModule", { value: true });
  /* Original source: utils/generic_utils.py */
  var tfjs_core_1 = require("@tensorflow/tfjs-core");
  var errors_1 = require("../errors");
  // tslint:enable
  /**
   * If `value` is an Array, equivalent to Python's `value * numValues`.
   * If `value` is not an Array, equivalent to Python's `[value] * numValues`
   */
  // tslint:disable-next-line:no-any
  function pyListRepeat(value, numValues) {
      if (Array.isArray(value)) {
          // tslint:disable-next-line:no-any
          var newArray = [];
          for (var i = 0; i < numValues; i++) {
              newArray = newArray.concat(value);
          }
          return newArray;
      }
      else {
          var newArray = new Array(numValues);
          newArray.fill(value);
          return newArray;
      }
  }
  exports.pyListRepeat = pyListRepeat;
  function assert(val, message) {
      if (!val) {
          throw new errors_1.AssertionError(message);
      }
  }
  exports.assert = assert;
  /**
   * Count the number of elements of the `array` that are equal to `reference`.
   */
  function count(array, refernce) {
      var counter = 0;
      for (var _i = 0, array_1 = array; _i < array_1.length; _i++) {
          var item = array_1[_i];
          if (item === refernce) {
              counter++;
          }
      }
      return counter;
  }
  exports.count = count;
  /**
   * If an array is of length 1, just return the first element. Otherwise, return
   * the full array.
   * @param tensors
   */
  function singletonOrArray(xs) {
      if (xs.length === 1) {
          return xs[0];
      }
      return xs;
  }
  exports.singletonOrArray = singletonOrArray;
  /**
   * Normalizes a list/tensor into a list.
   *
   * If a tensor is passed, we return
   * a list of size 1 containing the tensor.
   *
   * @param x target object to be normalized.
   */
  // tslint:disable-next-line:no-any
  function toList(x) {
      if (Array.isArray(x)) {
          return x;
      }
      return [x];
  }
  exports.toList = toList;
  /**
   * Generate a UID for a list
   */
  // tslint:disable-next-line:no-any
  function objectListUid(objs) {
      var objectList = toList(objs);
      var retVal = '';
      for (var _i = 0, objectList_1 = objectList; _i < objectList_1.length; _i++) {
          var obj = objectList_1[_i];
          if (obj.id == null) {
              throw new errors_1.ValueError("Object " + obj + " passed to objectListUid without an id");
          }
          if (retVal !== '') {
              retVal = retVal + ', ';
          }
          retVal = retVal + Math.abs(obj.id);
      }
      return retVal;
  }
  exports.objectListUid = objectListUid;
  /**
   * Converts string to snake-case.
   * @param name
   */
  function toSnakeCase(name) {
      var intermediate = name.replace(/(.)([A-Z][a-z0-9]+)/g, '$1_$2');
      var insecure = intermediate.replace(/([a-z])([A-Z])/g, '$1_$2').toLowerCase();
      /*
       If the class is private the name starts with "_" which is not secure
       for creating scopes. We prefix the name with "private" in this case.
       */
      if (insecure[0] !== '_') {
          return insecure;
      }
      return 'private' + insecure;
  }
  exports.toSnakeCase = toSnakeCase;
  function toCamelCase(identifier) {
      // quick return for empty string or single character strings
      if (identifier.length <= 1) {
          return identifier;
      }
      // Check for the underscore indicating snake_case
      if (identifier.indexOf('_') === -1) {
          return identifier;
      }
      return identifier.replace(/[_]+(\w|$)/g, function (m, p1) { return p1.toUpperCase(); });
  }
  exports.toCamelCase = toCamelCase;
  // tslint:disable-next-line:no-any
  var _GLOBAL_CUSTOM_OBJECTS = {};
  function serializeKerasObject(instance) {
      if (instance === null || instance === undefined) {
          return null;
      }
      var dict = {};
      dict['className'] = instance.getClassName();
      dict['config'] = instance.getConfig();
      return dict;
  }
  exports.serializeKerasObject = serializeKerasObject;
  /**
   * Replace ndarray-style scalar objects in serialization objects with numbers.
   *
   * Background: In some versions of tf.keras, certain scalar values in the HDF5
   * model save file can be serialized as: `{'type': 'ndarray', 'value': num}`,
   * where in `num` is a plain number. This method converts such serialization
   * to a `number`.
   *
   * @param config The keras-format serialization object to be processed
   *   (in place).
   */
  function convertNDArrayScalarsInConfig(config) {
      if (config == null || typeof config !== 'object') {
          return;
      }
      else if (Array.isArray(config)) {
          config.forEach(function (configItem) { return convertNDArrayScalarsInConfig(configItem); });
      }
      else {
          var fields = Object.keys(config);
          for (var _i = 0, fields_1 = fields; _i < fields_1.length; _i++) {
              var field = fields_1[_i];
              var value = config[field];
              if (value != null && typeof value === 'object') {
                  if (!Array.isArray(value) && value['type'] === 'ndarray' &&
                      typeof value['value'] === 'number') {
                      config[field] = value['value'];
                  }
                  else {
                      convertNDArrayScalarsInConfig(value);
                  }
              }
          }
      }
  }
  /**
   * Deserialize a saved Keras Object
   * @param identifier either a string ID or a saved Keras dictionary
   * @param moduleObjects a list of Python class names to object constructors
   * @param customObjects a list of Python class names to object constructors
   * @param printableModuleName debug text for the object being reconstituted
   * @param fastWeightInit Optional flag to use fast weight initialization
   *   during deserialization. This is applicable to cases in which
   *   the initialization will be immediately overwritten by loaded weight
   *   values. Default: `false`.
   * @returns a TensorFlow.js Layers object
   */
  // tslint:disable:no-any
  function deserializeKerasObject(identifier, moduleObjects, customObjects, printableModuleName, fastWeightInit) {
      if (moduleObjects === void 0) { moduleObjects = {}; }
      if (customObjects === void 0) { customObjects = {}; }
      if (printableModuleName === void 0) { printableModuleName = 'object'; }
      if (fastWeightInit === void 0) { fastWeightInit = false; }
      var _a, _b, _c;
      // tslint:enable
      if (typeof identifier === 'string') {
          var functionName = identifier;
          var fn = void 0;
          if (functionName in customObjects) {
              fn = customObjects[functionName];
          }
          else if (functionName in _GLOBAL_CUSTOM_OBJECTS) {
              fn = _GLOBAL_CUSTOM_OBJECTS[functionName];
          }
          else {
              fn = moduleObjects[functionName];
              if (fn == null) {
                  throw new errors_1.ValueError("Unknown " + printableModuleName + ": " + identifier + ". " +
                      "This may be due to one of the following reasons:\n" +
                      ("1. The " + printableModuleName + " is defined in Python, in which ") +
                      "case it needs to be ported to TensorFlow.js or your JavaScript " +
                      "code.\n" +
                      ("2. The custom " + printableModuleName + " is defined in JavaScript, ") +
                      "but is not registered properly with " +
                      "tf.serialization.registerClass().");
                  // TODO(cais): Add link to tutorial page on custom layers.
              }
          }
          return fn;
      }
      else {
          // In this case we are dealing with a Keras config dictionary.
          var config = identifier;
          if (config['className'] == null || config['config'] == null) {
              throw new errors_1.ValueError(printableModuleName + ": Improper config format: " +
                  (JSON.stringify(config) + ".\n") +
                  "'className' and 'config' must set.");
          }
          var className = config['className'];
          var cls = void 0, fromConfig = void 0;
          if (className in customObjects) {
              _a = customObjects[className], cls = _a[0], fromConfig = _a[1];
          }
          else if (className in _GLOBAL_CUSTOM_OBJECTS) {
              _b = _GLOBAL_CUSTOM_OBJECTS['className'], cls = _b[0], fromConfig = _b[1];
          }
          else if (className in moduleObjects) {
              _c = moduleObjects[className], cls = _c[0], fromConfig = _c[1];
          }
          if (cls == null) {
              throw new errors_1.ValueError("Unknown " + printableModuleName + ": " + className + ". " +
                  "This may be due to one of the following reasons:\n" +
                  ("1. The " + printableModuleName + " is defined in Python, in which ") +
                  "case it needs to be ported to TensorFlow.js or your JavaScript " +
                  "code.\n" +
                  ("2. The custom " + printableModuleName + " is defined in JavaScript, ") +
                  "but is not registered properly with " +
                  "tf.serialization.registerClass().");
              // TODO(cais): Add link to tutorial page on custom layers.
          }
          if (fromConfig != null) {
              // Porting notes: Instead of checking to see whether fromConfig accepts
              // customObjects, we create a customObjects dictionary and tack it on to
              // config['config'] as config['config'].customObjects. Objects can use it,
              // if they want.
              // tslint:disable-next-line:no-any
              var customObjectsCombined = {};
              for (var _i = 0, _d = Object.keys(_GLOBAL_CUSTOM_OBJECTS); _i < _d.length; _i++) {
                  var key = _d[_i];
                  customObjectsCombined[key] = _GLOBAL_CUSTOM_OBJECTS[key];
              }
              for (var _e = 0, _f = Object.keys(customObjects); _e < _f.length; _e++) {
                  var key = _f[_e];
                  customObjectsCombined[key] = customObjects[key];
              }
              // Add the customObjects to config
              var nestedConfig = config['config'];
              nestedConfig['customObjects'] = customObjectsCombined;
              var backupCustomObjects = __assign({}, _GLOBAL_CUSTOM_OBJECTS);
              for (var _g = 0, _h = Object.keys(customObjects); _g < _h.length; _g++) {
                  var key = _h[_g];
                  _GLOBAL_CUSTOM_OBJECTS[key] = customObjects[key];
              }
              convertNDArrayScalarsInConfig(config['config']);
              var returnObj = fromConfig(cls, config['config'], customObjects, fastWeightInit);
              _GLOBAL_CUSTOM_OBJECTS = __assign({}, backupCustomObjects);
              return returnObj;
          }
          else {
              // Then `cls` may be a function returning a class.
              // In this case by convention `config` holds
              // the kwargs of the function.
              var backupCustomObjects = __assign({}, _GLOBAL_CUSTOM_OBJECTS);
              for (var _j = 0, _k = Object.keys(customObjects); _j < _k.length; _j++) {
                  var key = _k[_j];
                  _GLOBAL_CUSTOM_OBJECTS[key] = customObjects[key];
              }
              // In python this is **config['config'], for tfjs-layers we require
              // classes that use this fall-through construction method to take
              // a config interface that mimics the expansion of named parameters.
              var returnObj = new cls(config['config']);
              _GLOBAL_CUSTOM_OBJECTS = __assign({}, backupCustomObjects);
              return returnObj;
          }
      }
  }
  exports.deserializeKerasObject = deserializeKerasObject;
  /**
   * Compares two numbers for sorting.
   * @param a
   * @param b
   */
  function numberCompare(a, b) {
      return (a < b) ? -1 : ((a > b) ? 1 : 0);
  }
  exports.numberCompare = numberCompare;
  /**
   * Comparison of two numbers for reverse sorting.
   * @param a
   * @param b
   */
  function reverseNumberCompare(a, b) {
      return -1 * numberCompare(a, b);
  }
  exports.reverseNumberCompare = reverseNumberCompare;
  /**
   * Convert a string into the corresponding DType.
   * @param dtype
   * @returns An instance of DType.
   */
  function stringToDType(dtype) {
      switch (dtype) {
          case 'float32':
              return 'float32';
          default:
              throw new errors_1.ValueError("Invalid dtype: " + dtype);
      }
  }
  exports.stringToDType = stringToDType;
  /**
   * Test the element-by-element equality of two Arrays of strings.
   * @param xs First array of strings.
   * @param ys Second array of strings.
   * @returns Wether the two arrays are all equal, element by element.
   */
  function stringsEqual(xs, ys) {
      if (xs == null || ys == null) {
          return xs === ys;
      }
      if (xs.length !== ys.length) {
          return false;
      }
      for (var i = 0; i < xs.length; ++i) {
          if (xs[i] !== ys[i]) {
              return false;
          }
      }
      return true;
  }
  exports.stringsEqual = stringsEqual;
  /**
   * Get the unique elements of an array.
   * @param xs Array.
   * @returns An Array consisting of the unique elements in `xs`.
   */
  function unique(xs) {
      if (xs == null) {
          return xs;
      }
      var out = [];
      // TODO(cais): Maybe improve performance by sorting.
      for (var _i = 0, xs_1 = xs; _i < xs_1.length; _i++) {
          var x = xs_1[_i];
          if (out.indexOf(x) === -1) {
              out.push(x);
          }
      }
      return out;
  }
  exports.unique = unique;
  /**
   * Determine if an Object is empty (i.e., does not have own properties).
   * @param obj Object
   * @returns Whether the Object is empty.
   * @throws ValueError: If object is `null` or `undefined`.
   */
  function isObjectEmpty(obj) {
      if (obj == null) {
          throw new errors_1.ValueError("Invalid value in obj: " + JSON.stringify(obj));
      }
      for (var key in obj) {
          if (obj.hasOwnProperty(key)) {
              return false;
          }
      }
      return true;
  }
  exports.isObjectEmpty = isObjectEmpty;
  /**
   * Helper function used to build type union/enum run-time checkers.
   * @param values The list of allowed values.
   * @param label A string name for the type
   * @param value The value to test.
   * @throws ValueError: If the value is not in values nor `undefined`/`null`.
   */
  function checkStringTypeUnionValue(values, label, value) {
      if (value == null) {
          return;
      }
      if (values.indexOf(value) < 0) {
          throw new errors_1.ValueError(value + " is not a valid " + label + ".  Valid values are " + values + " or null/undefined.");
      }
  }
  exports.checkStringTypeUnionValue = checkStringTypeUnionValue;
  /**
   * Helper function for verifying the types of inputs.
   *
   * Ensures that the elements of `x` are all of type `expectedType`.
   * Also verifies that the length of `x` is within bounds.
   *
   * @param x Object to test.
   * @param expectedType The string expected type of all of the elements in the
   * Array.
   * @param minLength Return false if x.length is less than this.
   * @param maxLength Return false if x.length is greater than this.
   * @returns true if and only if `x` is an `Array<expectedType>` with
   * length >= `minLength` and <= `maxLength`.
   */
  // tslint:disable:no-any
  function checkArrayTypeAndLength(x, expectedType, minLength, maxLength) {
      if (minLength === void 0) { minLength = 0; }
      if (maxLength === void 0) { maxLength = Infinity; }
      assert(minLength >= 0);
      assert(maxLength >= minLength);
      return (Array.isArray(x) && x.length >= minLength && x.length <= maxLength &&
          x.every(function (e) { return typeof e === expectedType; }));
  }
  exports.checkArrayTypeAndLength = checkArrayTypeAndLength;
  // tslint:enable:no-any
  /**
   * Assert that a value or an array of value are positive integer.
   *
   * @param value The value being asserted on. May be a single number or an array
   *   of numbers.
   * @param name Name of the value, used to make the error message.
   */
  function assertPositiveInteger(value, name) {
      if (Array.isArray(value)) {
          tfjs_core_1.util.assert(value.length > 0, function () { return name + " is unexpectedly an empty array."; });
          value.forEach(function (v, i) { return assertPositiveInteger(v, "element " + (i + 1) + " of " + name); });
      }
      else {
          tfjs_core_1.util.assert(Number.isInteger(value) && value > 0, function () { return "Expected " + name + " to be a positive integer, but got " +
              (formatAsFriendlyString(value) + "."); });
      }
  }
  exports.assertPositiveInteger = assertPositiveInteger;
  /**
   * Format a value into a display-friendly, human-readable fashion.
   *
   * - `null` is formatted as `'null'`
   * - Strings are formated with flanking pair of quotes.
   * - Arrays are formatted with flanking pair of square brackets.
   *
   * @param value The value to display.
   * @return Formatted string.
   */
  // tslint:disable-next-line:no-any
  function formatAsFriendlyString(value) {
      if (value === null) {
          return 'null';
      }
      else if (Array.isArray(value)) {
          return '[' + value.map(function (v) { return formatAsFriendlyString(v); }).join(',') + ']';
      }
      else if (typeof value === 'string') {
          return "\"" + value + "\"";
      }
      else {
          return "" + value;
      }
  }
  exports.formatAsFriendlyString = formatAsFriendlyString;
  /**
   * Returns a function `f2` (decorator) which wraps the original function
   * `f`. `f2` guarantees that `f` can be called at most once
   * every `waitMs` ms. If `f2` is called more often, it will return
   * the last returned result of `f`.
   *
   * @param f The original function `f` to wrap.
   * @param waitMs The time between two consecutive calls to `f` in ms.
   */
  function debounce(f, waitMs) {
      var lastTime = tfjs_core_1.util.now();
      var lastResult;
      var f2 = function () {
          var args = [];
          for (var _i = 0; _i < arguments.length; _i++) {
              args[_i] = arguments[_i];
          }
          var now = tfjs_core_1.util.now();
          if (now - lastTime < waitMs) {
              return lastResult;
          }
          lastTime = now;
          lastResult = f.apply(void 0, args);
          return lastResult;
      };
      return f2;
  }
  exports.debounce = debounce;
  //# sourceMappingURL=generic_utils.js.map