<commit_before>'use strict';

const _ = require('lodash');

module.exports = function versionAttributeMain() {
  /**
   * Function to see if we can descend down this
   * tree object from the passed path.
   *
   * @param {array} pathCrumbs
   *   An array collection of version paths, one per member, in order.
   *   The path crumbs indicates from where we will try and descend.
   * @param {object} tree
   *   An object with version keys and possible other properties.
   *
   * @return {array}
   *   The furthest descension path possible from this starting point
   *   as an array collection of version paths, one per member, in order.
   */
  const descendPath = function descender(pathCrumbs, tree) {
    // We need to mutate and reference the original.
    const tempCrumbs = _.cloneDeep(pathCrumbs);
    // The branch we will descend.
    let descentPath = _.get(tree, tempCrumbs);
    let newArray = [];
    let prep = [];
    let sentinel = false;

    // Keep going until we no longer have a branch to descend.
    while (descentPath) {
      // Does this path have children?
      // Get any keys, including non-version keys.
      // Sort them, and convert the inverted keys to Ints or (NaNs).
      prep = _.chain(descentPath)
        .keys()
        .invert()
        .keysIn()
        .map(_.parseInt)
        .value();

       // Do we have any items?
      if (prep.length > 0) {
        // This particular path has properties.
        // It may not have version key children though.
        newArray = _.filter(prep, (n) => {
          if (isNaN(n)) {
            // We don't want any NaNs.
            return false;
          }
          // Only version keys.
          return true;
        });

        // If Anything is left we can descend further.
        if (newArray.length) {
          // Add the next lowest item to the parent path
          // to make a new descent path.
          tempCrumbs.push(_.parseInt(newArray[newArray.length - 1]));
          descentPath = _.get(tree, tempCrumbs);
          // Make sure we know that we descended atleast once.
          sentinel = true;
        }
        else {
          break;
        }
      }
      else {
        // We only had non-version keys, we were at the furthest we could go.
        break;
      }
    }
    // If we were ever able to descend, sentinel
    // is true and we can return how far we got.
    // If sentinel is false no descension possible.
    return sentinel ? tempCrumbs : [];
  };

  /**
   * Function to see if we can ascend up this
   * tree object from the passed path.
   *
   * @param {array} pathCrumbs
   *   An array collection of version paths, one per member, in order.
   *   The path crumbs indicates from where we will try and ascend.
   * @param {object} tree
   *   An object with version keys and possible other properties.
   *
   * @return {array}
   *   The next ascendsion path up as an array collection of
   *   version paths, one per member, in order.
   */
  const ascendPath = function ascender(pathCrumbs, tree) {
    let parentCrumbs = [];
    let parent = {};
    let keys = [];
    let keyReturn = [];
    let tempKeyReturn = [];
    let i;
    let item;

    const crumbsNanTest = function NanTest(n) {
      // If n is not a number, it is another kind of property.
      if (isNaN(_.parseInt(n))) {
        return false;
      }
      // If the current item is larger or equal to pathCrumbs, we remove it.
      return n >= pathCrumbs[i];
    };

    for (i = pathCrumbs.length - 1; i >= 0; i--) {
      // From the end to the begining, get slices at i.
      parentCrumbs = _.slice(pathCrumbs, 0, i);
      parent = _.get(tree, parentCrumbs);

      // Are we on the base value?
      if (i === 0) {
        // If we've gotten here, the only ascension possible is moving the base.
        if (pathCrumbs[i] === 0) {
          // We can't go higher than 0, no ascension possible.
          parentCrumbs = [];
          break;
        }
        // To move the base we need parent as the tree at root.
        parent = tree;
        // We also need to specially handle non-existant keys case.
        keys = _.keysIn(parent);
        // Do we actually have keys higher up the tree or equal to the path?
        keyReturn = _.chain(keys)
        .filter((n) => n <= pathCrumbs[0])
        .value();
        // This a special case of special case.
        if (keyReturn.length === 1) {
          // Like <NAME>, we need to know if we can get much higher.
          tempKeyReturn = _.filter(keyReturn, (n) => n < pathCrumbs[0]);
          if (tempKeyReturn.length > 0) {
            // Yeah, we can.
            parentCrumbs = [keys[_.findLastKey(keys, (n) => (
               n <= pathCrumbs[0]
            ))]];
            break;
          }
          // Nope, this is the paramount location. No ascendancy possible.
          parentCrumbs = [];
          break;
        }
      }
      // Attempt to ascend by getting key children of the parent.
      // We want to see if removing the current key, gives us any siblings.
      item = _.chain(parent)
        .keysIn()
        .sort()
        .map(_.parseInt)
        .without(i)
        .value();

      // Check to see if we are at the lowest possible level in this process.
      if (i === (pathCrumbs.length - 1)) {
        // In this case we need to remove any value that is not version key.
        // We also need to remove any version key that is deeper.
        _.remove(item, (n) => crumbsNanTest(n));
      }

      if (item.length) {
        _.remove(item, isNaN);
        if (item.length > 0) {
          parentCrumbs.push(item[item.length - 1]);
        }
        // Parent crumbs is as far as it can go, return.
        break;
      }
    }
    return parentCrumbs;
  };

  /**
   * Function to return the next closest version number
   * to the one we've passed.
   *
   * @param {array} pathCrumbs
   *   An array collection of version paths, one per member, in order.
   *   The path crumbs indicates from where we will start looking.
   * @param {object} tree
   *   An object with version keys and possible other properties.
   *
   * @return {array}
   *   The nearest path upwards, up as an array collection of
   *   version paths, one per member, in order.
   */
  const getPreviousClosestVersion = function previousVersionGetter(pathCrumbs, tree) {
    let ascendPathCrumbs;
    let tempPathCrumbs;
    // Try to ascend - will give us the first real path above our target.
    ascendPathCrumbs = ascendPath(pathCrumbs, tree);
    if (_.isEqual(ascendPathCrumbs, [])) {
      // We could not ascend we are finished.
      return [];
    }

    // We will now try and descend.
    tempPathCrumbs = _.cloneDeep(ascendPathCrumbs);

    while (tempPathCrumbs.length !== 0) {
      // keep going until we get a
      tempPathCrumbs = descendPath(ascendPathCrumbs, tree);
      // Test for [].
      if (tempPathCrumbs.length === 0) {
        // We could not descend.
        break;
      }

      // Test if this is pathCrumbs.
      if (!(_.isMatch(tempPathCrumbs, pathCrumbs))) {
        // We could descend.  See if we can descend again.
        ascendPathCrumbs = _.clone(tempPathCrumbs, true);
      }
      else {
        // This was path crumbs.
        // We should pass ascend crumbs without modification
        // since modifying it will put us in an unending loop.<commit_msg>fix(functions): Resolves #3 trailing 0 version attributes can now ascend and descend to the closest attribute properly.<commit_after>'use strict';

const _ = require('lodash');

module.exports = function versionAttributeMain() {
  /**
   * Function to see if we can descend down this
   * tree object from the passed path.
   *
   * @param {array} pathCrumbs
   *   An array collection of version paths, one per member, in order.
   *   The path crumbs indicates from where we will try and descend.
   * @param {object} tree
   *   An object with version keys and possible other properties.
   *
   * @return {array}
   *   The furthest descension path possible from this starting point
   *   as an array collection of version paths, one per member, in order.
   */
  const descendPath = function descender(pathCrumbs, tree) {
    // We need to mutate and reference the original.
    const tempCrumbs = _.cloneDeep(pathCrumbs);
    // The branch we will descend.
    let descentPath = _.get(tree, tempCrumbs);
    let newArray = [];
    let prep = [];
    let sentinel = false;

    // Keep going until we no longer have a branch to descend.
    while (descentPath) {
      // Does this path have children?
      // Get any keys, including non-version keys.
      // Sort them, and convert the inverted keys to Ints or (NaNs).
      prep = _.chain(descentPath)
        .keys()
        .invert()
        .keysIn()
        .map(_.parseInt)
        .value();

       // Do we have any items?
      if (prep.length > 0) {
        // This particular path has properties.
        // It may not have version key children though.
        newArray = _.filter(prep, (n) => {
          if (isNaN(n)) {
            // We don't want any NaNs.
            return false;
          }
          // Only version keys.
          return true;
        });

        // If Anything is left we can descend further.
        if (newArray.length) {
          // Add the next lowest item to the parent path
          // to make a new descent path.
          tempCrumbs.push(_.parseInt(newArray[newArray.length - 1]));
          descentPath = _.get(tree, tempCrumbs);
          // Make sure we know that we descended atleast once.
          sentinel = true;
        }
        else {
          break;
        }
      }
      else {
        // We only had non-version keys, we were at the furthest we could go.
        break;
      }
    }
    // If we were ever able to descend, sentinel
    // is true and we can return how far we got.
    // If sentinel is false no descension possible.
    return sentinel ? tempCrumbs : [];
  };

  /**
   * Function to see if we can ascend up this
   * tree object from the passed path.
   *
   * @param {array} pathCrumbs
   *   An array collection of version paths, one per member, in order.
   *   The path crumbs indicates from where we will try and ascend.
   * @param {object} tree
   *   An object with version keys and possible other properties.
   *
   * @return {array}
   *   The next ascendsion path up as an array collection of
   *   version paths, one per member, in order.
   */
  const ascendPath = function ascender(pathCrumbs, tree) {
    let parentCrumbs = [];
    let parent = {};
    let keys = [];
    let keyReturn = [];
    let tempKeyReturn = [];
    let i;
    let item;

    const crumbsNanTest = function NanTest(n) {
      // If n is not a number, it is another kind of property.
      if (isNaN(_.parseInt(n))) {
        return false;
      }
      // If the current item is larger or equal to pathCrumbs, we remove it.
      return n >= pathCrumbs[i];
    };

    for (i = pathCrumbs.length - 1; i >= 0; i--) {
      // From the end to the begining, get slices at i.
      parentCrumbs = _.slice(pathCrumbs, 0, i);
      parent = _.get(tree, parentCrumbs);

      // Are we on the base value?
      if (i === 0) {
        // If we've gotten here, the only ascension possible is moving the base.
        if (pathCrumbs[i] === 0) {
          // We can't go higher than 0, no ascension possible.
          parentCrumbs = [];
          break;
        }

        // To move the base we need parent as the tree at root.
        parent = tree;
        // We also need to specially handle non-existant keys case.
        keys = _.keysIn(parent);
        // Do we actually have keys higher up the tree or equal to the path?
        keyReturn = _.chain(keys)
        .map(_.parseInt)
        .filter((n) => n <= pathCrumbs[0])
        .value();
        // This a special case of special case.
        if (keyReturn.length === 1 || _.without(_.drop(pathCrumbs, 1), 0).length === 0) {
          // Like <NAME>, we need to know if we can get much higher.
          tempKeyReturn = _.filter(keyReturn, (n) => n < pathCrumbs[0]);
          if (tempKeyReturn.length > 0) {
            // Yeah, we can.
            parentCrumbs = [keys[_.findLastKey(keys, (n) => (
               n < pathCrumbs[0]
            ))]];
            break;
          }
          // Nope, this is the paramount location. No ascendancy possible.
          parentCrumbs = [];
          break;
        }
      }
      // Attempt to ascend by getting key children of the parent.
      // We want to see if removing the current key, gives us any siblings.
      item = _.chain(parent)
        .keysIn()
        .sort()
        .map(_.parseInt)
        .without(i)
        .value();

      // Check to see if we are at the lowest possible level in this process.
      if (i === (pathCrumbs.length - 1)) {
        // In this case we need to remove any value that is not version key.
        // We also need to remove any version key that is deeper.
        _.remove(item, (n) => crumbsNanTest(n));
      }

      if (item.length) {
        _.remove(item, isNaN);
        if (item.length > 0) {
          parentCrumbs.push(item[item.length - 1]);
        }
        // Parent crumbs is as far as it can go, return.
        break;
      }
    }
    return parentCrumbs;
  };

  /**
   * Function to return the next closest version number
   * to the one we've passed.
   *
   * @param {array} pathCrumbs
   *   An array collection of version paths, one per member, in order.
   *   The path crumbs indicates from where we will start looking.
   * @param {object} tree
   *   An object with version keys and possible other properties.
   *
   * @return {array}
   *   The nearest path upwards, up as an array collection of
   *   version paths, one per member, in order.
   */
  const getPreviousClosestVersion = function previousVersionGetter(pathCrumbs, tree) {
    let ascendPathCrumbs;
    let tempPathCrumbs;
    // Try to ascend - will give us the first real path above our target.
    ascendPathCrumbs = ascendPath(pathCrumbs, tree);
    if (_.isEqual(ascendPathCrumbs, [])) {
      // We could not ascend we are finished.
      return [];
    }

    // We will now try and descend.
    tempPathCrumbs = _.cloneDeep(ascendPathCrumbs);
    while (tempPathCrumbs.length !== 0) {
      // keep going until we get a
      tempPathCrumbs = descendPath(ascendPathCrumbs, tree);
      // Test for [].
      if (tempPathCrumbs.length === 0) {
        // We could not descend.
        break;
      }

      // Test if this is pathCrumbs.
      if (!(_.isMatch(tempPathCrumbs, pathCrumbs))) {
        // We could descend.  See if we can descend again.
        ascendPathCrumbs = _.clone(tempPathCrumbs, true);
      }
      else {
        // This was path crumbs.
        // We should pass ascend crumbs without modification
        // since modifying it will put us in an unending loop.
{'fraction_non_alphanumeric': 0.09822263797942002, 'fraction_numerical': 0.0027395429640518507, 'mean_word_length': 1.9266718811106767, 'pattern_counts': {'":': 0, '<': 11, '<?xml version=': 0, '>': 25, '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': '3792448', 'n_tokens_mistral': 4427, 'n_tokens_neox': 4331, 'n_words': 1686}