import { ReadonlyTuple } from '@ulangi/extended-types' ; import * as _ from 'lodash' ; export class RandomRangeIterator { private ranges : ReadonlyTuple < number , number > [ ] = [ ] ; public setInitialRange ( range ) { this . ranges = [ range ] ; } public next ( amount ) { const randomRanges = [ ] ; let count = 0 ; while ( this . ranges . length > 0 && count < amount ) { const range = this . pickRandomRange ( ) ; if ( range [ 1 ] - range [ 0 ] > 1 ) { const randomNumber = _ . random ( range [ 0 ] + 1 , range [ 1 ] - 1 ) ; this . splitRange ( randomNumber ) ; } count ++ ; } count = 0 ; while ( this . ranges . length > 0 && count < amount ) { const randomRange = this . popRandomRange ( ) ; randomRanges . push ( randomRange ) ; count ++ ; } randomRanges . forEach ( ( range ) : void => { this . ranges . push ( range ) ; } ) ; const done = this . ranges . length === 0 ? true : false ; return { value : randomRanges , done } ; } public isDone ( ) { return this . ranges . length === 0 ; } public removeOrShortenRangeFromLeft ( num ) { const rangeIndex = this . findIndexOfRangeByNumberInBetween ( num ) ; if ( rangeIndex !== - 1 ) { const range = this . ranges [ rangeIndex ] ; if ( range [ 1 ] === num ) { this . ranges . splice ( rangeIndex , 1 ) ; } else { this . ranges . splice ( rangeIndex , 1 , [ num + 1 , range [ 1 ] ] ) ; } } } public removeExactRange ( range ) { const index = this . findIndexOfExactRange ( range ) ; if ( index !== - 1 ) { this . ranges . splice ( index , 1 ) ; } } private popRandomRange ( ) { const randomIndex = _ . random ( 0 , this . ranges . length - 1 ) ; const target = this . ranges [ randomIndex ] ; this . ranges . splice ( randomIndex , 1 ) ; return target ; } private pickRandomRange ( ) { return this . ranges [ _ . random ( 0 , this . ranges . length - 1 ) ] ; } private splitRange ( num ) { const index = this . findIndexOfRangeByNumberInBetween ( num ) ; if ( index !== - 1 ) { const range = this . ranges [ index ] ; if ( num !== range [ 0 ] && num !== range [ 1 ] ) { this . ranges . splice ( index , 1 , [ range [ 0 ] , num ] ) ; this . ranges . splice ( index + 1 , 0 , [ num + 1 , range [ 1 ] ] ) ; } } } private findIndexOfRangeByNumberInBetween ( num ) { return this . ranges . findIndex ( ( range ) : boolean => { return num >= range [ 0 ] && num <= range [ 1 ] ; } ) ; } private findIndexOfExactRange ( range ) { return this . ranges . findIndex ( ( tempRange ) : boolean => { return tempRange [ 0 ] === range [ 0 ] && tempRange [ 1 ] === range [ 1 ] ; } ) ; } }