<commit_before>var CS = ConstraintSolver;
var PV = PackageVersion;

var pvVar = function (p, v) {
  return p + ' ' + v;
};

CS.Solver = function (input, options) {
  var self = this;
  check(input, CS.Input);

  self.input = input;
  self.errors = []; // [String]

  self.pricer = new CS.VersionPricer();
  self.getConstraintFormula = _.memoize(_getConstraintFormula,
                                         function (p, vConstraint) {
                                           return p + "@" + vConstraint.raw;
                                         });

  self.options = options;

  self.steps = [];
  self.stepsByName = {};

  self.analysis = {};

  self.logic = null; // Logic.Solver, initialized later
};

// A Step consists of a name, an array of terms, and an array of weights.
//
// A term can be a package name, a package version, or any other variable
// name or Logic formula.
//
// A weight is a non-negative integer.  The weights array can be a single
// weight (which is used for all terms).
//
// The terms and weights arguments each default to [].  You can add terms
// with weights using addTerm.
//
// options is optional.
CS.Solver.Step = function (name, terms, weights) {
  check(name, String);
  terms = terms || [];
  check(terms, [String]);
  weights = (weights == null ? [] : weights);
  check(weights, Match.OneOf([Logic.WholeNumber], Logic.WholeNumber));

  this.name = name;

  // mutable:
  this.terms = terms;
  this.weights = weights;
  this.optimum = null; // set when optimized
};

// If weights is a single number, you can omit the weight argument.
// Adds a term.  If weight is 0, addTerm may skip it.
CS.Solver.Step.prototype.addTerm = function (term, weight) {
  if (weight == null) {
    if (typeof this.weights !== 'number') {
      throw new Error("Must specify a weight");
    }
    weight = this.weights;
  }
  check(weight, Logic.WholeNumber);
  if (weight !== 0) {
    this.terms.push(term);
    if (typeof this.weights === 'number') {
      if (weight !== this.weights) {
        throw new Error("Can't specify a different weight now: " +
                        weight + " != " + this.weights);
      }
    } else {
      this.weights.push(weight);
    }
  }
};

var DEBUG = false;

// Call as one of:
// * minimize(step)
// * minimize([step1, step2, ...])
// * minimize(stepName, costTerms, costWeights)
CS.Solver.prototype.minimize = function (step, costTerms_, costWeights_) {
  var self = this;

  if (_.isArray(step)) {
    _.each(step, function (st) {
      self.minimize(st);
    });
    return;
  }

  if (typeof step === 'string') {
    var theStep = new CS.Solver.Step(
      step, costTerms_, (costWeights_ == null ? 1 : costWeights_));
    self.minimize(theStep);
    return;
  }

  var logic = self.logic;

  self.steps.push(step);
  self.stepsByName[step.name] = step;

  if (DEBUG) {
    console.log("--- MINIMIZING " + step.name);
  }

  var costWeights = step.weights;
  var costTerms = step.terms;

  self.solution = logic.minimize(
    self.solution, costTerms, costWeights, {
      progress: function (status, cost) {
        if (status === 'improving') {
          if (DEBUG) {
            console.log(cost + " ... trying to improve ...");
          }
        }
      }
    });

  if (! self.solution) {
    // Optimizing shouldn't change satisfiability
    throw new Error("Unexpected unsatisfiability");
  }

  step.optimum = self.solution.getWeightedSum(costTerms, costWeights);
  if (DEBUG) {
    console.log(step.optimum + " is optimal");

    if (step.optimum) {
      _.each(costTerms, function (t, i) {
        var w = (typeof costWeights === 'number' ? costWeights :
                 costWeights[i]);
        if (w && self.solution.evaluate(t)) {
          console.log("    " + w + ": " + t);
        }
      });
    }
  }
};

// Determine the non-zero contributions to the cost function in `step`
// based on the current solution, returning a map from term (usually
// the name of a package or package version) to positive integer cost.
CS.Solver.prototype.getStepContributions = function (step) {
  var self = this;
  var solution = self.solution;
  var contributions = {};
  var weights = step.weights;
  _.each(step.terms, function (t, i) {
    var w = (typeof weights === 'number' ? weights : weights[i]);
    if (w && self.solution.evaluate(t)) {
      contributions[t] = w;
    }
  });
  return contributions;
};

CS.Solver.prototype.analyzeReachability = function () {<commit_msg>Improve comments on Steps and reachability<commit_after>var CS = ConstraintSolver;
var PV = PackageVersion;

var pvVar = function (p, v) {
  return p + ' ' + v;
};

CS.Solver = function (input, options) {
  var self = this;
  check(input, CS.Input);

  self.input = input;
  self.errors = []; // [String]

  self.pricer = new CS.VersionPricer();
  self.getConstraintFormula = _.memoize(_getConstraintFormula,
                                         function (p, vConstraint) {
                                           return p + "@" + vConstraint.raw;
                                         });

  self.options = options;

  self.steps = [];
  self.stepsByName = {};

  self.analysis = {};

  self.logic = null; // Logic.Solver, initialized later
};

// A Step consists of a name, an array of terms, and an array of weights.
// Steps are optimized one by one.  Optimizing a Step means to find
// the minimum whole number value for the weighted sum of the terms,
// and then to enforce in the solver that the weighted sum be that number.
// Thus, when the Steps are optimized in sequence, earlier Steps take
// precedence and will stay minimized while later Steps are optimized.
//
// A term can be a package name, a package version, or any other variable
// name or Logic formula.
//
// A weight is a non-negative integer.  The weights array can be a single
// weight (which is used for all terms).
//
// The terms and weights arguments each default to [].  You can add terms
// with weights using addTerm.
//
// options is optional.
CS.Solver.Step = function (name, terms, weights) {
  check(name, String);
  terms = terms || [];
  check(terms, [String]);
  weights = (weights == null ? [] : weights);
  check(weights, Match.OneOf([Logic.WholeNumber], Logic.WholeNumber));

  this.name = name;

  // mutable:
  this.terms = terms;
  this.weights = weights;
  this.optimum = null; // set when optimized
};

// If weights is a single number, you can omit the weight argument.
// Adds a term.  If weight is 0, addTerm may skip it.
CS.Solver.Step.prototype.addTerm = function (term, weight) {
  if (weight == null) {
    if (typeof this.weights !== 'number') {
      throw new Error("Must specify a weight");
    }
    weight = this.weights;
  }
  check(weight, Logic.WholeNumber);
  if (weight !== 0) {
    this.terms.push(term);
    if (typeof this.weights === 'number') {
      if (weight !== this.weights) {
        throw new Error("Can't specify a different weight now: " +
                        weight + " != " + this.weights);
      }
    } else {
      this.weights.push(weight);
    }
  }
};

var DEBUG = false;

// Call as one of:
// * minimize(step)
// * minimize([step1, step2, ...])
// * minimize(stepName, costTerms, costWeights)
CS.Solver.prototype.minimize = function (step, costTerms_, costWeights_) {
  var self = this;

  if (_.isArray(step)) {
    _.each(step, function (st) {
      self.minimize(st);
    });
    return;
  }

  if (typeof step === 'string') {
    var theStep = new CS.Solver.Step(
      step, costTerms_, (costWeights_ == null ? 1 : costWeights_));
    self.minimize(theStep);
    return;
  }

  var logic = self.logic;

  self.steps.push(step);
  self.stepsByName[step.name] = step;

  if (DEBUG) {
    console.log("--- MINIMIZING " + step.name);
  }

  var costWeights = step.weights;
  var costTerms = step.terms;

  self.solution = logic.minimize(
    self.solution, costTerms, costWeights, {
      progress: function (status, cost) {
        if (status === 'improving') {
          if (DEBUG) {
            console.log(cost + " ... trying to improve ...");
          }
        }
      }
    });

  if (! self.solution) {
    // Optimizing shouldn't change satisfiability
    throw new Error("Unexpected unsatisfiability");
  }

  step.optimum = self.solution.getWeightedSum(costTerms, costWeights);
  if (DEBUG) {
    console.log(step.optimum + " is optimal");

    if (step.optimum) {
      _.each(costTerms, function (t, i) {
        var w = (typeof costWeights === 'number' ? costWeights :
                 costWeights[i]);
        if (w && self.solution.evaluate(t)) {
          console.log("    " + w + ": " + t);
        }
      });
    }
  }
};

// Determine the non-zero contributions to the cost function in `step`
// based on the current solution, returning a map from term (usually
// the name of a package or package version) to positive integer cost.
CS.Solver.prototype.getStepContributions = function (step) {
  var self = this;
  var solution = self.solution;
  var contributions = {};
  var weights = step.weights;
  _.each(step.terms, function (t, i) {
    var w = (typeof weights === 'number' ? weights : weights[i]);
    if (w && self.solution.evaluate(t)) {
      contributions[t] = w;
    }
  });
  return contributions;
};

// A "reachable" package is one that is either a root dependency or
// a strong dependency of any version of a reachable package.
// In other words, we walk all strong dependencies starting
// with the root dependencies, and visiting all versions of each
// package.
//
// This analysis is mainly done for performance, because if there are
// extraneous packages in the CatalogCache (for whatever reason) we
// want to spend as little time on them as possible.  It also establishes
// the universe of possible "known" and "unknown" packages we might
// come across.
//
// A more nuanced reachability analysis that takes versions into account
// is probably possible.
CS.Solver.prototype.analyzeReachability = function () {
{'fraction_non_alphanumeric': 0.133426825603526, 'fraction_numerical': 0.0010017028949213663, 'mean_word_length': 2.5204513399153736, 'pattern_counts': {'":': 2, '<': 3, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 10, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6702424', 'n_tokens_mistral': 3109, 'n_tokens_neox': 2980, 'n_words': 964}