90cc55af0196863b89006f6ac1789f115ac89d62964efdbd38505f9076859d75c0d02e1d8f00791dd1164017691c67b92c39d2a472bae199c2797d19d23abf 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249
  1. // Copyright Takatoshi Kondo 2021
  2. //
  3. // Distributed under the MIT License
  4. 'use strict'
  5. const SortedSet = require('js-sdsl').OrderedSet
  6. const debugTrace = require('debug')('number-allocator:trace')
  7. const debugError = require('debug')('number-allocator:error')
  8. /**
  9. * Interval constructor
  10. * @constructor
  11. * @param {Number} low - The lowest value of the interval
  12. * @param {Number} high - The highest value of the interval
  13. */
  14. function Interval (low, high) {
  15. this.low = low
  16. this.high = high
  17. }
  18. Interval.prototype.equals = function (other) {
  19. return this.low === other.low && this.high === other.high
  20. }
  21. Interval.prototype.compare = function (other) {
  22. if (this.low < other.low && this.high < other.low) return -1
  23. if (other.low < this.low && other.high < this.low) return 1
  24. return 0
  25. }
  26. /**
  27. * NumberAllocator constructor.
  28. * The all numbers are set to vacant status.
  29. * Time Complexity O(1)
  30. * @constructor
  31. * @param {Number} min - The maximum number of allocatable. The number must be integer.
  32. * @param {Number} maxh - The minimum number of allocatable. The number must be integer.
  33. */
  34. function NumberAllocator (min, max) {
  35. if (!(this instanceof NumberAllocator)) {
  36. return new NumberAllocator(min, max)
  37. }
  38. this.min = min
  39. this.max = max
  40. this.ss = new SortedSet(
  41. [],
  42. (lhs, rhs) => {
  43. return lhs.compare(rhs)
  44. }
  45. )
  46. debugTrace('Create')
  47. this.clear()
  48. }
  49. /**
  50. * Get the first vacant number. The status of the number is not updated.
  51. * Time Complexity O(1)
  52. * @return {Number} - The first vacant number. If all numbers are occupied, return null.
  53. * When alloc() is called then the same value will be allocated.
  54. */
  55. NumberAllocator.prototype.firstVacant = function () {
  56. if (this.ss.size() === 0) return null
  57. return this.ss.front().low
  58. }
  59. /**
  60. * Allocate the first vacant number. The number become occupied status.
  61. * Time Complexity O(1)
  62. * @return {Number} - The first vacant number. If all numbers are occupied, return null.
  63. */
  64. NumberAllocator.prototype.alloc = function () {
  65. if (this.ss.size() === 0) {
  66. debugTrace('alloc():empty')
  67. return null
  68. }
  69. const it = this.ss.begin()
  70. const low = it.pointer.low
  71. const high = it.pointer.high
  72. const num = low
  73. if (num + 1 <= high) {
  74. // x|----|
  75. this.ss.updateKeyByIterator(it, new Interval(low + 1, high))
  76. } else {
  77. this.ss.eraseElementByPos(0)
  78. }
  79. debugTrace('alloc():' + num)
  80. return num
  81. }
  82. /**
  83. * Use the number. The number become occupied status.
  84. * If the number has already been occupied, then return false.
  85. * Time Complexity O(logN) : N is the number of intervals (not numbers)
  86. * @param {Number} num - The number to request use.
  87. * @return {Boolean} - If `num` was not occupied, then return true, otherwise return false.
  88. */
  89. NumberAllocator.prototype.use = function (num) {
  90. const key = new Interval(num, num)
  91. const it = this.ss.lowerBound(key)
  92. if (!it.equals(this.ss.end())) {
  93. const low = it.pointer.low
  94. const high = it.pointer.high
  95. if (it.pointer.equals(key)) {
  96. // |x|
  97. this.ss.eraseElementByIterator(it)
  98. debugTrace('use():' + num)
  99. return true
  100. }
  101. // x |-----|
  102. if (low > num) return false
  103. // |x----|
  104. if (low === num) {
  105. // x|----|
  106. this.ss.updateKeyByIterator(it, new Interval(low + 1, high))
  107. debugTrace('use():' + num)
  108. return true
  109. }
  110. // |----x|
  111. if (high === num) {
  112. // |----|x
  113. this.ss.updateKeyByIterator(it, new Interval(low, high - 1))
  114. debugTrace('use():' + num)
  115. return true
  116. }
  117. // |--x--|
  118. // x|--|
  119. this.ss.updateKeyByIterator(it, new Interval(num + 1, high))
  120. // |--|x|--|
  121. this.ss.insert(new Interval(low, num - 1))
  122. debugTrace('use():' + num)
  123. return true
  124. }
  125. debugTrace('use():failed')
  126. return false
  127. }
  128. /**
  129. * Deallocate the number. The number become vacant status.
  130. * Time Complexity O(logN) : N is the number of intervals (not numbers)
  131. * @param {Number} num - The number to deallocate. The number must be occupied status.
  132. * In other words, the number must be allocated by alloc() or occupied be use().
  133. */
  134. NumberAllocator.prototype.free = function (num) {
  135. if (num < this.min || num > this.max) {
  136. debugError('free():' + num + ' is out of range')
  137. return
  138. }
  139. const key = new Interval(num, num)
  140. const it = this.ss.upperBound(key)
  141. if (it.equals(this.ss.end())) {
  142. // ....v
  143. if (it.equals(this.ss.begin())) {
  144. // Insert new interval
  145. this.ss.insert(key)
  146. return
  147. }
  148. it.pre()
  149. const low = it.pointer.high
  150. const high = it.pointer.high
  151. if (high + 1 === num) {
  152. // Concat to left
  153. this.ss.updateKeyByIterator(it, new Interval(low, num))
  154. } else {
  155. // Insert new interval
  156. this.ss.insert(key)
  157. }
  158. } else {
  159. if (it.equals(this.ss.begin())) {
  160. // v....
  161. if (num + 1 === it.pointer.low) {
  162. // Concat to right
  163. const high = it.pointer.high
  164. this.ss.updateKeyByIterator(it, new Interval(num, high))
  165. } else {
  166. // Insert new interval
  167. this.ss.insert(key)
  168. }
  169. } else {
  170. // ..v..
  171. const rLow = it.pointer.low
  172. const rHigh = it.pointer.high
  173. it.pre()
  174. const lLow = it.pointer.low
  175. const lHigh = it.pointer.high
  176. if (lHigh + 1 === num) {
  177. if (num + 1 === rLow) {
  178. // Concat to left and right
  179. this.ss.eraseElementByIterator(it)
  180. this.ss.updateKeyByIterator(it, new Interval(lLow, rHigh))
  181. } else {
  182. // Concat to left
  183. this.ss.updateKeyByIterator(it, new Interval(lLow, num))
  184. }
  185. } else {
  186. if (num + 1 === rLow) {
  187. // Concat to right
  188. this.ss.eraseElementByIterator(it.next())
  189. this.ss.insert(new Interval(num, rHigh))
  190. } else {
  191. // Insert new interval
  192. this.ss.insert(key)
  193. }
  194. }
  195. }
  196. }
  197. debugTrace('free():' + num)
  198. }
  199. /**
  200. * Clear all occupied numbers.
  201. * The all numbers are set to vacant status.
  202. * Time Complexity O(1)
  203. */
  204. NumberAllocator.prototype.clear = function () {
  205. debugTrace('clear()')
  206. this.ss.clear()
  207. this.ss.insert(new Interval(this.min, this.max))
  208. }
  209. /**
  210. * Get the number of intervals. Interval is internal structure of this library.
  211. * This function is for debugging.
  212. * Time Complexity O(1)
  213. * @return {Number} - The number of intervals.
  214. */
  215. NumberAllocator.prototype.intervalCount = function () {
  216. return this.ss.size()
  217. }
  218. /**
  219. * Dump the internal structor of the library.
  220. * This function is for debugging.
  221. * Time Complexity O(N) : N is the number of intervals (not numbers)
  222. */
  223. NumberAllocator.prototype.dump = function () {
  224. console.log('length:' + this.ss.size())
  225. for (const element of this.ss) {
  226. console.log(element)
  227. }
  228. }
  229. module.exports = NumberAllocator