<commit_before>import util from './util'
/*
 * A generic spatial hash used for comparing
 * @param comparator
 * @param collision - collision function for
 * @param bucketHash - hash function for determining buckets
 *
 * Usage:
 * Can only take in objects with a built in onCollide(object) method. The object must also contain an `_id` parameter to access a unique id
 *
 *
 * Pass in a bucket hash function that will determine the bucket index in which the object is located
 * Pass in the uuid hash function referencing the item
 * Pass in the Collision function for determining the collision of two polygons or game objects (AABB, Separating Axis, etc)
 *
 * compare(item) will return true if there exists a collision
 *
 *
 *
 * TODO:
 * - allow for a set of buckets with which you do not detect collision
 * - enforce that buckethash and _id exist on the object
 *
 * http://gamedev.stackexchange.com/questions/26501/how-does-a-collision-engine-work
 *
 */
class SpatialHash {
  // Takes in a 2d map tile size for calculating the bucket.
  constructor(tileSize=10, comparator, testCollide, bucketHash) {
    this.comparator = comparator
    this.testCollide = testCollide

    // cache:
    // this.cache = {
    //   gob_id:

    // }


    this.buckets = {
      // {
      //   bucket_id: [gob (actual gob objects)]
      // }
      //
    }


    // Required so that you can remove the old reference
    // {
    //   gob_id: {
    //    buckets: [{bucketId: bucket_id, index: idx}...],
    //    gob: gob
    //   }
    // }
    this.gobs = {}

    // this must also have a uuid
    this._id = util.uuid()

    this.tileSize = tileSize
    this.bucketHash = bucketHash || defaultBucketHash
  }

  // returns possible gobs that might collide with given gob
  getPossibleGobs(gob) {
    // TODO: can combine getBuckets with the reduce to halve the bucket calls
    let buckets = this._getBuckets(gob)
    // console.log(buckets.length)

    // let memo = {}
    // for (let i=0; i<buckets.length; i++) {

    //   for (let j=0; j<this.buckets[buckets[i]]; j++) {
    //     this.buckets[buckets[i]][j]

    //   }


    //   // console.log(Object.keys(this.buckets[buckets[i]]).length)
    //   let gobIds = Object.keys(this.buckets[buckets[i]])
    //   for (let j=0; j < gobIds.length; j++) {
    //     if (gob._id !== gobIds[j] && memo[gobIds[j]] == null) {
    //       memo[gobIds[j]] = this.buckets[buckets[i]][gobIds[j]]
    //     }
    //   }
    // }
    // console.log(memo)
    // return [].concat(...buckets.map((bucketId) => {
    //   return this.buckets[bucketId]
    // }))

    // return buckets.reduce((memo, bucketId) => {
    //   let bucket = this.buckets[bucketId]
    //   for (let gobId in bucket) {
    //     if (gob._id !== gobId && memo[gobId] == null) {
    //       memo[gobId] = bucket[gobId]
    //     }
    //   }
    //   return memo
    // }, {})
  }


  add(gob) {
    let previous
    // add this to gob's refs
    gob.addRef(this)

    // remove from all existing buckets if any exist
    if (previous = this.gobs[gob._id]) {
      // for (let i=0; i<gob.buckets; i++) {
      //   this.remove(gob, gob.buckets[i])
      // }
      this.removeAll(gob)
    }

    // get buckets for the current position
    let buckets = this._getBuckets(gob)

    buckets.map((item) => {
      this.addToOneBucket(gob, item)
    })
  }

  // Note: This was added afterwards. The initial implementation can be found in release v0.0.1
  // TODO: stop duplicate checks - contact cache?
  // Note: DOES NOT REMOVE FROM PREVIOUS BUCKETS
  addToOneBucket(gob, bucket) {

    // initialize bucket
    this.buckets[bucket] = this.buckets[bucket] || []
    // add gob to bucket
    this.buckets[bucket].push(gob)
    // this.buckets[bucket][gob._id] = gob
    // update items with the new bucket hash
    this.gobs[gob._id] = this.gobs[gob._id] || { buckets: [], gob }

    this.gobs[gob._id].buckets.push({
      bucketId: bucket,
      index: this.buckets[bucket].length - 1
    })

//     // get the set of gobs that need to be checked for collision, and pass that to checkCollisions

//     // check if it collides with anything else in the bucket
//     let gobsToCheck = {}

//     for (let id in this.buckets[bucket]) {
//       let other = this.gobs[id].gob
//       if (gob._id !== other._id && this.testCollide(other, gob)) {
//         gobsToCheck[other._id] = other
//       }
//     }

//     // execute collisions
//     for (let id in gobsToCheck) {
//       gobsToCheck[id].onCollide(gob)
//       gob.onCollide(gobsToCheck[id])
//     }

  }


  // TODO: remove gob from single bucket
  // TODO: see if there's a better way to remove all previous positions. Right now
  //       X lookups are required, where X is the number of buckets it previously occupied
  // remove(gob, bucket=this.bucketHash(gob, this.tileSize)) {
  //   // calculate all possible buckets its in
  //   this.buckets[bucket] && delete this.buckets[bucket][gob._id]
  // }

  // removes item from all buckets it is in
  // TODO: see if there's a better way to remove all previous positions. Right now
  //       X lookups are required, where X is the number of buckets it previously occupied
  removeAll(gob) {
    let cachedGob
    if (cachedGob = this.gobs[gob._id]) {
      // remove the gob from the bucket
      for (let i=0; i<cachedGob.buckets.length; i++) {
        this.buckets[cachedGob.buckets[i].bucketId].splice(cachedGob.buckets[i].index, 1)
      }

//       for (let bucket in cachedGob.buckets) {<commit_msg>an attempt to optimize spatialhash.<commit_after>import util from './util'
/*
 * A generic spatial hash used for comparing
 * @param comparator
 * @param collision - collision function for
 * @param bucketHash - hash function for determining buckets
 *
 * Usage:
 * Can only take in objects with a built in onCollide(object) method. The object must also contain an `_id` parameter to access a unique id
 *
 *
 * Pass in a bucket hash function that will determine the bucket index in which the object is located
 * Pass in the uuid hash function referencing the item
 * Pass in the Collision function for determining the collision of two polygons or game objects (AABB, Separating Axis, etc)
 *
 * compare(item) will return true if there exists a collision
 *
 *
 *
 * TODO:
 * - allow for a set of buckets with which you do not detect collision
 * - enforce that buckethash and _id exist on the object
 *
 * http://gamedev.stackexchange.com/questions/26501/how-does-a-collision-engine-work
 *
 */
class SpatialHash {
  // Takes in a 2d map tile size for calculating the bucket.
  constructor(tileSize=10, comparator, testCollide, bucketHash) {
    this.comparator = comparator
    this.testCollide = testCollide

    // cache:
    // this.cache = {
    //   gob_id:

    // }


    this.buckets = {
      // {
      //   bucket_id: [gob (actual gob objects)]
      // }
      //
    }


    // Required so that you can remove the old reference
    // {
    //   gob_id: {
    //    buckets: [{bucketId: bucket_id, index: idx}...], represents the index of this gob within that bucket on this spatial hash's this.buckets!!
    //    bucketIndices: {
    //      [bucketId]: this.buckets' index of bucketId. Necessary for adjusting the buckets array!
    //    }
    //    gob: gob
    //   }
    // }
    this.gobs = {}

    // this must also have a uuid
    this._id = util.uuid()

    this.tileSize = tileSize
    this.bucketHash = bucketHash || defaultBucketHash
  }

  // returns possible gobs that might collide with given gob
  getPossibleGobs(gob) {
    // TODO: can combine getBuckets with the reduce to halve the bucket calls
    let buckets = this._getBuckets(gob)
    // console.log(buckets.length)

    // let memo = {}
    // for (let i=0; i<buckets.length; i++) {

    //   for (let j=0; j<this.buckets[buckets[i]]; j++) {
    //     this.buckets[buckets[i]][j]

    //   }


    //   // console.log(Object.keys(this.buckets[buckets[i]]).length)
    //   let gobIds = Object.keys(this.buckets[buckets[i]])
    //   for (let j=0; j < gobIds.length; j++) {
    //     if (gob._id !== gobIds[j] && memo[gobIds[j]] == null) {
    //       memo[gobIds[j]] = this.buckets[buckets[i]][gobIds[j]]
    //     }
    //   }
    // }
    // console.log(memo)
    // return [].concat(...buckets.map((bucketId) => {
    //   return this.buckets[bucketId]
    // }))

    // return buckets.reduce((memo, bucketId) => {
    //   let bucket = this.buckets[bucketId]
    //   for (let gobId in bucket) {
    //     if (gob._id !== gobId && memo[gobId] == null) {
    //       memo[gobId] = bucket[gobId]
    //     }
    //   }
    //   return memo
    // }, {})
  }


  add(gob) {
    let previous
    // add this to gob's refs
    gob.addRef(this)

    // remove from all existing buckets if any exist
    if (previous = this.gobs[gob._id]) {
      // for (let i=0; i<gob.buckets; i++) {
      //   this.remove(gob, gob.buckets[i])
      // }
      this.removeAll(gob)
    }

    // get buckets for the current position
    let buckets = this._getBuckets(gob)

    for (let i=0; i < buckets.length; i++) {
      this.addToOneBucket(gob, buckets[i])
    }
  }

  // Note: This was added afterwards. The initial implementation can be found in release v0.0.1
  // TODO: stop duplicate checks - contact cache?
  // Note: DOES NOT REMOVE FROM PREVIOUS BUCKETS
  addToOneBucket(gob, bucket) {

    // initialize bucket
    this.buckets[bucket] = this.buckets[bucket] || []
    // add gob to bucket
    this.buckets[bucket].push(gob)
    // this.buckets[bucket][gob._id] = gob
    // update items with the new bucket hash
    this.gobs[gob._id] = this.gobs[gob._id] || { buckets: [], bucketIndices: {}, gob }

    this.gobs[gob._id].buckets.push({
      bucketId: bucket,
      index: this.buckets[bucket].length - 1
    })
    this.gobs[gob._id].bucketIndices[bucket] = this.gobs[gob._id].buckets.length - 1

//     // get the set of gobs that need to be checked for collision, and pass that to checkCollisions

//     // check if it collides with anything else in the bucket
//     let gobsToCheck = {}

//     for (let id in this.buckets[bucket]) {
//       let other = this.gobs[id].gob
//       if (gob._id !== other._id && this.testCollide(other, gob)) {
//         gobsToCheck[other._id] = other
//       }
//     }

//     // execute collisions
//     for (let id in gobsToCheck) {
//       gobsToCheck[id].onCollide(gob)
//       gob.onCollide(gobsToCheck[id])
//     }

  }


  // TODO: remove gob from single bucket
  // TODO: see if there's a better way to remove all previous positions. Right now
  //       X lookups are required, where X is the number of buckets it previously occupied
  // remove(gob, bucket=this.bucketHash(gob, this.tileSize)) {
  //   // calculate all possible buckets its in
  //   this.buckets[bucket] && delete this.buckets[bucket][gob._id]
  // }

  // removes item from all buckets it is in
  // TODO: see if there's a better way to remove all previous positions. Right now
  //       X lookups are required, where X is the number of buckets it previously occupied
  removeAll(gob) {
    let cachedGob
    if (cachedGob = this.gobs[gob._id]) {
      // remove the gob from the bucket
      for (let i=0; i<cachedGob.buckets.length; i++) {
        let bucketInfo = cachedGob.buckets[i]

        // this.buckets[bucketInfo.bucketId].splice(bucketInfo.index, 1)

        console.log(this.buckets[bucketInfo.bucketId])
        // instead of splicing, swap it with the final item for performance

        if (this.buckets[bucketInfo.bucketId][bucketInfo.bucketId.length - 1] != null) {
          this.buckets[bucketInfo.bucketId][bucketInfo.index] = this.buckets[bucketInfo.bucketId][bucketInfo.bucketId.length - 1]

          this.buckets[bucketInfo.bucketId].pop()
          // the newly moved gob
          let movedGob = this.buckets[bucketInfo.bucketId][bucketInfo.index]
          console.log(movedGob, bucketInfo.bucketId.length, this.buckets[bucketInfo.bucketId])

          // update the location and other information of the gob that got moved
          let bucketIndexInGob = this.gobs[movedGob._id].bucketIndices[bucketInfo.bucketId]
          this.gobs[movedGob._id].buckets[bucketIndexInGob].index = bucketInfo.index
        }



        // array[array.length-1];
        // array.pop();

      }

//       for (let bucket in cachedGob.buckets) {
{'fraction_non_alphanumeric': 0.13039278815196395, 'fraction_numerical': 0.003300064391500322, 'mean_word_length': 2.3364661654135337, 'pattern_counts': {'":': 0, '<': 14, '<?xml version=': 0, '>': 8, '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': '2926106', 'n_tokens_mistral': 4202, 'n_tokens_neox': 3998, 'n_words': 1145}