<commit_before>const deepEqual = require("deep-equal");

function scatterUser(userId) {
  const userCriteria = this.usersById.get(userId).criteria;
  Object.keys(userCriteria).forEach((criterionKey) => {
    const criterionValue = userCriteria[criterionKey];
    if (!this.userIdsByCriterion.has(criterionKey)) {
      this.userIdsByCriterion.set(criterionKey, new Map());
    }
    const userIdsByCriterionValue = this.userIdsByCriterion.get(criterionKey);
    if (!userIdsByCriterionValue.has(criterionValue)) {
      userIdsByCriterionValue.set(criterionValue, new Set());
    }
    userIdsByCriterionValue.get(criterionValue).add(userId);
  });
}

function clearScatteredUser(userId) {
  const userCriteria = this.usersById.get(userId).criteria;
  Object.keys(userCriteria).forEach((criterionKey) => {
    const criterionValue = userCriteria[criterionKey];
    const userIdsByCriterionValue = this.userIdsByCriterion.get(criterionKey);
    const userIds = userIdsByCriterionValue.get(criterionValue);
    userIds.delete(userId);
    if (0 === userIds.size) {
      userIdsByCriterionValue.delete(criterionValue);
      if (0 === userIdsByCriterionValue.size) {
        this.userIdsByCriterion.delete(criterionKey);
      }
    }
  });
}

function findUserIds(criterionKey, criterionValue) {
  let userIds = new Set();
  if (this.userIdsByCriterion.has(criterionKey)) {
    const userIdsByCriterionValue = this.userIdsByCriterion.get(criterionKey);
    if (userIdsByCriterionValue.has(criterionValue)) {
      userIds = userIdsByCriterionValue.get(criterionValue);
    }
  }
  return userIds;
}

class Middleware {

  constructor(getUserCriteriaById) {
    this.getUserCriteriaById = getUserCriteriaById;
    this.usersById = new Map();
    this.userIdsByCriterion = new Map();
    Object.seal(this);
  }

  async register(socket) {
    const userCriteria = await this.getUserCriteriaById(socket.userId);
    if (this.usersById.has(socket.userId)) {
      const existingUser = this.usersById.get(socket.userId);
      existingUser.sockets.push(socket);
      if (!deepEqual(existingUser.criteria, userCriteria)) {
        clearScatteredUser.call(this, socket.userId);
        existingUser.criteria = userCriteria;
        scatterUser.call(this, socket.userId);
      }
    } else {
      this.usersById.set(socket.userId, {
        criteria: userCriteria,
        sockets: [socket]
      });
      scatterUser.call(this, socket.userId);
    }
  }

  remove(socket) {
    if (this.usersById.has(socket.userId)) {
      const user = this.usersById.get(socket.userId);
      user.sockets.splice(user.sockets.indexOf(socket), 1);
      if (0 === user.sockets.length) {
        clearScatteredUser.call(this, socket.userId);
        this.usersById.delete(socket.userId);
      }
    }
  }

  forward(name, payload, audience) {
    let userIdsMatchingAudience = new Set();
    audience.forEach((criteria) => {
      let userIdsMatchingAllCriteria = new Set();
      let i = 0;
      Object.keys(criteria).forEach((criterionKey) => {
        const criterionValue = criteria[criterionKey];
        if (0 === i || 0 < userIdsMatchingAllCriteria.length) {
          const userIdsMatchingCriterion = findUserIds.call(this, criterionKey, criterionValue);
          userIdsMatchingAllCriteria = 0 === i
            ? userIdsMatchingCriterion
            : new Set([...userIdsMatchingAllCriteria].filter((userId) =>
                userIdsMatchingCriterion.has(userId)));
          i++;
        }
      });
      userIdsMatchingAudience = new Set([...userIdsMatchingAudience, ...userIdsMatchingAllCriteria]);
    });
    [...userIdsMatchingAudience]
      .map((userId) => this.usersById.get(userId))
      .reduce((sockets, user) => sockets.concat(user.sockets), [])
      .forEach((socket) => socket.emit(name, payload));
  }

}

module.exports = class MiddlewareServer {

  constructor(middlewares) {
    this.middlewares = middlewares;
    this.middlewaresByVersion = new Map();
    Object.seal(this);<commit_msg>[1.0.0] Middleware lookups improved<commit_after>const deepEqual = require("deep-equal");

function scatterUser(userId) {
  const userCriteria = this.usersById.get(userId).criteria;
  Object.keys(userCriteria).forEach((criterionKey) => {
    const criterionValue = userCriteria[criterionKey];
    if (!this.userIdsByCriterion.has(criterionKey)) {
      this.userIdsByCriterion.set(criterionKey, new Map());
    }
    const userIdsByCriterionValue = this.userIdsByCriterion.get(criterionKey);
    if (!userIdsByCriterionValue.has(criterionValue)) {
      userIdsByCriterionValue.set(criterionValue, new Set());
    }
    userIdsByCriterionValue.get(criterionValue).add(userId);
  });
}

function clearScatteredUser(userId) {
  const userCriteria = this.usersById.get(userId).criteria;
  Object.keys(userCriteria).forEach((criterionKey) => {
    const criterionValue = userCriteria[criterionKey];
    const userIdsByCriterionValue = this.userIdsByCriterion.get(criterionKey);
    const userIds = userIdsByCriterionValue.get(criterionValue);
    userIds.delete(userId);
    if (0 === userIds.size) {
      userIdsByCriterionValue.delete(criterionValue);
      if (0 === userIdsByCriterionValue.size) {
        this.userIdsByCriterion.delete(criterionKey);
      }
    }
  });
}

function findUserIdsByCriterion(criterionKey, criterionValue) {
  let userIds = new Set();
  if (this.userIdsByCriterion.has(criterionKey)) {
    const userIdsByCriterionValue = this.userIdsByCriterion.get(criterionKey);
    if (userIdsByCriterionValue.has(criterionValue)) {
      userIds = userIdsByCriterionValue.get(criterionValue);
    }
  }
  return userIds;
}

const findUserIdsByCriteria = (criteria) => function (criteriaUserIds, criterionKey, i) {
  let userIds = criteriaUserIds;
  if (0 === i || 0 < criteriaUserIds.length) {
    const criterionUserIds = findUserIdsByCriterion.call(this, criterionKey, criteria[criterionKey]);
    userIds = 0 === i
      ? criterionUserIds
      : new Set([...criteriaUserIds].filter((userId) => criterionUserIds.has(userId)));
  }
  return userIds;
};

function findUserIdsByAudience(audienceUserIds, criteria) {
  return new Set([
    ...audienceUserIds,
    ...Object.keys(criteria).reduce(findUserIdsByCriteria(criteria).bind(this), new Set())
  ]);
}

class Middleware {

  constructor(getUserCriteriaById) {
    this.getUserCriteriaById = getUserCriteriaById;
    this.usersById = new Map();
    this.userIdsByCriterion = new Map();
    Object.seal(this);
  }

  async register(socket) {
    const userCriteria = await this.getUserCriteriaById(socket.userId);
    if (this.usersById.has(socket.userId)) {
      const existingUser = this.usersById.get(socket.userId);
      existingUser.sockets.push(socket);
      if (!deepEqual(existingUser.criteria, userCriteria)) {
        clearScatteredUser.call(this, socket.userId);
        existingUser.criteria = userCriteria;
        scatterUser.call(this, socket.userId);
      }
    } else {
      this.usersById.set(socket.userId, {
        criteria: userCriteria,
        sockets: [socket]
      });
      scatterUser.call(this, socket.userId);
    }
  }

  remove(socket) {
    if (this.usersById.has(socket.userId)) {
      const user = this.usersById.get(socket.userId);
      user.sockets.splice(user.sockets.indexOf(socket), 1);
      if (0 === user.sockets.length) {
        clearScatteredUser.call(this, socket.userId);
        this.usersById.delete(socket.userId);
      }
    }
  }

  forward(name, payload, audience) {
    [...audience.reduce(findUserIdsByAudience.bind(this), new Set())]
      .map((userId) => this.usersById.get(userId))
      .reduce((sockets, user) => sockets.concat(user.sockets), [])
      .forEach((socket) => socket.emit(name, payload));
  }

}

module.exports = class MiddlewareServer {

  constructor(middlewares) {
    this.middlewares = middlewares;
    this.middlewaresByVersion = new Map();
    Object.seal(this);
{'fraction_non_alphanumeric': 0.12703213610586012, 'fraction_numerical': 0.002268431001890359, 'mean_word_length': 3.7520958083832334, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 18, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4517858', 'n_tokens_mistral': 2438, 'n_tokens_neox': 2482, 'n_words': 371}