<commit_before>var $ = require('jquery');
var _ = require('underscore');
var Backbone = require('backbone');
var LayerDefinitionModel = require('./layer-definition-model');
var layerLetters = require('./layer-letters');
var layerColors = require('./layer-colors');
var nodeIds = require('../value-objects/analysis-node-ids');
var layerTypesAndKinds = require('./layer-types-and-kinds');
var SQLUtils = require('../helpers/sql-utils');
var TableNameUtils = require('../helpers/table-name-utils');
var checkAndBuildOpts = require('../helpers/required-opts');
var REQUIRED_OPTS = [
  'analysisDefinitionNodesCollection',
  'configModel',
  'mapId',
  'stateDefinitionModel',
  'userModel'
];

var generateLabelsLayerName = function (layerName) {
  return layerName + ' Labels';
};

/**
 * Collection of layer definitions
 */
module.exports = Backbone.Collection.extend({

  initialize: function (models, opts) {
    checkAndBuildOpts(opts, REQUIRED_OPTS, this);
  },

  url: function () {
    var baseUrl = this._configModel.get('base_url');
    return baseUrl + '/api/v1/maps/' + this._mapId + '/layers';
  },

  parse: function (r) {
    return r.layers;
  },

  save: function (options) {
    this.each(function (layerDefModel, index) {
      layerDefModel.set('order', index, { silent: true });
    });
    Backbone.sync('update', this, options);
  },

  toJSON: function () {
    return {
      layers: Backbone.Collection.prototype.toJSON.apply(this, arguments)
    };
  },

  /**
   * Intended to be called from entry point, to make sure initial layers are taken into account
   */
  resetByLayersData: function (layersData) {
    this.reset(layersData, {
      silent: true,
      initialLetters: _.chain(layersData)
        .pluck('options')
        .pluck('letter')
        .compact()
        .value()
    });
  },

  comparator: function (m) {
    return m.get('order');
  },

  model: function (d, opts) {
    var self = opts.collection;

    // Add data required for new editor if not set (e.g. a vis created on old editor doesn't contain letter and source)
    var o = _.clone(d.options) || {};
    var attrs = _.defaults(
      { options: o },
      _.omit(d, ['options']
    ));

    if (!isNaN(opts.at)) {
      attrs.options.order = opts.at;
    }

    if (layerTypesAndKinds.isKindDataLayer(attrs.kind)) {
      o.letter = o.letter || layerLetters.next(self._takenLetters(opts.initialLetters));
      o.color = o.color || layerColors.getColorForLetter(o.letter);

      // Create source attr if it does not already exist
      var sourceId = nodeIds.next(o.letter);

      if (o.table_name && (!o.source || o.source === sourceId)) {
        var tableName = TableNameUtils.getUnqualifiedName(o.table_name);
        var userName = o.user_name || TableNameUtils.getUsername(o.table_name);

        var qualifyTableName = (userName && self._configModel.get('user_name') !== userName) || self._userModel.isInsideOrg();
        tableName = TableNameUtils.getQualifiedTableName(o.table_name, userName, qualifyTableName);

        o.source = o.source || sourceId;
        o.query = o.query || SQLUtils.getDefaultSQLFromTableName(tableName);

        // Add analysis definition unless already existing
        self._analysisDefinitionNodesCollection.createSourceNode({
          id: sourceId,
          tableName: tableName,
          query: o.query
        });
      }
    }

    var parseAttrs = typeof opts.parse === 'boolean' ? opts.parse : true;
    var m = new LayerDefinitionModel(attrs, {
      parse: parseAttrs,
      collection: self,
      configModel: self._configModel
    });

    return m;
  },

  nextLetter: function () {
    return layerLetters.next(this._takenLetters());
  },

  findAnalysisDefinitionNodeModel: function (id) {
    return this._analysisDefinitionNodesCollection.get(id);
  },

  findOwnerOfAnalysisNode: function (nodeDefModel) {
    return this.find(function (layerDefModel) {
      return layerDefModel.isOwnerOfAnalysisNode(nodeDefModel);
    });
  },

  findPrimaryParentLayerToAnalysisNode: function (nodeDefModel, opts) {
    if (!nodeDefModel) return;

    opts = opts || {};
    if (!_.isArray(opts.exclude)) {
      opts.exclude = [opts.exclude];
    }
    var owner = this.findOwnerOfAnalysisNode(nodeDefModel);

    return this.find(function (layerDefModel) {
      if (layerDefModel === owner) return;
      if (_.contains(opts.exclude, layerDefModel)) return;

      var layerHeadNode = layerDefModel.getAnalysisDefinitionNodeModel();
      if (nodeDefModel === layerHeadNode) return true;

      if (layerHeadNode) {
        var primarySourceOfLastOwnNode = _.last(layerHeadNode.linkedListBySameLetter()).getPrimarySource();
        return nodeDefModel === primarySourceOfLastOwnNode;
      }
    });
  },

  isThereAnyGeometryData: function () {
    return this.filter(function (layerDefModel) {
      return layerDefModel.isDataLayer();
    }).some(function (layerDefModel) {
      var queryGeometryModel = layerDefModel.getAnalysisDefinitionNodeModel().queryGeometryModel;
      return queryGeometryModel && queryGeometryModel.hasValue();
    });
  },

  loadAllQueryGeometryModels: function (callback) {
    var promises = this.filter(function (layerDefModel) {
      return layerDefModel.isDataLayer();
    }).map(function (layerDefModel) {
      var queryGeometryModel = layerDefModel.getAnalysisDefinitionNodeModel().queryGeometryModel;
      var status = queryGeometryModel.get('status');
      var deferred = new $.Deferred();

      if (queryGeometryModel.attributes.query.indexOf("_raster") > 0 || queryGeometryModel.isFetched()) {
        deferred.resolve();
      } else if (queryGeometryModel.canFetch()) {
        if (status !== 'fetching') {
          queryGeometryModel.fetch({
            success: function () {
              deferred.resolve();
            },
            error: function () {
              deferred.reject();
            }
          });
        } else {
          deferred.resolve();
        }
      } else {
        deferred.reject();
      }

      return deferred.promise();
    }, this);

    $.when.apply($, promises).done(callback);
  },

  isThereAnyTorqueLayer: function () {
    return this.any(function (m) {
      return layerTypesAndKinds.isTorqueType(m.get('type'));
    });
  },

  isThereAnyCartoDBLayer: function () {
    return this.any(function (m) {
      return layerTypesAndKinds.isCartoDBType(m.get('type'));
    });
  },

  anyContainsNode: function (nodeDefModel) {
    return this.any(function (layerDefModel) {
      return layerDefModel.containsNode(nodeDefModel);
    });
  },

  isDataLayerOnTop: function (lyrModel) {
    var currentPos = this.indexOf(lyrModel);
    return currentPos === this.getTopDataLayerIndex();
  },

  getTopDataLayerIndex: function () {
    var layerOnTop = this.getLayerOnTop();
    var at = this.indexOf(layerOnTop);
    var hasLabels = layerOnTop &&
      !layerTypesAndKinds.isCartoDBType(layerOnTop.get('type')) &&
      !layerTypesAndKinds.isTorqueType(layerOnTop.get('type'));

    if (hasLabels) { at--; }

    return at;
  },

  getNumberOfDataLayers: function () {
    return this.select(function (model) {
      return layerTypesAndKinds.isTypeDataLayer(model.get('type'));
    }).length;
  },

  _takenLetters: function (otherLetters) {
    var valuesFromAddedModels = _.compact(this.pluck('letter'));

    // When adding multiple items the models created so far are stored in the internal object this._byId,
    // need to make sure to take them into account when returning already taken letter.
    var valuesFromModelsNotYetAdded = _.chain(this._byId).values().invoke('get', 'letter').value();

    return _.union(valuesFromAddedModels, valuesFromModelsNotYetAdded, otherLetters);
  },

  setBaseLayer: function (newBaseLayerAttrs) {
    this.trigger('changingBaseLayer');

    newBaseLayerAttrs = _.clone(newBaseLayerAttrs);
    if (this.isBaseLayerAdded(newBaseLayerAttrs)) {
      this.trigger('baseLayerChanged');
      return false;
    }

    var newBaseLayerHasLabels = newBaseLayerAttrs.labels && newBaseLayerAttrs.labels.url;

    var labelsLayerOptions = {
      silent: true,
      wait: true, // do not add/remove the layer unless it's created/removed/updated successfully
      success: function () {
        this.trigger('baseLayerChanged');
      }.bind(this),
      error: function () {
        this.trigger('baseLayerFailed');
      }.bind(this)
    };

    var baseLayerOptions = {
      silent: true,
      wait: true, // do not update the layer unless it's created/updated successfully
      success: function () {
        var labelsLayer = this.getLabelsLayer();
        if (newBaseLayerHasLabels) {
          if (labelsLayer) {
            this._updateLabelsLayer(newBaseLayerAttrs, labelsLayerOptions);
          } else {
            this._addLabelsLayer(newBaseLayerAttrs, labelsLayerOptions);
          }
        } else {
          if (labelsLayer) {
            this._destroyLabelsLayer(labelsLayerOptions);
          } else {
            this.trigger('baseLayerChanged');
          }
        }
      }.bind(this),
      error: function () {
        this.trigger('baseLayerFailed');
      }.bind(this)
    };

    if (this.getBaseLayer()) {
      this._updateBaseLayer(newBaseLayerAttrs, baseLayerOptions);
    } else {
      this._createBaseLayer(newBaseLayerAttrs, baseLayerOptions);
    }
  },

  isBaseLayerAdded: function (basemapAttrs) {
    var baseLayerDefinitionModel = this.getBaseLayer();
    return baseLayerDefinitionModel.matchesAttrs(basemapAttrs);
  },

  _updateBaseLayer: function (basemapAttrs, opts) {
    var currentBaseLayer = this.getBaseLayer();
    var newBaseLayerAttrs = _.omit(basemapAttrs, [ 'id', 'order', 'labels', 'template' ]);

    currentBaseLayer.attributes = _.extend({
      order: 0
    }, _.pick(currentBaseLayer.attributes, [
      'id',
      'type',
      'category'
    ]));
    currentBaseLayer.save(newBaseLayerAttrs, opts);
  },

  _createBaseLayer: function (newBaseLayerAttrs, opts) {
    newBaseLayerAttrs = _.extend(newBaseLayerAttrs, {
      order: 0
    });
    return this.create(newBaseLayerAttrs, _.extend({ at: 0 }, opts));
  },

  _updateLabelsLayer: function (newBaseLayerAttrs, opts) {
    var labelsLayer = this.getLabelsLayer();
    var newAttrs = _.omit(newBaseLayerAttrs, 'labels');
    newAttrs = _.pick(newAttrs, _.identity);
    newAttrs = _.extend(newAttrs, {
      name: generateLabelsLayerName(newBaseLayerAttrs.name),
      urlTemplate: newBaseLayerAttrs.labels.url
    });
    labelsLayer.save(newAttrs, opts);
  },

  _addLabelsLayer: function (newBaseLayerAttrs, opts) {
    var optionsAttribute = _.pick(newBaseLayerAttrs, ['type', 'subdomains', 'minZoom', 'maxZoom', 'name', 'className', 'attribution', 'category']);
    optionsAttribute = _.extend(optionsAttribute, {
      urlTemplate: newBaseLayerAttrs.labels.url,
      name: generateLabelsLayerName(newBaseLayerAttrs.name)
    });

    var labelsLayerAttrs = {
      order: this.getLayerOnTop().get('order') + 1,
      options: optionsAttribute
    };

    this.create(labelsLayerAttrs, opts);
  },

  _destroyLabelsLayer: function (opts) {
    this.getLayerOnTop().destroy(opts);
  },

  getBaseLayer: function () {
    return this.first();
  },

  getLabelsLayer: function () {
    var layerOnTop = this.getLayerOnTop();
    if (this.size() > 1 && layerTypesAndKinds.isTiledType(layerOnTop.get('type'))) {
      return layerOnTop;
    }
  },

  getLayerOnTop: function () {
    return this.last();
  }
});
<commit_msg>linting and cleaning<commit_after>var $ = require('jquery');
var _ = require('underscore');
var Backbone = require('backbone');
var LayerDefinitionModel = require('./layer-definition-model');
var layerLetters = require('./layer-letters');
var layerColors = require('./layer-colors');
var nodeIds = require('../value-objects/analysis-node-ids');
var layerTypesAndKinds = require('./layer-types-and-kinds');
var SQLUtils = require('../helpers/sql-utils');
var TableNameUtils = require('../helpers/table-name-utils');
var checkAndBuildOpts = require('../helpers/required-opts');
var REQUIRED_OPTS = [
  'analysisDefinitionNodesCollection',
  'configModel',
  'mapId',
  'stateDefinitionModel',
  'userModel'
];

var generateLabelsLayerName = function (layerName) {
  return layerName + ' Labels';
};

/**
 * Collection of layer definitions
 */
module.exports = Backbone.Collection.extend({
  initialize: function (models, opts) {
    checkAndBuildOpts(opts, REQUIRED_OPTS, this);
  },

  url: function () {
    var baseUrl = this._configModel.get('base_url');
    return baseUrl + '/api/v1/maps/' + this._mapId + '/layers';
  },

  parse: function (r) {
    return r.layers;
  },

  save: function (options) {
    this.each(function (layerDefModel, index) {
      layerDefModel.set('order', index, { silent: true });
    });
    Backbone.sync('update', this, options);
  },

  toJSON: function () {
    return {
      layers: Backbone.Collection.prototype.toJSON.apply(this, arguments)
    };
  },

  /**
   * Intended to be called from entry point, to make sure initial layers are taken into account
   */
  resetByLayersData: function (layersData) {
    this.reset(layersData, {
      silent: true,
      initialLetters: _.chain(layersData)
        .pluck('options')
        .pluck('letter')
        .compact()
        .value()
    });
  },

  comparator: function (m) {
    return m.get('order');
  },

  model: function (d, opts) {
    var self = opts.collection;

    // Add data required for new editor if not set (e.g. a vis created on old editor doesn't contain letter and source)
    var o = _.clone(d.options) || {};
    var attrs = _.defaults(
      { options: o },
      _.omit(d, ['options']
      ));

    if (!isNaN(opts.at)) {
      attrs.options.order = opts.at;
    }

    if (layerTypesAndKinds.isKindDataLayer(attrs.kind)) {
      o.letter = o.letter || layerLetters.next(self._takenLetters(opts.initialLetters));
      o.color = o.color || layerColors.getColorForLetter(o.letter);

      // Create source attr if it does not already exist
      var sourceId = nodeIds.next(o.letter);

      if (o.table_name && (!o.source || o.source === sourceId)) {
        var tableName = TableNameUtils.getUnqualifiedName(o.table_name);
        var userName = o.user_name || TableNameUtils.getUsername(o.table_name);

        var qualifyTableName = (userName && self._configModel.get('user_name') !== userName) || self._userModel.isInsideOrg();
        tableName = TableNameUtils.getQualifiedTableName(o.table_name, userName, qualifyTableName);

        o.source = o.source || sourceId;
        o.query = o.query || SQLUtils.getDefaultSQLFromTableName(tableName);

        // Add analysis definition unless already existing
        self._analysisDefinitionNodesCollection.createSourceNode({
          id: sourceId,
          tableName: tableName,
          query: o.query
        });
      }
    }

    var parseAttrs = typeof opts.parse === 'boolean' ? opts.parse : true;
    var m = new LayerDefinitionModel(attrs, {
      parse: parseAttrs,
      collection: self,
      configModel: self._configModel
    });

    return m;
  },

  nextLetter: function () {
    return layerLetters.next(this._takenLetters());
  },

  findAnalysisDefinitionNodeModel: function (id) {
    return this._analysisDefinitionNodesCollection.get(id);
  },

  findOwnerOfAnalysisNode: function (nodeDefModel) {
    return this.find(function (layerDefModel) {
      return layerDefModel.isOwnerOfAnalysisNode(nodeDefModel);
    });
  },

  findPrimaryParentLayerToAnalysisNode: function (nodeDefModel, opts) {
    if (!nodeDefModel) return;

    opts = opts || {};
    if (!_.isArray(opts.exclude)) {
      opts.exclude = [opts.exclude];
    }
    var owner = this.findOwnerOfAnalysisNode(nodeDefModel);

    return this.find(function (layerDefModel) {
      if (layerDefModel === owner) return;
      if (_.contains(opts.exclude, layerDefModel)) return;

      var layerHeadNode = layerDefModel.getAnalysisDefinitionNodeModel();
      if (nodeDefModel === layerHeadNode) return true;

      if (layerHeadNode) {
        var primarySourceOfLastOwnNode = _.last(layerHeadNode.linkedListBySameLetter()).getPrimarySource();
        return nodeDefModel === primarySourceOfLastOwnNode;
      }
    });
  },

  isThereAnyGeometryData: function () {
    return this.filter(function (layerDefModel) {
      return layerDefModel.isDataLayer();
    }).some(function (layerDefModel) {
      var queryGeometryModel = layerDefModel.getAnalysisDefinitionNodeModel().queryGeometryModel;
      return queryGeometryModel && queryGeometryModel.hasValue();
    });
  },

  loadAllQueryGeometryModels: function (callback) {
    var promises = this.filter(function (layerDefModel) {
      return layerDefModel.isDataLayer();
    }).map(function (layerDefModel) {
      var queryGeometryModel = layerDefModel.getAnalysisDefinitionNodeModel().queryGeometryModel;
      var status = queryGeometryModel.get('status');
      var deferred = new $.Deferred();

      if (queryGeometryModel.attributes.query.indexOf('_raster') > 0 || queryGeometryModel.isFetched()) {
        deferred.resolve();
      } else if (queryGeometryModel.canFetch()) {
        if (status !== 'fetching') {
          queryGeometryModel.fetch({
            success: function () {
              deferred.resolve();
            },
            error: function () {
              deferred.reject();
            }
          });
        } else {
          deferred.resolve();
        }
      } else {
        deferred.reject();
      }

      return deferred.promise();
    }, this);

    $.when.apply($, promises).done(callback);
  },

  isThereAnyTorqueLayer: function () {
    return this.any(function (m) {
      return layerTypesAndKinds.isTorqueType(m.get('type'));
    });
  },

  isThereAnyCartoDBLayer: function () {
    return this.any(function (m) {
      return layerTypesAndKinds.isCartoDBType(m.get('type'));
    });
  },

  anyContainsNode: function (nodeDefModel) {
    return this.any(function (layerDefModel) {
      return layerDefModel.containsNode(nodeDefModel);
    });
  },

  isDataLayerOnTop: function (lyrModel) {
    var currentPos = this.indexOf(lyrModel);
    return currentPos === this.getTopDataLayerIndex();
  },

  getTopDataLayerIndex: function () {
    var layerOnTop = this.getLayerOnTop();
    var at = this.indexOf(layerOnTop);
    var hasLabels = layerOnTop &&
      !layerTypesAndKinds.isCartoDBType(layerOnTop.get('type')) &&
      !layerTypesAndKinds.isTorqueType(layerOnTop.get('type'));

    if (hasLabels) { at--; }

    return at;
  },

  getNumberOfDataLayers: function () {
    return this.select(function (model) {
      return layerTypesAndKinds.isTypeDataLayer(model.get('type'));
    }).length;
  },

  _takenLetters: function (otherLetters) {
    var valuesFromAddedModels = _.compact(this.pluck('letter'));

    // When adding multiple items the models created so far are stored in the internal object this._byId,
    // need to make sure to take them into account when returning already taken letter.
    var valuesFromModelsNotYetAdded = _.chain(this._byId).values().invoke('get', 'letter').value();

    return _.union(valuesFromAddedModels, valuesFromModelsNotYetAdded, otherLetters);
  },

  setBaseLayer: function (newBaseLayerAttrs) {
    this.trigger('changingBaseLayer');

    newBaseLayerAttrs = _.clone(newBaseLayerAttrs);
    if (this.isBaseLayerAdded(newBaseLayerAttrs)) {
      this.trigger('baseLayerChanged');
      return false;
    }

    var newBaseLayerHasLabels = newBaseLayerAttrs.labels && newBaseLayerAttrs.labels.url;

    var labelsLayerOptions = {
      silent: true,
      wait: true, // do not add/remove the layer unless it's created/removed/updated successfully
      success: function () {
        this.trigger('baseLayerChanged');
      }.bind(this),
      error: function () {
        this.trigger('baseLayerFailed');
      }.bind(this)
    };

    var baseLayerOptions = {
      silent: true,
      wait: true, // do not update the layer unless it's created/updated successfully
      success: function () {
        var labelsLayer = this.getLabelsLayer();
        if (newBaseLayerHasLabels) {
          if (labelsLayer) {
            this._updateLabelsLayer(newBaseLayerAttrs, labelsLayerOptions);
          } else {
            this._addLabelsLayer(newBaseLayerAttrs, labelsLayerOptions);
          }
        } else {
          if (labelsLayer) {
            this._destroyLabelsLayer(labelsLayerOptions);
          } else {
            this.trigger('baseLayerChanged');
          }
        }
      }.bind(this),
      error: function () {
        this.trigger('baseLayerFailed');
      }.bind(this)
    };

    if (this.getBaseLayer()) {
      this._updateBaseLayer(newBaseLayerAttrs, baseLayerOptions);
    } else {
      this._createBaseLayer(newBaseLayerAttrs, baseLayerOptions);
    }
  },

  isBaseLayerAdded: function (basemapAttrs) {
    var baseLayerDefinitionModel = this.getBaseLayer();
    return baseLayerDefinitionModel.matchesAttrs(basemapAttrs);
  },

  _updateBaseLayer: function (basemapAttrs, opts) {
    var currentBaseLayer = this.getBaseLayer();
    var newBaseLayerAttrs = _.omit(basemapAttrs, [ 'id', 'order', 'labels', 'template' ]);

    currentBaseLayer.attributes = _.extend({
      order: 0
    }, _.pick(currentBaseLayer.attributes, [
      'id',
      'type',
      'category'
    ]));
    currentBaseLayer.save(newBaseLayerAttrs, opts);
  },

  _createBaseLayer: function (newBaseLayerAttrs, opts) {
    newBaseLayerAttrs = _.extend(newBaseLayerAttrs, {
      order: 0
    });
    return this.create(newBaseLayerAttrs, _.extend({ at: 0 }, opts));
  },

  _updateLabelsLayer: function (newBaseLayerAttrs, opts) {
    var labelsLayer = this.getLabelsLayer();
    var newAttrs = _.omit(newBaseLayerAttrs, 'labels');
    newAttrs = _.pick(newAttrs, _.identity);
    newAttrs = _.extend(newAttrs, {
      name: generateLabelsLayerName(newBaseLayerAttrs.name),
      urlTemplate: newBaseLayerAttrs.labels.url
    });
    labelsLayer.save(newAttrs, opts);
  },

  _addLabelsLayer: function (newBaseLayerAttrs, opts) {
    var optionsAttribute = _.pick(newBaseLayerAttrs, ['type', 'subdomains', 'minZoom', 'maxZoom', 'name', 'className', 'attribution', 'category']);
    optionsAttribute = _.extend(optionsAttribute, {
      urlTemplate: newBaseLayerAttrs.labels.url,
      name: generateLabelsLayerName(newBaseLayerAttrs.name)
    });

    var labelsLayerAttrs = {
      order: this.getLayerOnTop().get('order') + 1,
      options: optionsAttribute
    };

    this.create(labelsLayerAttrs, opts);
  },

  _destroyLabelsLayer: function (opts) {
    this.getLayerOnTop().destroy(opts);
  },

  getBaseLayer: function () {
    return this.first();
  },

  getLabelsLayer: function () {
    var layerOnTop = this.getLayerOnTop();
    if (this.size() > 1 && layerTypesAndKinds.isTiledType(layerOnTop.get('type'))) {
      return layerOnTop;
    }
  },

  getLayerOnTop: function () {
    return this.last();
  }
});
<|endoftext|>


<commit_before>// LICENSE : MIT
"use strict";
const path = require("path");
const fs = require("fs");
function append(item, filePath) {
    // guard
    if (item == null || item.length == 0) {
        return;
    }
    var json = [];
    var absoluteFilePath = path.join(process.cwd(), filePath);
    try {
        json = require(absoluteFilePath);
    } catch (e) {
        // no
    }
    json.push(item);
    fs.writeFileSync(absoluteFilePath, JSON.stringify(json, null, 2), "utf-8");
}
module.exports.append = append;<commit_msg>feat(append): add parse<commit_after>// LICENSE : MIT
"use strict";
const path = require("path");
const fs = require("fs");
function append(item, filePath) {
    // guard
    if (item == null || item.length == 0) {
        return;
    }
    var itemObject;
    try {
        itemObject = JSON.parse(item);
    } catch (e) {
        itemObject = item;
    }
    var json = [];
    var absoluteFilePath = path.join(process.cwd(), filePath);
    try {
        json = require(absoluteFilePath);
    } catch (e) {
        // no
    }
    json.push(itemObject);
    fs.writeFileSync(absoluteFilePath, JSON.stringify(json, null, 2), "utf-8");
}
module.exports.append = append;<|endoftext|>


<commit_before>'use strict';

// MODULES //

var tape = require( 'tape' );
var kstest = require( '@stdlib/math/generics/statistics/kstest' );
var round = require( '@stdlib/math/base/special/round' );
var isnan = require( '@stdlib/math/base/utils/is-nan' );
var factory = require( './../lib/factory.js' );

// VARIABLES //

var opts = {
	'skip': ( process.env.TEST_MODE === 'coverage' )
};

// TESTS //

tape( 'main export is a function', function test( t ) {
	t.ok( true, __filename );
	t.equal( typeof factory, 'function', 'main export is a function' );
	t.end();
});

tape( 'the function returns a pseudorandom number generator (no seed)', function test( t ) {
	var triangular;
	var r;
	var i;

	// When binding distribution parameters...
	triangular = factory( -10.0, 10.0, 5.0 );
	for ( i = 0; i < 100; i++ ) {
		r = triangular();
		t.equal( typeof r, 'number', 'returns a number' );
	}

	// Without binding distribution parameters...
	triangular = factory();
	for ( i = 0; i < 100; i++ ) {
		r = triangular( -10.0, 10.0, 5.0 );
		t.equal( typeof r, 'number', 'returns a number' );
	}

	t.end();
});

tape( 'the function returns a seeded pseudorandom number generator', function test( t ) {
	var triangular1;
	var triangular2;
	var seed;
	var r1;
	var r2;
	var i;

	seed = round( Date.now() / 1000 );

	triangular1 = factory( 0.0, 4.0, 2.0, {
		'seed': seed
	});
	triangular2 = factory( 0.0, 4.0, 2.0, {
		'seed': seed
	});

	t.notEqual( triangular1, triangular2, 'separate generators' );

	for ( i = 0; i < 100; i++ ) {
		r1 = triangular1();
		r2 = triangular2();
		t.equal( r1, r2, 'both return same number' );
	}
	t.end();
});

tape( 'attached to the returned function is the generator name', function test( t ) {
	var triangular = factory();
	t.equal( triangular.NAME, 'triangular', 'has `NAME` property' );
	t.end();
});

tape( 'attached to the returned function is the underlying PRNG', function test( t ) {
	var triangular = factory();
	t.equal( typeof triangular.PRNG, 'function', 'has `PRNG` property' );
	t.end();
});

tape( 'attached to the returned function is the generator seed', function test( t ) {
	var triangular = factory({
		'seed': 12345
	});
	t.equal( typeof triangular.SEED, 'number', 'has `SEED` property' );
	t.equal( triangular.SEED, 12345, 'equal to provided seed' );
	t.end();
});

tape( 'the function throws an error if minimum support `a` is not a number primitive', function test( t ) {
	var values;
	var i;

	values = [
		'5',
		null,
		true,
		undefined,
		NaN,
		[],
		{},
		function(){}
	];

	for ( i = 0; i < values.length; i++ ) {
		t.throws( badValue( values[i] ), TypeError, 'throws an error when provided '+values[i] );
	}
	t.end();

	function badValue( value ) {
		return function() {
			factory( value, 2.0, 1.0 );
		};
	}
});

tape( 'the function throws an error if maximum support `b` is not a number primitive', function test( t ) {
	var values;
	var i;

	values = [
		'5',
		null,
		true,
		undefined,
		NaN,
		[],
		{},
		function(){}
	];

	for ( i = 0; i < values.length; i++ ) {
		t.throws( badValue( values[i] ), TypeError, 'throws an error when provided '+values[i] );
	}
	t.end();

	function badValue( value ) {
		return function() {
			factory( 2.0, value, 3.0 );
		};
	}
});

tape( 'the function throws an error if mode `c` is not a number primitive', function test( t ) {
	var values;
	var i;

	values = [
		'5',
		null,
		true,
		undefined,
		NaN,
		[],
		{},
		function(){}
	];

	for ( i = 0; i < values.length; i++ ) {
		t.throws( badValue( values[i] ), TypeError, 'throws an error when provided '+values[i] );
	}
	t.end();

	function badValue( value ) {
		return function() {
			factory( 2.0, 3.0, value );
		};
	}
});

tape( 'the function throws an error if the condition `a <= c <= b` is not satisfied', function test( t ) {
	var values;
	var i;

	values = [
		[ 0.0, 1.0, 2.0 ],
		[ -2.0, -4.0, -3.0 ],
		[ 2.0, 1.0, 1.5 ]
	];

	for ( i = 0; i < values.length; i++ ) {
		t.throws( badValue( values[i] ), RangeError, 'throws an error when provided '+values[i] );
	}
	t.end();

	function badValue( arr ) {
		return function() {
			factory( arr[0], arr[1], arr[2] );
		};
	}
});

tape( 'the function throws an error if provided an options argument which is not an object (no other arguments)', function test( t ) {
	var values;
	var i;

	values = [
		'abc',
		5,
		null,
		true,
		undefined,
		NaN,
		[],
		function(){}
	];

	for ( i = 0; i < values.length; i++ ) {
		t.throws( badValue( values[i] ), TypeError, 'throws an error when provided '+values[i] );
	}
	t.end();

	function badValue( value ) {
		return function() {
			factory( value );
		};
	}
});

tape( 'the function throws an error if provided an options argument which is not an object (other arguments)', function test( t ) {
	var values;
	var i;

	values = [
		'abc',
		5,
		null,
		true,
		undefined,
		NaN,
		[],
		function(){}
	];

	for ( i = 0; i < values.length; i++ ) {
		t.throws( badValue( values[i] ), TypeError, 'throws an error when provided '+values[i] );
	}
	t.end();

	function badValue( value ) {
		return function() {
			factory( 0.0, 1.0, 0.5, value );
		};
	}
});

tape( 'when called without arguments, the function returns a function that returns `NaN` if `a` is `NaN`', function test( t ) {
	var triangular;
	var r;

	triangular = factory();
	r = triangular( NaN, 5.0, 2.5 );

	t.strictEqual( isnan( r ), true, 'returns NaN' );
	t.end();
});

tape( 'when called without arguments, the function returns a function that returns `NaN` if `b` is `NaN`', function test( t ) {
	var triangular;
	var r;

	triangular = factory();
	r = triangular( 0.0, NaN, 1.0 );

	t.strictEqual( isnan( r ), true, 'returns NaN' );
	t.end();
});

tape( 'when called without arguments, the function returns a function that returns `NaN` if `c` is `NaN`', function test( t ) {
	var triangular;
	var r;

	triangular = factory();
	r = triangular( 0.0, 10.0, NaN );

	t.strictEqual( isnan( r ), true, 'returns NaN' );
	t.end();
});

tape( 'when called without arguments, the function returns a function that returns `NaN` if provided all `NaNs`', function test( t ) {
	var triangular;
	var r;

	triangular = factory();
	r = triangular( NaN, NaN, NaN );

	t.strictEqual( isnan( r ), true, 'returns NaN' );
	t.end();
});

tape( 'when called without arguments, the function returns a function that returns `NaN` if the condition `a <= c <= b` is not satisfied', function test( t ) {
	var triangular;
	var values;
	var r;
	var i;

	triangular = factory();
	values = [
		[ 2.0, 1.0, 1.5 ],
		[ -5.0, -4.0, 3.14 ],
		[ 0.0, 1.0, 5.5 ],
	];

	for ( i = 0; i < values.length; i++ ) {
		r = triangular( values[ i ][ 0 ], values[ i ][ 1 ], values[ i ][ 2 ] );
		t.strictEqual( isnan( r ), true, 'returns `NaN` when provided '+values[ i ] );
	}
	t.end();
});

tape( 'the function returns a function for creating random numbers from a triangular distribution', opts, function test( t ) {
	var triangular;
	var rejected;
	var pValue;
	var N;
	var a;
	var b;
	var c;
	var i;
	var j;
	var x;

	a = 2.0;
	b = 4.0;
	c = 3.33;

	rejected = 0;
	x = new Array( 1e3 );
	N = 500;
	for ( i = 0; i < N; i++ ) {
		triangular = factory( a, b, c );
		t.ok( true, 'seed: '+triangular.SEED );
		for ( j = 0; j < x.length; j++ ) {
			x[ j ] = triangular();
			if ( x[ j ] < a || x[ j ] > b ) {
				t.ok( false, 'returned a number outside support: '+x[ j ] );
			}
		}
		// Test using Kolmogorov-Smirnov goodness-of-fit test:
		pValue = kstest( x, 'triangular', a, b, c ).pValue;
		t.equal( typeof pValue, 'number', 'returns a p-value: '+pValue );
		if ( pValue < 0.05 ) {
			rejected += 1;
		}
	}
	// Account for small sample sizes and few repeats...
	t.ok( rejected / N < 0.10, 'null hypothesis (i.e., that numbers are drawn from Triangular('+a+','+b+','+c+')) is rejected in less than 10% of cases ('+rejected+' of '+N+')' );
	t.end();
});
<commit_msg>Require that goodness-of-fit tests succeed twice in a row if the first run fails<commit_after>'use strict';

// MODULES //

var tape = require( 'tape' );
var kstest = require( '@stdlib/math/generics/statistics/kstest' );
var round = require( '@stdlib/math/base/special/round' );
var isnan = require( '@stdlib/math/base/utils/is-nan' );
var factory = require( './../lib/factory.js' );

// VARIABLES //

var opts = {
	'skip': ( process.env.TEST_MODE === 'coverage' )
};

// TESTS //

tape( 'main export is a function', function test( t ) {
	t.ok( true, __filename );
	t.equal( typeof factory, 'function', 'main export is a function' );
	t.end();
});

tape( 'the function returns a pseudorandom number generator (no seed)', function test( t ) {
	var triangular;
	var r;
	var i;

	// When binding distribution parameters...
	triangular = factory( -10.0, 10.0, 5.0 );
	for ( i = 0; i < 100; i++ ) {
		r = triangular();
		t.equal( typeof r, 'number', 'returns a number' );
	}

	// Without binding distribution parameters...
	triangular = factory();
	for ( i = 0; i < 100; i++ ) {
		r = triangular( -10.0, 10.0, 5.0 );
		t.equal( typeof r, 'number', 'returns a number' );
	}

	t.end();
});

tape( 'the function returns a seeded pseudorandom number generator', function test( t ) {
	var triangular1;
	var triangular2;
	var seed;
	var r1;
	var r2;
	var i;

	seed = round( Date.now() / 1000 );

	triangular1 = factory( 0.0, 4.0, 2.0, {
		'seed': seed
	});
	triangular2 = factory( 0.0, 4.0, 2.0, {
		'seed': seed
	});

	t.notEqual( triangular1, triangular2, 'separate generators' );

	for ( i = 0; i < 100; i++ ) {
		r1 = triangular1();
		r2 = triangular2();
		t.equal( r1, r2, 'both return same number' );
	}
	t.end();
});

tape( 'attached to the returned function is the generator name', function test( t ) {
	var triangular = factory();
	t.equal( triangular.NAME, 'triangular', 'has `NAME` property' );
	t.end();
});

tape( 'attached to the returned function is the underlying PRNG', function test( t ) {
	var triangular = factory();
	t.equal( typeof triangular.PRNG, 'function', 'has `PRNG` property' );
	t.end();
});

tape( 'attached to the returned function is the generator seed', function test( t ) {
	var triangular = factory({
		'seed': 12345
	});
	t.equal( typeof triangular.SEED, 'number', 'has `SEED` property' );
	t.equal( triangular.SEED, 12345, 'equal to provided seed' );
	t.end();
});

tape( 'the function throws an error if minimum support `a` is not a number primitive', function test( t ) {
	var values;
	var i;

	values = [
		'5',
		null,
		true,
		undefined,
		NaN,
		[],
		{},
		function(){}
	];

	for ( i = 0; i < values.length; i++ ) {
		t.throws( badValue( values[i] ), TypeError, 'throws an error when provided '+values[i] );
	}
	t.end();

	function badValue( value ) {
		return function() {
			factory( value, 2.0, 1.0 );
		};
	}
});

tape( 'the function throws an error if maximum support `b` is not a number primitive', function test( t ) {
	var values;
	var i;

	values = [
		'5',
		null,
		true,
		undefined,
		NaN,
		[],
		{},
		function(){}
	];

	for ( i = 0; i < values.length; i++ ) {
		t.throws( badValue( values[i] ), TypeError, 'throws an error when provided '+values[i] );
	}
	t.end();

	function badValue( value ) {
		return function() {
			factory( 2.0, value, 3.0 );
		};
	}
});

tape( 'the function throws an error if mode `c` is not a number primitive', function test( t ) {
	var values;
	var i;

	values = [
		'5',
		null,
		true,
		undefined,
		NaN,
		[],
		{},
		function(){}
	];

	for ( i = 0; i < values.length; i++ ) {
		t.throws( badValue( values[i] ), TypeError, 'throws an error when provided '+values[i] );
	}
	t.end();

	function badValue( value ) {
		return function() {
			factory( 2.0, 3.0, value );
		};
	}
});

tape( 'the function throws an error if the condition `a <= c <= b` is not satisfied', function test( t ) {
	var values;
	var i;

	values = [
		[ 0.0, 1.0, 2.0 ],
		[ -2.0, -4.0, -3.0 ],
		[ 2.0, 1.0, 1.5 ]
	];

	for ( i = 0; i < values.length; i++ ) {
		t.throws( badValue( values[i] ), RangeError, 'throws an error when provided '+values[i] );
	}
	t.end();

	function badValue( arr ) {
		return function() {
			factory( arr[0], arr[1], arr[2] );
		};
	}
});

tape( 'the function throws an error if provided an options argument which is not an object (no other arguments)', function test( t ) {
	var values;
	var i;

	values = [
		'abc',
		5,
		null,
		true,
		undefined,
		NaN,
		[],
		function(){}
	];

	for ( i = 0; i < values.length; i++ ) {
		t.throws( badValue( values[i] ), TypeError, 'throws an error when provided '+values[i] );
	}
	t.end();

	function badValue( value ) {
		return function() {
			factory( value );
		};
	}
});

tape( 'the function throws an error if provided an options argument which is not an object (other arguments)', function test( t ) {
	var values;
	var i;

	values = [
		'abc',
		5,
		null,
		true,
		undefined,
		NaN,
		[],
		function(){}
	];

	for ( i = 0; i < values.length; i++ ) {
		t.throws( badValue( values[i] ), TypeError, 'throws an error when provided '+values[i] );
	}
	t.end();

	function badValue( value ) {
		return function() {
			factory( 0.0, 1.0, 0.5, value );
		};
	}
});

tape( 'when called without arguments, the function returns a function that returns `NaN` if `a` is `NaN`', function test( t ) {
	var triangular;
	var r;

	triangular = factory();
	r = triangular( NaN, 5.0, 2.5 );

	t.strictEqual( isnan( r ), true, 'returns NaN' );
	t.end();
});

tape( 'when called without arguments, the function returns a function that returns `NaN` if `b` is `NaN`', function test( t ) {
	var triangular;
	var r;

	triangular = factory();
	r = triangular( 0.0, NaN, 1.0 );

	t.strictEqual( isnan( r ), true, 'returns NaN' );
	t.end();
});

tape( 'when called without arguments, the function returns a function that returns `NaN` if `c` is `NaN`', function test( t ) {
	var triangular;
	var r;

	triangular = factory();
	r = triangular( 0.0, 10.0, NaN );

	t.strictEqual( isnan( r ), true, 'returns NaN' );
	t.end();
});

tape( 'when called without arguments, the function returns a function that returns `NaN` if provided all `NaNs`', function test( t ) {
	var triangular;
	var r;

	triangular = factory();
	r = triangular( NaN, NaN, NaN );

	t.strictEqual( isnan( r ), true, 'returns NaN' );
	t.end();
});

tape( 'when called without arguments, the function returns a function that returns `NaN` if the condition `a <= c <= b` is not satisfied', function test( t ) {
	var triangular;
	var values;
	var r;
	var i;

	triangular = factory();
	values = [
		[ 2.0, 1.0, 1.5 ],
		[ -5.0, -4.0, 3.14 ],
		[ 0.0, 1.0, 5.5 ],
	];

	for ( i = 0; i < values.length; i++ ) {
		r = triangular( values[ i ][ 0 ], values[ i ][ 1 ], values[ i ][ 2 ] );
		t.strictEqual( isnan( r ), true, 'returns `NaN` when provided '+values[ i ] );
	}
	t.end();
});

tape( 'the function returns a PRNG for generating random numbers from a triangular distribution', opts, function test( t ) {
	var threshold;
	var count;
	var npass;
	var N;
	var a;
	var b;
	var c;
	var x;

	threshold = 0.10;

	a = 2.0;
	b = 4.0;
	c = 3.33;

	x = new Array( 1e3 );
	N = 500;

	count = -1;
	npass = 0;

	gof();

	function gof() {
		var triangular;
		var rejected;
		var pValue;
		var bool;
		var i;
		var j;

		count += 1;
		rejected = 0;
		for ( i = 0; i < N; i++ ) {
			triangular = factory( a, b, c );
			t.ok( true, 'seed: '+triangular.SEED );
			for ( j = 0; j < x.length; j++ ) {
				x[ j ] = triangular();
				if ( x[ j ] < a || x[ j ] > b ) {
					t.ok( false, 'returned a number outside support: '+x[ j ] );
				}
			}
			// Test using Kolmogorov-Smirnov goodness-of-fit test:
			pValue = kstest( x, 'triangular', a, b, c ).pValue;
			t.equal( typeof pValue, 'number', 'returns a p-value: '+pValue );
			if ( pValue < 0.05 ) {
				rejected += 1;
			}
		}
		// Account for small sample size and few repeats...
		bool = ( rejected / N < threshold );

		// If we succeed the first time, we are done...
		if ( count === 0 && bool ) {
			return done( bool, rejected );
		}
		// Retry mode...
		if ( bool ) {
			npass += 1;
		}
		// Retry twice...
		if ( count < 2 ) {
			return gof();
		}
		// Both retries must succeed for test to pass:
		bool = ( npass >= 2 );
		return done( bool, rejected );
	}

	function done( bool, rejected ) {
		t.ok( bool, 'null hypothesis (i.e., that numbers are drawn from Triangular('+a+','+b+','+c+')) is rejected in less than'+(threshold*100)+'% of cases ('+rejected+' of '+N+'). Repeats: '+npass+' of '+count+'.' );
		t.end();
	}
});
<|endoftext|>


<commit_before>
var endpoint = require('endpoint');

// Instead of calling Date.now() all the time, an internal second variable
// is used.
var NOW_SECOND = Math.floor(Date.now() / 1000);
setInterval(function() {
  NOW_SECOND = Math.floor(Date.now() / 1000);
}, 1000).unref();

function KeyManager(database, maxage) {
  var self = this;

  this._database = database;
  this._seconds = {};
  this._cacheAge = NOW_SECOND;
  this._cacheMaxAge = maxage;

  this._cleanTimer = setTimeout(function () {
    clean();
    self._cleanTimer = setInterval(clean, 1000);
  }, maxage * 1000);

  function clean() { self._cleanup(); }
}
module.exports = KeyManager;

KeyManager.prototype.generateKey = function (second, millisecond, level, callback) {
  // Fetch or create the second object
  var sec = this._seconds[second];
  if (sec === undefined) {
    if (second <= this._cacheAge) {
      sec = this._seconds[second] = new SecondPast(second, this._database);
    } else {
      sec = this._seconds[second] = new SecondCurrent(second, this._database);
    }
  }

  var buf = new Buffer(9);

  // Write static part of buffer
  buf.writeUInt32BE(second, 0);
  buf.writeUInt16BE(millisecond, 4);
  buf.writeUInt8(level, 8);

  // Write id part of buffer
  sec.nextId(millisecond, function (err, counter) {
    if (err) return callback(err, null);

    buf.writeUInt16BE(counter, 6);
    callback(null, buf);
  });
};

KeyManager.prototype._cleanup = function () {
  var oldest = NOW_SECOND - this._cacheMaxAge;

  var keys = Object.keys(this._seconds);
  for (var i = 0, l = keys.length; i < l; i++) {
    var secobj = this._seconds[ keys[i] ];
    if (secobj.cleanable === true && secobj.time <= oldest) {
      delete this._seconds[ keys[i] ];
    }
  }

  // Update the cacheAge so new second objects before this time becomes past
  // second objects.
  this._cacheAge = oldest;
};

KeyManager.prototype.close = function () {
  clearTimeout(this._cleanTimer);
  clearInterval(this._cleanTimer);
};

//
// Second abstactions
//

var MAX_COUNT = Math.pow(2, 16);
var MAX_COUNT_ERROR = new RangeError('duplicate incrementer exceeded 16 bit uint capacity');

function doIdCallback(counter, callback) {
  if (global.debug) console.log(counter);
  if (counter >= MAX_COUNT) {
    callback(MAX_COUNT_ERROR, null);
  } else {
    callback(null, counter);
  }
}

// This is a new second, there is absolutely that anything from this time
// could be in the database previous to the object initialization.
function SecondCurrent(second) {
  this._second = second;
  this._current = {};

  this.cleanable = true;
  this.time = Math.max(second, NOW_SECOND);
}

SecondCurrent.prototype.nextId = function (milliseconds, callback) {
  var counter;

  if (this._current.hasOwnProperty(milliseconds)) {
    counter = this._current[milliseconds] += 1;
  } else {
    counter = this._current[milliseconds] = 0;
  }

  doIdCallback(counter, callback);
};

// It is unkown if something from this time is in the database, it is necessary
// to query the database.
function SecondPast(second, database) {
  this._second = second;
  this._database = database;
  this._current = {};
  this._fetchers = {};
  this._actives = 0;

  this.cleanable = true;
  this.time = Math.max(second, NOW_SECOND);
}

// Creates a buffer used for sorted search
function timestampBuffer(seconds, milliseconds, fill) {
  var buf = new Buffer(9);
      buf.fill(fill);
      buf.writeUInt32BE(seconds, 0);
      buf.writeUInt16BE(milliseconds, 4);

  return buf;
}

SecondPast.prototype._fetchCounter = function (millisecond) {
  var self = this;

  // Create a timestamp `start` and `end` buffers used in a sorted key search
  var tstart = timestampBuffer(this._second, millisecond, 0x00);
  var tend = timestampBuffer(this._second, millisecond, 0xff);

  // Start the reverse single item search
  this._database.createKeyStream({
    'limit': 1,
    'reverse': true,
    'start': tstart,
    'end': tend
  }).pipe(endpoint({objectMode: true}, function (err, keys) {
    if (err) return fetched(err, null);

    // If timestamp dosn't match no item exist and the count is zero
    if (keys.length === 0) return fetched(null, 0);

    // Otherwise read the counter and incease by one
    return fetched(null, keys[0].readUInt16BE(6) + 1);
  }));

  function fetched(err, nextId) {
    var cbs = self._fetchers[millisecond];
    var i = 0, l = cbs.length;

    // Indicate that this millisecond request is done and reset the cleanable
    // to true, if no other requests are in progress.
    this._actives -= 1;
    if (this._actives === 0) {
      this.cleanable = true;
    }

    // Cleanup the callback array
    delete self._fetchers[millisecond];

    // Loop though cbs and execute callback
    if (err) {
      for (; i < l; i++) cbs[i](err, null);
    }
    // Loop though cbs and set millisecond to the currentId
    else {
      for (; i < l; i++) doIdCallback(nextId + i, cbs[i]);
      self._current[millisecond] = nextId + (l - 1);
    }
  }
};

SecondPast.prototype.nextId = function (milliseconds, callback) {
  // This has already been fetched
  if (this._current.hasOwnProperty(milliseconds)) {
    doIdCallback(++this._current[milliseconds], callback);
  }
  // This is being fetched, queue the callbacks to avoid race conditions
  else if (this._fetchers.hasOwnProperty(milliseconds)) {
    this._fetchers[milliseconds].push(callback);
  }
  // We know nothing, create a queue and process it
  else {
    this._fetchers[milliseconds] = [callback];
    this._fetchCounter(milliseconds);

    // Indicate that something is in progress so this object should
    // not be removed.
    this._actives += 1;
    this.cleanable = false;
  }
};
<commit_msg>[fix] max counter query<commit_after>
var endpoint = require('endpoint');

// Instead of calling Date.now() all the time, an internal second variable
// is used.
var NOW_SECOND = Math.floor(Date.now() / 1000);
setInterval(function() {
  NOW_SECOND = Math.floor(Date.now() / 1000);
}, 1000).unref();

function KeyManager(database, maxage) {
  var self = this;

  this._database = database;
  this._seconds = {};
  this._cacheAge = NOW_SECOND;
  this._cacheMaxAge = maxage;

  this._cleanTimer = setTimeout(function () {
    clean();
    self._cleanTimer = setInterval(clean, 1000);
  }, maxage * 1000);

  function clean() { self._cleanup(); }
}
module.exports = KeyManager;

KeyManager.prototype.generateKey = function (second, millisecond, level, callback) {
  // Fetch or create the second object
  var sec = this._seconds[second];
  if (sec === undefined) {
    if (second <= this._cacheAge) {
      sec = this._seconds[second] = new SecondPast(second, this._database);
    } else {
      sec = this._seconds[second] = new SecondCurrent(second, this._database);
    }
  }

  var buf = new Buffer(9);

  // Write static part of buffer
  buf.writeUInt32BE(second, 0);
  buf.writeUInt16BE(millisecond, 4);
  buf.writeUInt8(level, 8);

  // Write id part of buffer
  sec.nextId(millisecond, function (err, counter) {
    if (err) return callback(err, null);

    buf.writeUInt16BE(counter, 6);
    callback(null, buf);
  });
};

KeyManager.prototype._cleanup = function () {
  var oldest = NOW_SECOND - this._cacheMaxAge;

  var keys = Object.keys(this._seconds);
  for (var i = 0, l = keys.length; i < l; i++) {
    var secobj = this._seconds[ keys[i] ];
    if (secobj.cleanable === true && secobj.time <= oldest) {
      delete this._seconds[ keys[i] ];
    }
  }

  // Update the cacheAge so new second objects before this time becomes past
  // second objects.
  this._cacheAge = oldest;
};

KeyManager.prototype.close = function () {
  clearTimeout(this._cleanTimer);
  clearInterval(this._cleanTimer);
};

//
// Second abstactions
//
var MAX_COUNT = Math.pow(2, 16);
var MAX_COUNT_ERROR = new RangeError('duplicate incrementer exceeded 16 bit uint capacity');

function doIdCallback(counter, callback) {
  if (counter >= MAX_COUNT) {
    callback(MAX_COUNT_ERROR, null);
  } else {
    callback(null, counter);
  }
}

// This is a new second, there is absolutely that anything from this time
// could be in the database previous to the object initialization.
function SecondCurrent(second) {
  this._second = second;
  this._current = {};

  this.cleanable = true;
  this.time = Math.max(second, NOW_SECOND);
}

SecondCurrent.prototype.nextId = function (milliseconds, callback) {
  var counter;

  if (this._current.hasOwnProperty(milliseconds)) {
    counter = this._current[milliseconds] += 1;
  } else {
    counter = this._current[milliseconds] = 0;
  }

  doIdCallback(counter, callback);
};

// It is unkown if something from this time is in the database, it is necessary
// to query the database.
function SecondPast(second, database) {
  this._second = second;
  this._database = database;
  this._current = {};
  this._fetchers = {};
  this._actives = 0;

  this.cleanable = true;
  this.time = Math.max(second, NOW_SECOND);
}

// Creates a buffer used for sorted search
function timestampBuffer(seconds, milliseconds, fill) {
  var buf = new Buffer(9);
      buf.fill(fill);
      buf.writeUInt32BE(seconds, 0);
      buf.writeUInt16BE(milliseconds, 4);

  return buf;
}

SecondPast.prototype._fetchCounter = function (millisecond) {
  var self = this;

  // Create a timestamp `start` and `end` buffers used in a sorted key search
  var tstart = timestampBuffer(this._second, millisecond, 0x00);
  var tend = timestampBuffer(this._second, millisecond, 0xff);

  // Start the reverse single item search
  this._database.createKeyStream({
    'limit': 1,
    'reverse': true,
    'start': tend,
    'end': tstart
  }).pipe(endpoint({objectMode: true}, function (err, keys) {
    if (err) return fetched(err, null);

    // If timestamp dosn't match no item exist and the count is zero
    if (keys.length === 0) return fetched(null, 0);

    // Otherwise read the counter and incease by one
    return fetched(null, keys[0].readUInt16BE(6) + 1);
  }));

  function fetched(err, nextId) {
    var cbs = self._fetchers[millisecond];
    var i = 0, l = cbs.length;

    // Indicate that this millisecond request is done and reset the cleanable
    // to true, if no other requests are in progress.
    self._actives -= 1;
    if (self._actives === 0) {
      self.cleanable = true;
    }

    // Cleanup the callback array
    delete self._fetchers[millisecond];

    // Loop though cbs and execute callback
    if (err) {
      for (; i < l; i++) cbs[i](err, null);
    }
    // Loop though cbs and set millisecond to the currentId
    else {
      for (; i < l; i++) doIdCallback(nextId + i, cbs[i]);
      self._current[millisecond] = nextId + (l - 1);
    }
  }
};

SecondPast.prototype.nextId = function (milliseconds, callback) {
  // This has already been fetched
  if (this._current.hasOwnProperty(milliseconds)) {
    doIdCallback(++this._current[milliseconds], callback);
  }
  // This is being fetched, queue the callbacks to avoid race conditions
  else if (this._fetchers.hasOwnProperty(milliseconds)) {
    this._fetchers[milliseconds].push(callback);
  }
  // We know nothing, create a queue and process it
  else {
    this._fetchers[milliseconds] = [callback];
    this._fetchCounter(milliseconds);

    // Indicate that something is in progress so this object should
    // not be removed.
    this._actives += 1;
    this.cleanable = false;
  }
};
<|endoftext|>


<commit_before>'use strict';

// MODULES //

var tape = require( 'tape' );
var isNumberArray = require( '@stdlib/utils/is-number' ).isNumberArray;
var isStringArray = require( '@stdlib/utils/is-string' ).isStringArray;
var factory = require( './../lib/factory.js' );

// TESTS //

tape( 'main export is a function', function test( t ) {
	t.ok( true, __filename );
	t.equal( typeof factory, 'function', 'main export is a function' );
	t.end();
});

tape( 'the returned function throws an error if not provided an array-like object', function test( t ) {
	var sample;
	var values;
	var i;

	sample = factory();

	values = [
		5,
		NaN,
		null,
		undefined,
		true,
		{},
		function(){}
	];
	for ( i = 0; i < values.length; i++ ) {
		t.throws( badValue( values[i] ), TypeError, 'throws a type error when provided '+values[i] );
	}
	t.end();

	function badValue( value ) {
		return function badValue() {
			sample( value );
		};
	}
});

tape( 'the returned function throws an error if provided an options object which is not an object', function test( t ) {
	var sample;
	var values;
	var i;

	sample = factory();

	values = [
		'5',
		5,
		NaN,
		null,
		undefined,
		true,
		[],
		function(){}
	];
	for ( i = 0; i < values.length; i++ ) {
		t.throws( badValue( values[i] ), TypeError, 'throws a type error when provided '+values[i] );
	}
	t.end();

	function badValue( value ) {
		return function badValue() {
			sample( [], value );
		};
	}
});

tape( 'the returned function throws an error if provided an invalid option', function test( t ) {
	var sample;
	var values;
	var i;

	sample = factory();

	values = [
		'5',
		5,
		NaN,
		null,
		undefined,
		[],
		{},
		function(){}
	];
	for ( i = 0; i < values.length; i++ ) {
		t.throws( badValue( values[i] ), TypeError, 'throws a type error when provided '+values[i] );
	}
	t.end();

	function badValue( value ) {
		return function badValue() {
			sample( [], {
				'replace': value
			});
		};
	}
});

tape( 'the returned function throws an error if provided an `probs` option whose elements do not sum to one', function test( t ) {
	var sample;
	var values;
	var i;

	sample = factory();

	values = [
		[ 0.5, 0.2, 0.1, 0.1 ],
		[ 0.3, 0.3, 0.3, 0.3 ]
	];
	for ( i = 0; i < values.length; i++ ) {
		t.throws( badValue( values[i] ), RangeError, 'throws a range error when provided '+values[i] );
	}
	t.end();

	function badValue( value ) {
		return function badValue() {
			sample( [ 1, 2, 3, 4 ], {
				'probs': value
			});
		};
	}
});

tape( 'the returned function throws an error if `probs` contains non-probabilities', function test( t ) {
	var sample;
	var values;
	var i;

	sample = factory();

	values = [
		[ -0.1, 1.0, 0.1 ],
		[ 0.2, 1.1, -0.3 ]
	];
	for ( i = 0; i < values.length; i++ ) {
		t.throws( badValue( values[i] ), RangeError, 'throws a range error when provided '+values[i] );
	}
	t.end();

	function badValue( value ) {
		return function badValue() {
			sample( [ 0, 1, 2 ], {
				'probs': value
			});
		};
	}
});

tape( 'the returned function samples characters with replacement from a string', function test( t ) {
	var expected;
	var actual;
	var sample;

	sample = factory( 311 );

	expected = [ 'f', 'e', 'e', 'e', 'b', 'a' ];
	actual = sample( 'abcdef' );

	t.equal( isStringArray( actual ), true, 'returns an array of chracters' );
	t.equal( actual.length, 6, 'returned array has six elements' );
	t.deepEqual( actual, expected, 'returned array is a random draw' );

	t.end();
});

tape( 'the returned function samples with replacement from an array', function test( t ) {
	var expected;
	var actual;
	var sample;

	sample = factory( 311 );

	// Simple array:
	expected = [ 4, 4, 3, 3 ];
	actual = sample( [ 1, 2, 3, 4 ] );

	t.equal( isNumberArray( actual ), true, 'returns an array of numbers' );
	t.equal( actual.length, 4, 'returned array has four elements' );
	t.deepEqual( actual, expected, 'returned array is a random draw' );

	// Typed array:
	expected = [ 6, 5, 8, 8, 8 ];
	actual = sample( new Float64Array( [ 5, 6, 7, 8, 9 ] ) );

	t.equal( isNumberArray( actual ), true, 'returns an array of numbers' );
	t.equal( actual.length, 5, 'returned array has five elements' );
	t.deepEqual( actual, expected, 'returned array is a random draw' );

	t.end();
});

tape( 'the returned function samples without replacement from an array', function test( t ) {
	var expected;
	var actual;
	var sample;

	sample = factory( 789 );

	// Simple array:
	expected = [ 4, 3, 1, 2 ];
	actual = sample( [ 1, 2, 3, 4 ], {
		'replace': false
	});

	// Typed array:
	expected = [ 7, 9, 6, 5, 8 ];
	actual = sample( new Int32Array( [ 5, 6, 7, 8, 9 ] ), {
		'replace': false
	});

	t.equal( isNumberArray( actual ), true, 'returns an array of numbers' );
	t.equal( actual.length, 5, 'returned array has five elements' );
	t.deepEqual( actual, expected, 'returned array is a random draw' );

	t.end();
});

tape( 'the returned function draws a sample with the specified size from an array (with replacement)', function test( t ) {
	var expected;
	var actual;
	var sample;

	sample = factory( 789 );

	// Simple array:
	expected = [ 0, 0, 0, 1, 0, 1, 1, 1, 1, 1 ];
	actual = sample( [ 0, 1 ], {
		'size': 10
	});

	t.equal( isNumberArray( actual ), true, 'returns an array of numbers' );
	t.equal( actual.length, 10, 'returned array has ten elements' );
	t.deepEqual( actual, expected, 'returned array is a random draw' );

	// Typed array:
	expected = [ 1, 1, 1, 0, 0, 1, 0, 1, 1, 0 ];
	actual = sample( new Int8Array( [ 0, 1 ] ), {
		'size': 10
	});

	t.equal( isNumberArray( actual ), true, 'returns an array of numbers' );
	t.equal( actual.length, 10, 'returned array has ten elements' );
	t.deepEqual( actual, expected, 'returned array is a random draw' );

	t.end();
});

tape( 'the returned function draws a sample with the specified size from an array (without replacement)', function test( t ) {
	var expected;
	var actual;
	var sample;

	sample = factory( 656 );

	// Simple array:
	expected = [ 3, 8, 7, 5 ];
	actual = sample( [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 ], {
		'size': 4,
		'replace': false
	});

	t.equal( isNumberArray( actual ), true, 'returns an array of numbers' );
	t.equal( actual.length, 4, 'returned array has four elements' );
	t.deepEqual( actual, expected, 'returned array is a random draw' );

	// Typed array:
	expected = [ 3, 5, 4, 8, 6 ];
	actual = sample( [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 ], {
		'size': 5,
		'replace': false
	});

	t.equal( isNumberArray( actual ), true, 'returns an array of numbers' );
	t.equal( actual.length, 5, 'returned array has five elements' );
	t.deepEqual( actual, expected, 'returned array is a random draw' );

	t.end();
});
<commit_msg>Add remaining tests to cover all loc<commit_after>'use strict';

// MODULES //

var tape = require( 'tape' );
var isNumberArray = require( '@stdlib/utils/is-number' ).isNumberArray;
var isStringArray = require( '@stdlib/utils/is-string' ).isStringArray;
var factory = require( './../lib/factory.js' );

// TESTS //

tape( 'main export is a function', function test( t ) {
	t.ok( true, __filename );
	t.equal( typeof factory, 'function', 'main export is a function' );
	t.end();
});

tape( 'the returned function throws an error if not provided an array-like object', function test( t ) {
	var sample;
	var values;
	var i;

	sample = factory();

	values = [
		5,
		NaN,
		null,
		undefined,
		true,
		{},
		function(){}
	];
	for ( i = 0; i < values.length; i++ ) {
		t.throws( badValue( values[i] ), TypeError, 'throws a type error when provided '+values[i] );
	}
	t.end();

	function badValue( value ) {
		return function badValue() {
			sample( value );
		};
	}
});

tape( 'the returned function throws an error if provided an options object which is not an object', function test( t ) {
	var sample;
	var values;
	var i;

	sample = factory();

	values = [
		'5',
		5,
		NaN,
		null,
		undefined,
		true,
		[],
		function(){}
	];
	for ( i = 0; i < values.length; i++ ) {
		t.throws( badValue( values[i] ), TypeError, 'throws a type error when provided '+values[i] );
	}
	t.end();

	function badValue( value ) {
		return function badValue() {
			sample( [], value );
		};
	}
});

tape( 'the returned function throws an error if provided an invalid option', function test( t ) {
	var sample;
	var values;
	var i;

	sample = factory();

	values = [
		'5',
		5,
		NaN,
		null,
		undefined,
		[],
		{},
		function(){}
	];
	for ( i = 0; i < values.length; i++ ) {
		t.throws( badValue( values[i] ), TypeError, 'throws a type error when provided '+values[i] );
	}
	t.end();

	function badValue( value ) {
		return function badValue() {
			sample( [], {
				'replace': value
			});
		};
	}
});

tape( 'the returned function throws an error if provided an `probs` option whose elements do not sum to one', function test( t ) {
	var sample;
	var values;
	var i;

	sample = factory();

	values = [
		[ 0.5, 0.2, 0.1, 0.1 ],
		[ 0.3, 0.3, 0.3, 0.3 ]
	];
	for ( i = 0; i < values.length; i++ ) {
		t.throws( badValue( values[i] ), RangeError, 'throws a range error when provided '+values[i] );
	}
	t.end();

	function badValue( value ) {
		return function badValue() {
			sample( [ 1, 2, 3, 4 ], {
				'probs': value
			});
		};
	}
});

tape( 'the returned function throws an error if provided a `size` option exceeding the input array length when sampling without recplacement', function test( t ) {
	var sample;
	var values;
	var i;

	sample = factory();

	values = [
		[ [ 1, 2, 3 ], 4 ],
		[ [ 1, 2, 3, 4, 5, 6 ], 10 ]
	];
	for ( i = 0; i < values.length; i++ ) {
		t.throws( badValue( values[i] ), TypeError, 'throws a type error' );
	}
	t.end();

	function badValue( value ) {
		return function badValue() {
			sample( value[ 0 ], {
				'size': value[ 1 ],
				'replace': false
			});
		};
	}
});

tape( 'the returned function throws an error if `probs` contains non-probabilities', function test( t ) {
	var sample;
	var values;
	var i;

	sample = factory();

	values = [
		[ -0.1, 1.0, 0.1 ],
		[ 0.2, 1.1, -0.3 ]
	];
	for ( i = 0; i < values.length; i++ ) {
		t.throws( badValue( values[i] ), RangeError, 'throws a range error when provided '+values[i] );
	}
	t.end();

	function badValue( value ) {
		return function badValue() {
			sample( [ 0, 1, 2 ], {
				'probs': value
			});
		};
	}
});

tape( 'attached to the returned function is the generator seed', function test( t ) {
	var sample = factory( 311 );
	t.equal( typeof sample.SEED, 'number', 'has `SEED` property' );
	t.end();
});

tape( 'the returned function samples characters with replacement from a string', function test( t ) {
	var expected;
	var actual;
	var sample;

	sample = factory( 311 );

	expected = [ 'f', 'e', 'e', 'e', 'b', 'a' ];
	actual = sample( 'abcdef' );

	t.equal( isStringArray( actual ), true, 'returns an array of chracters' );
	t.equal( actual.length, 6, 'returned array has six elements' );
	t.deepEqual( actual, expected, 'returned array is a random draw' );

	t.end();
});

tape( 'the returned function samples with replacement from an array', function test( t ) {
	var expected;
	var actual;
	var sample;

	sample = factory( 311 );

	// Simple array:
	expected = [ 4, 4, 3, 3 ];
	actual = sample( [ 1, 2, 3, 4 ] );

	t.equal( isNumberArray( actual ), true, 'returns an array of numbers' );
	t.equal( actual.length, 4, 'returned array has four elements' );
	t.deepEqual( actual, expected, 'returned array is a random draw' );

	// Typed array:
	expected = [ 6, 5, 8, 8, 8 ];
	actual = sample( new Float64Array( [ 5, 6, 7, 8, 9 ] ) );

	t.equal( isNumberArray( actual ), true, 'returns an array of numbers' );
	t.equal( actual.length, 5, 'returned array has five elements' );
	t.deepEqual( actual, expected, 'returned array is a random draw' );

	t.end();
});

tape( 'the returned function samples without replacement from an array', function test( t ) {
	var expected;
	var actual;
	var sample;

	sample = factory( 789 );

	// Simple array:
	expected = [ 4, 3, 1, 2 ];
	actual = sample( [ 1, 2, 3, 4 ], {
		'replace': false
	});

	// Typed array:
	expected = [ 7, 9, 6, 5, 8 ];
	actual = sample( new Int32Array( [ 5, 6, 7, 8, 9 ] ), {
		'replace': false
	});

	t.equal( isNumberArray( actual ), true, 'returns an array of numbers' );
	t.equal( actual.length, 5, 'returned array has five elements' );
	t.deepEqual( actual, expected, 'returned array is a random draw' );

	t.end();
});

tape( 'the returned function draws a sample with the specified size from an array (with replacement)', function test( t ) {
	var expected;
	var actual;
	var sample;

	sample = factory( 789 );

	// Simple array:
	expected = [ 0, 0, 0, 1, 0, 1, 1, 1, 1, 1 ];
	actual = sample( [ 0, 1 ], {
		'size': 10
	});

	t.equal( isNumberArray( actual ), true, 'returns an array of numbers' );
	t.equal( actual.length, 10, 'returned array has ten elements' );
	t.deepEqual( actual, expected, 'returned array is a random draw' );

	// Typed array:
	expected = [ 1, 1, 1, 0, 0, 1, 0, 1, 1, 0 ];
	actual = sample( new Int8Array( [ 0, 1 ] ), {
		'size': 10
	});

	t.equal( isNumberArray( actual ), true, 'returns an array of numbers' );
	t.equal( actual.length, 10, 'returned array has ten elements' );
	t.deepEqual( actual, expected, 'returned array is a random draw' );

	t.end();
});

tape( 'the returned function draws a sample with the specified size from an array (without replacement)', function test( t ) {
	var expected;
	var actual;
	var sample;

	sample = factory( 656 );

	// Simple array:
	expected = [ 3, 8, 7, 5 ];
	actual = sample( [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 ], {
		'size': 4,
		'replace': false
	});

	t.equal( isNumberArray( actual ), true, 'returns an array of numbers' );
	t.equal( actual.length, 4, 'returned array has four elements' );
	t.deepEqual( actual, expected, 'returned array is a random draw' );

	// Typed array:
	expected = [ 3, 5, 4, 8, 6 ];
	actual = sample( new Int16Array( [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 ] ), {
		'size': 5,
		'replace': false
	});

	t.equal( isNumberArray( actual ), true, 'returns an array of numbers' );
	t.equal( actual.length, 5, 'returned array has five elements' );
	t.deepEqual( actual, expected, 'returned array is a random draw' );

	t.end();
});

tape( 'the returned function draws a sample from an array using specified probabilities (with replacement)', function test( t ) {
	var expected;
	var actual;
	var sample;

	sample = factory( 321 );

	// Simple array:
	expected = [ 4, 5, 2, 2 ];
	actual = sample( [ 1, 2, 3, 4, 5, 6 ], {
		'size': 4,
		'probs': [ 1/12, 2/6, 2/6, 1/12, 1/12, 1/12 ]
	});

	t.equal( isNumberArray( actual ), true, 'returns an array of numbers' );
	t.equal( actual.length, 4, 'returned array has four elements' );
	t.deepEqual( actual, expected, 'returned array is a random draw' );

	// Typed array:
	expected = [ 5, 4, 5, 2, 2 ];
	actual = sample( new Int8Array( [ 1, 2, 3, 4, 5, 6 ] ), {
		'size': 5,
		'probs': [ 0.0, 1/12, 1/12, 3/6, 2/6, 0.0 ]
	});

	t.equal( isNumberArray( actual ), true, 'returns an array of numbers' );
	t.equal( actual.length, 5, 'returned array has five elements' );
	t.deepEqual( actual, expected, 'returned array is a random draw' );

	expected = [ 1, 3, 1, 1, 3 ];
	actual = sample( new Int8Array( [ 1, 2, 3, 4 ] ), {
		'size': 5,
		'probs': [ 0.5, 0.2, 0.2, 0.1 ]
	});

	t.equal( isNumberArray( actual ), true, 'returns an array of numbers' );
	t.equal( actual.length, 5, 'returned array has five elements' );
	t.deepEqual( actual, expected, 'returned array is a random draw' );

	t.end();
});

tape( 'the returned function draws a sample from an array using specified probabilities (without replacement)', function test( t ) {
	var expected;
	var actual;
	var sample;

	sample = factory( 589 );

	// Simple array:
	expected = [ 3, 6, 5 ];
	actual = sample( [ 1, 2, 3, 4, 5, 6 ], {
		'size': 3,
		'probs': [ 1/12, 2/6, 2/6, 1/12, 1/12, 1/12 ],
		'replace': false
	});

	t.equal( isNumberArray( actual ), true, 'returns an array of numbers' );
	t.equal( actual.length, 3, 'returned array has three elements' );
	t.deepEqual( actual, expected, 'returned array is a random draw' );

	// Typed array:
	expected = [ 6, 3, 2, 1 ];
	actual = sample( new Int8Array( [ 1, 2, 3, 4, 5, 6 ] ), {
		'size': 4,
		'probs': [ 1/12, 2/6, 2/6, 1/12, 1/12, 1/12 ],
		'replace': false
	});

	t.equal( isNumberArray( actual ), true, 'returns an array of numbers' );
	t.equal( actual.length, 4, 'returned array has four elements' );
	t.deepEqual( actual, expected, 'returned array is a random draw' );

	t.end();
});
<|endoftext|>


<commit_before>var util = require("../util"),
    components = require("graphlib").alg.components,
    PriorityQueue = require("graphlib").data.PriorityQueue,
    Set = require("graphlib").data.Set;

module.exports = function(g, debugLevel) {
  var timer = util.createTimer(debugLevel >= 1);
  timer.wrap("Rank phase", function() {
    initRank(g);

    components(g).forEach(function(cmpt) {
      var subgraph = g.subgraph(cmpt);
      feasibleTree(subgraph);
      normalize(subgraph);
    });
  })();
};

function initRank(g) {
  var minRank = {};
  var pq = new PriorityQueue();

  g.eachNode(function(u) {
    pq.add(u, g.inEdges(u).length);
    minRank[u] = 0;
  });

  while (pq.size() > 0) {
    var minId = pq.min();
    if (pq.priority(minId) > 0) {
      throw new Error("Input graph is not acyclic: " + g.toString());
    }
    pq.removeMin();

    var rank = minRank[minId];
    g.node(minId).rank = rank;

    g.outEdges(minId).forEach(function(e) {
      var target = g.target(e);
      minRank[target] = Math.max(minRank[target], rank + (g.edge(e).minLen || 1));
      pq.decrease(target, pq.priority(target) - 1);
    });
  }
}

function feasibleTree(g) {
  var remaining = new Set(g.nodes()),
      minLen = []; // Array of {u, v, len}

  // Collapse multi-edges and precompute the minLen, which will be the
  // max value of minLen for any edge in the multi-edge.
  var minLenMap = {};
  g.eachEdge(function(e, u, v, edge) {
    var id = incidenceId(u, v);
    if (!(id in minLenMap)) {
      minLen.push(minLenMap[id] = { u: u, v: v, len: 1 });
    }
    minLenMap[id].len = Math.max(minLenMap[id].len, edge.minLen || 1);
  });

  function slack(mle /* minLen entry*/) {
    return Math.abs(g.node(mle.u).rank - g.node(mle.v).rank) - mle.len;
  }

  // Remove arbitrary node - it is effectively the root of the spanning tree.
  remaining.remove(g.nodes()[0]);

  // Finds the next edge with the minimum slack.
  function findMinSlack() {
    var result,
        eSlack = Number.POSITIVE_INFINITY;
    minLen.forEach(function(mle /* minLen entry */) {
      if (remaining.has(mle.u) !== remaining.has(mle.v)) {
        var mleSlack = slack(mle);
        if (mleSlack < eSlack) {
          if (!remaining.has(mle.u)) {
            result = { treeNode: mle.u, graphNode: mle.v, len: mle.len};
          } else {
            result = { treeNode: mle.v, graphNode: mle.u, len: -mle.len };
          }
          eSlack = mleSlack;
        }
      }
    });

    return result;
  }

  while (remaining.size() > 0) {
    var result = findMinSlack();
    remaining.remove(result.graphNode);
    g.node(result.graphNode).rank = g.node(result.treeNode).rank + result.len;
  }
}

function normalize(g) {
  var m = util.min(g.nodes().map(function(u) { return g.node(u).rank; }));
  g.eachNode(function(u, node) { node.rank -= m; });
}

/*
 * This id can be used to group (in an undirected manner) multi-edges
 * incident on the same two nodes.
 */
function incidenceId(u, v) {
  return u < v ?  u.length + ":" + u + "-" + v : v.length + ":" + v + "-" + u;
}
<commit_msg>Make feasible tree explicit<commit_after>var util = require("../util"),
    components = require("graphlib").alg.components,
    PriorityQueue = require("graphlib").data.PriorityQueue,
    Set = require("graphlib").data.Set,
    Digraph = require("graphlib").Digraph;

module.exports = function(g, debugLevel) {
  var timer = util.createTimer(debugLevel >= 1);
  timer.wrap("Rank phase", function() {
    initRank(g);

    components(g).forEach(function(cmpt) {
      var subgraph = g.subgraph(cmpt);
      var tree = feasibleTree(subgraph);
      console.log("Feasible tree: " + JSON.stringify(tree, null, 4));
      // while (true) {
      // var e = leave_edge(subgraph);
      // if (e == null) { break; }
      // var f = enter_edge(e);
      // exchange(e, f);
      // }
      normalize(subgraph);
      // balance(subgraph);
    });
  })();
};

function initRank(g) {
  var minRank = {};
  var pq = new PriorityQueue();

  g.eachNode(function(u) {
    pq.add(u, g.inEdges(u).length);
    minRank[u] = 0;
  });

  while (pq.size() > 0) {
    var minId = pq.min();
    if (pq.priority(minId) > 0) {
      throw new Error("Input graph is not acyclic: " + g.toString());
    }
    pq.removeMin();

    var rank = minRank[minId];
    g.node(minId).rank = rank;

    g.outEdges(minId).forEach(function(e) {
      var target = g.target(e);
      minRank[target] = Math.max(minRank[target], rank + (g.edge(e).minLen || 1));
      pq.decrease(target, pq.priority(target) - 1);
    });
  }
}

function feasibleTree(g) {
  var remaining = new Set(g.nodes()),
      minLen = [], // Array of {u, v, len}
      tree = new Digraph();

  // Collapse multi-edges and precompute the minLen, which will be the
  // max value of minLen for any edge in the multi-edge.
  var minLenMap = {};
  g.eachEdge(function(e, u, v, edge) {
    var id = incidenceId(u, v);
    if (!(id in minLenMap)) {
      minLen.push(minLenMap[id] = { u: u, v: v, len: 1 });
    }
    minLenMap[id].len = Math.max(minLenMap[id].len, edge.minLen || 1);
  });

  function slack(mle /* minLen entry*/) {
    return Math.abs(g.node(mle.u).rank - g.node(mle.v).rank) - mle.len;
  }

  // Remove arbitrary node - it is effectively the root of the spanning tree.
  var root = g.nodes()[0];
  remaining.remove();
  tree.addNode(root, g.node(root));

  // Finds the next edge with the minimum slack.
  function findMinSlack() {
    var result,
        eSlack = Number.POSITIVE_INFINITY;
    minLen.forEach(function(mle /* minLen entry */) {
      if (remaining.has(mle.u) !== remaining.has(mle.v)) {
        var mleSlack = slack(mle);
        if (mleSlack < eSlack) {
          if (!remaining.has(mle.u)) {
            result = { mle: mle, treeNode: mle.u, graphNode: mle.v, len: mle.len};
          } else {
            result = { mle: mle, treeNode: mle.v, graphNode: mle.u, len: -mle.len };
          }
          eSlack = mleSlack;
        }
      }
    });

    return result;
  }

  while (remaining.size() > 0) {
    var result = findMinSlack();
    remaining.remove(result.graphNode);
    tree.addNode(result.graphNode);
    tree.addEdge(result.mle.u, result.mle.v);
    g.node(result.graphNode).rank = g.node(result.treeNode).rank + result.len;
  }

  return tree;
}

function normalize(g) {
  var m = util.min(g.nodes().map(function(u) { return g.node(u).rank; }));
  g.eachNode(function(u, node) { node.rank -= m; });
}

/*
 * This id can be used to group (in an undirected manner) multi-edges
 * incident on the same two nodes.
 */
function incidenceId(u, v) {
  return u < v ?  u.length + ":" + u + "-" + v : v.length + ":" + v + "-" + u;
}
<|endoftext|>


<commit_before>var FS = require('fs'),
    UTIL = require('util'),
    bemUtil = require('./util'),
    PATH = require('./path');

exports.Tech = function(path, techName) {
    this.path = path;
    var tech = require(this.path);
    for(var i in tech) tech.hasOwnProperty(i) && (this[i] = tech[i]);
    if(techName) this.techName = techName;
};

exports.Tech.prototype.old = true;

exports.Tech.setContext = function(ctx) {
    exports.Tech.prototype.context = ctx;
};

exports.Tech.prototype.setContext = function(ctx) {
    this.context = ctx;
    return this;
};

exports.Tech.prototype.getContext = function() {
    return this.context;
};

exports.Tech.prototype.bemBuild = function(prefixes, outputDir, outputName) {
    var _this = this,
        content = this.filterExists(prefixes)
            .map(function(file) {
                return _this.outFile(
                    PATH.relative(outputDir, file),
                    file, outputDir, outputName);
            });

    var filename = this.fileByPrefix(PATH.join(outputDir, outputName));
    FS.writeFileSync(filename, content.join(''));
};

exports.Tech.prototype.bemCreate = function(prefix, vars, force) {
    var file = this.fileByPrefix(prefix);
    if(bemUtil.isFile(file) && !force) {
        UTIL.error('Уже существует ' + file);
        return;
    }
    bemUtil.mkdirs(PATH.dirname(file));
    FS.writeFileSync(file, this.newFileContent(vars));
};

exports.Tech.prototype.filterExists = function(prefixes) {
    var _this = this,
        res = [];
    prefixes.forEach(function(prefix){
        var file = _this.fileByPrefix(prefix);
        bemUtil.isFile(file) && res.push(file);
    });
    return res;
};

exports.Tech.prototype.getTechName = function() {
    if(this.techName) return this.techName;
    return bemUtil.stripModuleExt(PATH.basename(this.getTechPath()));
};

exports.Tech.prototype.getFileSuffix = function() {
    return '.' + this.getTechName();
};

exports.Tech.prototype.fileByPrefix = function(prefix) {
    return prefix + this.getFileSuffix();
};

exports.Tech.prototype.matchSuffix = function(suffix) {
    return this.getFileSuffix() == suffix;
};

exports.Tech.prototype.outFile = function(file) {
    return file + '\n';
};

exports.Tech.prototype.newFileContent = function() {
    return '';
};

exports.Tech.prototype.getFileContent = function(prefix) {
    var file = this.fileByPrefix(prefix);
    if(bemUtil.isFile(file)) {
        return FS.readFileSync(file, 'utf8');
    }
    //UTIL.error('Нет файла ' + file);
    return '';
};

exports.Tech.prototype.getTechRelativePath = function(from) {
    from = PATH.join(from || '.', PATH.dirSep);
    var absPath = this.getTechPath(),
        techPath = PATH.relative(PATH.join(__dirname, PATH.unixToOs('../../')), absPath),

        testDotRe = new RegExp('^[\\.' + PATH.dirSepRe + ']'),
        testLibRe = new RegExp('^.*?' + PATH.dirSepRe + 'lib'),
        replaceRe = new RegExp('^.*?' + PATH.dirSepRe);

    // tech from 'bem' module
    if(!testDotRe.test(techPath) && testLibRe.test(techPath)) {
        techPath = techPath.replace(replaceRe, PATH.unixToOs('bem/'));
    } else {
        // look for tech into node_modules and NODE_PATH env variable
        var shortestPath = PATH.relative(from, absPath);
        shortestPath = shortestPath.split(PATH.dirSep);
        module.paths.concat(bemUtil.getNodePaths()).forEach(function(reqPath) {
            var relPath = PATH.relative(PATH.join(reqPath, PATH.dirSep), absPath);
            if(!/^\./.test(relPath)) {
                relPath = relPath.split(PATH.dirSep);
                if(relPath.length < shortestPath.length) {
                    shortestPath = relPath;
                }
            }
        });

        techPath = PATH.join.apply(null, shortestPath);
        // NOTE: could not replace to PATH.join('.', techPath), because of
        // '.' will be stripped
        if(!/^\./.test(techPath)) techPath = '.' + PATH.dirSep + techPath;
    }

    techPath = bemUtil.stripModuleExt(techPath);

    // NOTE: default tech, need to return empty path for it
    if(techPath == bemUtil.getBemTechPath('default')) return '';
    return techPath;
};

exports.Tech.prototype.getTechPath = function() {
    return this.techModule.filename;
};
<commit_msg>Fix jshint errors in lib/legacy-tech.js<commit_after>'use strict';

var FS = require('fs'),
    UTIL = require('util'),
    bemUtil = require('./util'),
    PATH = require('./path');

exports.Tech = function(path, techName) {
    this.path = path;
    var tech = require(this.path);
    for(var i in tech) tech.hasOwnProperty(i) && (this[i] = tech[i]);
    if(techName) this.techName = techName;
};

exports.Tech.prototype.old = true;

exports.Tech.setContext = function(ctx) {
    exports.Tech.prototype.context = ctx;
};

exports.Tech.prototype.setContext = function(ctx) {
    this.context = ctx;
    return this;
};

exports.Tech.prototype.getContext = function() {
    return this.context;
};

exports.Tech.prototype.bemBuild = function(prefixes, outputDir, outputName) {
    var _this = this,
        content = this.filterExists(prefixes)
            .map(function(file) {
                return _this.outFile(
                    PATH.relative(outputDir, file),
                    file, outputDir, outputName);
            });

    var filename = this.fileByPrefix(PATH.join(outputDir, outputName));
    FS.writeFileSync(filename, content.join(''));
};

exports.Tech.prototype.bemCreate = function(prefix, vars, force) {
    var file = this.fileByPrefix(prefix);
    if(bemUtil.isFile(file) && !force) {
        UTIL.error('Уже существует ' + file);
        return;
    }
    bemUtil.mkdirs(PATH.dirname(file));
    FS.writeFileSync(file, this.newFileContent(vars));
};

exports.Tech.prototype.filterExists = function(prefixes) {
    var _this = this,
        res = [];
    prefixes.forEach(function(prefix){
        var file = _this.fileByPrefix(prefix);
        bemUtil.isFile(file) && res.push(file);
    });
    return res;
};

exports.Tech.prototype.getTechName = function() {
    if(this.techName) return this.techName;
    return bemUtil.stripModuleExt(PATH.basename(this.getTechPath()));
};

exports.Tech.prototype.getFileSuffix = function() {
    return '.' + this.getTechName();
};

exports.Tech.prototype.fileByPrefix = function(prefix) {
    return prefix + this.getFileSuffix();
};

exports.Tech.prototype.matchSuffix = function(suffix) {
    return this.getFileSuffix() === suffix;
};

exports.Tech.prototype.outFile = function(file) {
    return file + '\n';
};

exports.Tech.prototype.newFileContent = function() {
    return '';
};

exports.Tech.prototype.getFileContent = function(prefix) {
    var file = this.fileByPrefix(prefix);
    if(bemUtil.isFile(file)) {
        return FS.readFileSync(file, 'utf8');
    }
    //UTIL.error('Нет файла ' + file);
    return '';
};

exports.Tech.prototype.getTechRelativePath = function(from) {
    from = PATH.join(from || '.', PATH.dirSep);
    var absPath = this.getTechPath(),
        techPath = PATH.relative(PATH.join(__dirname, PATH.unixToOs('../../')), absPath),

        testDotRe = new RegExp('^[\\.' + PATH.dirSepRe + ']'),
        testLibRe = new RegExp('^.*?' + PATH.dirSepRe + 'lib'),
        replaceRe = new RegExp('^.*?' + PATH.dirSepRe);

    // tech from 'bem' module
    if(!testDotRe.test(techPath) && testLibRe.test(techPath)) {
        techPath = techPath.replace(replaceRe, PATH.unixToOs('bem/'));
    } else {
        // look for tech into node_modules and NODE_PATH env variable
        var shortestPath = PATH.relative(from, absPath);
        shortestPath = shortestPath.split(PATH.dirSep);
        module.paths.concat(bemUtil.getNodePaths()).forEach(function(reqPath) {
            var relPath = PATH.relative(PATH.join(reqPath, PATH.dirSep), absPath);
            if(!/^\./.test(relPath)) {
                relPath = relPath.split(PATH.dirSep);
                if(relPath.length < shortestPath.length) {
                    shortestPath = relPath;
                }
            }
        });

        techPath = PATH.join.apply(null, shortestPath);
        // NOTE: could not replace to PATH.join('.', techPath), because of
        // '.' will be stripped
        if(!/^\./.test(techPath)) techPath = '.' + PATH.dirSep + techPath;
    }

    techPath = bemUtil.stripModuleExt(techPath);

    // NOTE: default tech, need to return empty path for it
    if(techPath === bemUtil.getBemTechPath('default')) return '';
    return techPath;
};

exports.Tech.prototype.getTechPath = function() {
    return this.techModule.filename;
};
<|endoftext|>


<commit_before>import AtRule from 'postcss/lib/at-rule';
import Comment from 'postcss/lib/comment';
import Parser from 'postcss/lib/parser';

import lessTokenizer from './less-tokenize';

const MixinDefinitionPattern = /^([#.](?:[\w-]|\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+)\s*\(/;

export default class LessParser extends Parser {
    constructor (input, opts) {
        super(input);
        this.mixins = {};
    }

    tokenize () {
        this.tokens = lessTokenizer(this.input);
    }

    comment (token) {
        if (token[6] === 'inline') {
            const node = new Comment();

            this.init(node, token[2], token[3]);
            node.raws.inline = true;
            node.source.end = {line: token[4], column: token[5]};

            const text = token[1].slice(2);

            if (/^\s*$/.test(text)) {
                node.text = '';
                node.raws.left = text;
                node.raws.right = '';
            } else {
                const match = text.match(/^(\s*)([^]*[^\s])(\s*)$/);

                node.text = match[2];
                node.raws.left = match[1];
                node.raws.right = match[3];
            }
        } else {
            super.comment(token);
        }
    }

    rule (token) {
        super.rule(token);

        if (MixinDefinitionPattern.test(this.current.source.input.css)) {
            // this.current is the 'rule' node created in super.rule()
            this.current.name = token[0][1].slice(1);
            this.current.type = 'mixin';
            this.current.definition = true;
            this.current.params = [];

            let blockStart = token.findIndex((x) => x[0] === '(');
            const blockEnd = token.findIndex((x) => x[0] === ')');

            token.forEach((tokn) => {
                if (tokn[0] !== 'mixin-param') {
                    return;
                }

                const index = token.indexOf(tokn);
                const param = new AtRule();
                let pos = blockStart;
                const prev = this.current.params[this.current.params.length - 1];
                let termToken = null;

                param.name = tokn[1].slice(1);
                param.type = 'mixin-param';
                param.source = {
                    start: {line: tokn[2], column: tokn[3]},
                    input: this.input
                };
                param.raws = {before: '', after: '', between: ''};

                if (tokn[6] && tokn[6] === 'var-dict') {
                    param.variableDict = true;
                }

                while (pos < blockEnd) {
                    termToken = token[pos];

                    if (termToken[0] === ';') {
                        param.source.end = {line: termToken[2], column: termToken[3]};
                        param.raws.semicolon = true;
                        blockStart = pos + 1;
                        break;
                    } else if (termToken[0] === 'space') {
                        param.raws.before += termToken[1];

                        if (prev) {
                            prev.raws.after += termToken[1];
                        }
                    }

                    pos += 1;
                }

                // this is the last param in the block
                if (pos === blockEnd) {
                    pos = index;
                    while (pos < blockEnd) {
                        termToken = token[pos];

                        if (termToken[0] === 'space') {
                            param.raws.after += termToken[1];
                        }

                        pos += 1;
                    }

                    param.source.end = {line: tokn[4], column: tokn[5]};
                }

                this.current.params.push(param);
            });
        }
    }
}
<commit_msg>parser can now be opted in to parsing mixins as at rules<commit_after>import AtRule from 'postcss/lib/at-rule';
import Comment from 'postcss/lib/comment';
import Parser from 'postcss/lib/parser';

import lessTokenizer from './less-tokenize';

const MixinDefinitionPattern = /^([#.](?:[\w-]|\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+)\s*\(/;

export default class LessParser extends Parser {
    constructor (input, opts) {
        super(input);
        this.mixins = {};
        this.mixinsAsAtRules = (opts || {}).mixinsAsAtRules;
    }

    tokenize () {
        this.tokens = lessTokenizer(this.input);
    }

    comment (token) {
        if (token[6] === 'inline') {
            const node = new Comment();

            this.init(node, token[2], token[3]);
            node.raws.inline = true;
            node.source.end = {line: token[4], column: token[5]};

            const text = token[1].slice(2);

            if (/^\s*$/.test(text)) {
                node.text = '';
                node.raws.left = text;
                node.raws.right = '';
            } else {
                const match = text.match(/^(\s*)([^]*[^\s])(\s*)$/);

                node.text = match[2];
                node.raws.left = match[1];
                node.raws.right = match[3];
            }
        } else {
            super.comment(token);
        }
    }

    rule (token) {
        super.rule(token);

        if (MixinDefinitionPattern.test(this.current.source.input.css)) {
            // this.current is the 'rule' node created in super.rule()
            this.current.name = token[0][1].slice(1);

            if (this.mixinsAsAtRules === true) {
                this.current.type = 'atrule';
                this.current.lessType = 'mixin';
            } else {
                this.current.type = 'mixin';
            }

            this.current.definition = true;
            this.current.params = [];

            let blockStart = token.findIndex((x) => x[0] === '(');
            const blockEnd = token.findIndex((x) => x[0] === ')');

            token.forEach((tokn) => {
                if (tokn[0] !== 'mixin-param') {
                    return;
                }

                const index = token.indexOf(tokn);
                const param = new AtRule();
                let pos = blockStart;
                const prev = this.current.params[this.current.params.length - 1];
                let termToken = null;

                param.name = tokn[1].slice(1);
                param.type = 'mixin-param';
                param.source = {
                    start: {line: tokn[2], column: tokn[3]},
                    input: this.input
                };
                param.raws = {before: '', after: '', between: ''};

                if (tokn[6] && tokn[6] === 'var-dict') {
                    param.variableDict = true;
                }

                while (pos < blockEnd) {
                    termToken = token[pos];

                    if (termToken[0] === ';') {
                        param.source.end = {line: termToken[2], column: termToken[3]};
                        param.raws.semicolon = true;
                        blockStart = pos + 1;
                        break;
                    } else if (termToken[0] === 'space') {
                        param.raws.before += termToken[1];

                        if (prev) {
                            prev.raws.after += termToken[1];
                        }
                    }

                    pos += 1;
                }

                // this is the last param in the block
                if (pos === blockEnd) {
                    pos = index;
                    while (pos < blockEnd) {
                        termToken = token[pos];

                        if (termToken[0] === 'space') {
                            param.raws.after += termToken[1];
                        }

                        pos += 1;
                    }

                    param.source.end = {line: tokn[4], column: tokn[5]};
                }

                this.current.params.push(param);
            });
        }
    }
}
<|endoftext|>