/**
 * Created by Bruno Grieder.
 */ import { eq } from './Utils' ; import { Iterable , Iterator } from './Iter' ; const arrayFrom = < A > ( iterator ) : Array < A > => { let a = [ ] ; while ( true ) { const n = iterator . next ( ) ; if ( n . done ) { return a ; } a . push ( n . value ) ; } } ; /**
 * The base class from which all other monads are created
 * Collection is an extension of ES6 Iterable: a collection of elements that can be iterated over
 */ export abstract class Collection < A > implements Iterable < A > { protected _value : Iterable < A > ; protected constructor ( value ) { this . _value = value ; } [ Symbol . iterator ] ( ) { return this . _value [ Symbol . iterator ] ( ) as Iterator < A > ; } build < B > ( next ) { const iter = { [ Symbol . iterator ] : ( ) => { return { next : next } ; } } ; return new ( this . constructor as any ) ( iter ) ; } /**
     * Returns the element at index.
     * The first element is at index 0
     * O(1) if the underlying iterable is indexed, O(n) otherwise
     */ at ( index ) { if ( index < 0 ) { throw new Error ( 'Invalid index: ' + index ) ; } if ( Array . isArray ( this . _value ) ) { return this . _value [ index ] ; } if ( typeof this . _value === 'string' ) { return ( this . _value as string ) . charAt ( index ) as any as A ; } const it = this [ Symbol . iterator ] ( ) ; let i = 0 ; while ( true ) { const n = it . next ( ) ; if ( n . done ) throw new Error ( "No such Element" ) ; if ( i === index ) { return n . value ; } } } /**
     * Creates a Collection by transforming values produced by this Collection with a partial function, dropping those values for which the partial function is not defined.
     */ collect < B > ( filter ) { return ( mapper ) => this . filter ( filter ) . map ( mapper ) ; } /**
     * Concatenates this Collection with another.
     */ concat ( that ) { const thisIt = this [ Symbol . iterator ] ( ) ; const thatIt = that [ Symbol . iterator ] ( ) ; let useThis = true ; const next = ( ) : { done : boolean ; value ? : A ; } => { if ( useThis ) { const n = thisIt . next ( ) ; if ( n . done ) { useThis = false ; return thatIt . next ( ) ; } else { return n ; } } else { return thatIt . next ( ) ; } } ; return this . build ( next ) ; } /**
     * Tests whether this Collection contains a given value as an element.
     */ contains ( elem ) { return this . indexOf ( elem ) !== - 1 ; } /**
     * Counts the number of elements in the Collection which satisfy a predicate.
     */ count ( p ) { return this . filter ( p ) . size ; } /**
     * Advances this Collection past the first n elements, or the length of the Collection, whichever is smaller.
     */ drop ( n ) { const it = this [ Symbol . iterator ] ( ) ; let i = 0 ; const next = ( ) : { done : boolean ; value ? : A ; } => { const nv = it . next ( ) ; if ( nv . done ) { return { done : true } ; } i = i + 1 ; if ( i <= n ) { return next ( ) ; } return { done : false , value : nv . value } ; } ; return this . build < A > ( next ) ; } /**
     * Skips longest sequence of elements of this Collection which satisfy given predicate p, and returns a Collection of the remaining elements.
     */ dropWhile ( p ) { const it = this [ Symbol . iterator ] ( ) ; let i = 0 ; const next = ( ) : { done : boolean ; value ? : A ; } => { const nv = it . next ( ) ; if ( nv . done ) { return { done : true } ; } i = i + 1 ; if ( p ( nv . value ) ) { return next ( ) ; } return { done : false , value : nv . value } ; } ; return this . build < A > ( next ) ; } /**
     * Test whether these two Iters are equal by testing equality on all elements
     * Equality on elements is tested first by using an `equals` method if it exists, or `===` otherwise
     */ equals ( that ) { const thisIt = this [ Symbol . iterator ] ( ) ; const thatIt = that [ Symbol . iterator ] ( ) ; while ( true ) { const thisn = thisIt . next ( ) ; const thatn = thatIt . next ( ) ; const bothDone = thisn . done && thatn . done ; if ( bothDone ) { return true ; } else { if ( eq ( thisn . value , thatn . value ) ) { continue ; } } return false ; } } /**
     * Tests whether a predicate holds for some of the values produced by this Collection
     */ exists ( p ) { return this . filter ( p ) . take ( 1 ) . size === 1 ; } /**
     * Returns a Collection over all the elements of this Collection that satisfy the predicate p.
     */ filter ( filter ) { const it = this [ Symbol . iterator ] ( ) ; const next = ( ) : { done : boolean ; value ? : A ; } => { const n = it . next ( ) ; if ( n . done ) { return { done : true } ; } if ( filter ( n . value ) ) { return { done : false , value : n . value } ; } return next ( ) ; } ; return this . build < A > ( next ) ; } /**
     * Creates a Collection over all the elements of this Collection which do not satisfy a predicate p.
     */ filterNot ( filter ) { return this . filter ( ( value ) => ! filter ( value ) ) ; } /**
     * Creates a new Collection by applying a function to all values produced by this Collection and concatenating the results.
     */ flatMap < B > ( f ) { return this . map < Collection < B > > ( f ) . flatten < B > ( ) ; } /**
     * Converts this Collection of Collections into a Collection formed by the elements of the Collections
     * e.g. seq( seq(1,2), seq(3,4) ).flatten() = seq(1,2,3,4)
     */ flatten < U > ( ) { const it = this [ Symbol . iterator ] ( ) ; let inMain = true ; let subIt ; const iterateInMain = ( ) : { done : boolean ; value ? : U ; } => { inMain = true ; const n = it . next ( ) ; if ( n . done ) return { done : true } ; if ( n . value instanceof Collection ) { subIt = n . value [ Symbol . iterator ] ( ) ; return iterateInSub ( ) ; } return { done : false , value : n . value as any as U } ; } ; const iterateInSub = ( ) : { done : boolean ; value ? : U ; } => { inMain = false ; const n = subIt . next ( ) ; if ( n . done ) return iterateInMain ( ) ; return { done : false , value : n . value } ; } ; const next = ( ) : { done : boolean ; value ? : U ; } => { if ( inMain ) { return iterateInMain ( ) ; } return iterateInSub ( ) ; } ; return this . build ( next ) ; } /**
     * Applies a binary operator to a start value and all elements of Collection, going left to right.
     */ foldLeft < B > ( initialValue ) { return ( op ) : B => { const it = this [ Symbol . iterator ] ( ) ; let z = initialValue ; let i = 0 ; for ( let n = it . next ( ) ; ! n . done ; n = it . next ( ) ) { z = op ( z , n . value , i ) ; i = i + 1 ; } return z ; } ; } /**
     * Applies a binary operator to all elements of Collection and a start value, going right to left.
     */ foldRight < B > ( initialValue ) { return ( op ) : B => this . reverse . foldLeft ( initialValue ) ( op ) ; } /**
     * Tests whether a predicate holds for all values produced by this Collection
     */ forall ( p ) { const it = this [ Symbol . iterator ] ( ) ; for ( let n = it . next ( ) ; ! n . done ; n = it . next ( ) ) { if ( ! p ( n . value ) ) return false ; } return true ; } /**
     * Applies a function f to all values produced by this Collection
     */ foreach ( f ) { const it = this [ Symbol . iterator ] ( ) ; for ( let n = it . next ( ) ; ! n . done ; n = it . next ( ) ) { f ( n . value ) ; } } /**
     * Tests whether this Collection has a known size.
     */ get hasDefiniteSize ( ) : boolean { return typeof this . _value . length !== 'undefined' || Array . isArray ( this . _value ) ; } /**
     * Selects the first element
     */ get head ( ) : A { const it = this [ Symbol . iterator ] ( ) ; const n = it . next ( ) ; if ( n . done ) throw new Error ( "No such element" ) ; return n . value ; } /**
     * Returns the index of the first occurrence of the specified object in Collection after or at some optional start index.
     */ indexOf ( elem , from ? ) { if ( Array . isArray ( this . _value ) ) { return this . _value . indexOf ( elem , from ) ; } const start = typeof from === 'undefined' ? 0 : from ; const it = this [ Symbol . iterator ] ( ) ; let index = - 1 ; while ( true ) { const n = it . next ( ) ; if ( n . done ) return - 1 ; index = index + 1 ; if ( index >= start ) { if ( eq ( n . value , elem ) ) return index ; } } } /**
     * Tests whether this Collection is empty.
     */ get isEmpty ( ) : boolean { if ( typeof this . _value . length !== 'undefined' ) { return this . _value . length === 0 ; } const it = this [ Symbol . iterator ] ( ) ; return it . next ( ) . done ; } /**
     * Tests whether this Collection is an indexed Collection
     * i.e. its elements can be accessed using an index
     */ get isIndexed ( ) : boolean { return Array . isArray ( this . _value ) || typeof this . _value === 'string' ; } /**
     * Selects the last element.
     */ get last ( ) : A { if ( this . isEmpty ) { throw new Error ( 'No such element: head' ) ; } if ( this . isIndexed ) { return this . at ( this . size - 1 ) ; } if ( typeof this . _value . reverseIterator !== 'undefined' ) { return this . _value . reverseIterator ( ) . next ( ) . value ; } const it = this [ Symbol . iterator ] ( ) ; let last = void 0 ; while ( true ) { const n = it . next ( ) ; if ( n . done ) return last ; last = n . value ; } } /**
     * Returns the number of elements
     */ get length ( ) : number { return this . size ; } /**
     * Creates a new Collection that maps all produced values of this Collection to new values using a transformation function.
     */ map < B > ( f ) { const it = this [ Symbol . iterator ] ( ) ; let i = - 1 ; const next = ( ) : { done : boolean ; value ? : B ; } => { const n = it . next ( ) ; if ( n . done ) { return { done : true } ; } i = i + 1 ; return { done : false , value : f ( n . value , i ) } ; } ; return this . build < B > ( next ) ; } /**
     * Displays all elements of this Collection in a string using an optional separator string.
     */ mkString ( sep ? ) ; /**
     * Displays all elements of this Collection in a string using start, end, and separator strings.
     */ mkString ( start ? , sep ? , end ? ) ; /**
     * Displays all elements of this Collection in a string using start, end, and separator strings.
     */ mkString ( startOrSep ? , sep ? , end ? ) { if ( typeof startOrSep === 'undefined' ) { startOrSep = '' ; sep = '' ; end = '' ; } else if ( typeof sep === 'undefined' ) { sep = startOrSep ; startOrSep = '' ; end = '' ; } return this . foldLeft ( '' ) ( ( s , v , i ) => { return s + ( i == 0 ? startOrSep : sep ) + v . toString ( ) ; } ) + end ; } /**
     * Tests whether the Collection is not empty.
     */ get nonEmpty ( ) : boolean { return ! this . isEmpty ; } /**
     * Returns a new Collection with the elements in reverse order
     * If a reverse iterator is available, it will be used otherwise:
     *      - reversing an indexed Collection will return a linear (non indexed Iter).
     *      - reversing a linear Collection will create an indexed Collection by by loading its content into an im-memory array
     */ get reverse ( ) : Collection < A > { if ( typeof this . _value . reverseIterator !== 'undefined' ) { return new ( this . constructor as any ) ( { [ Symbol . iterator ] : this . _value . reverseIterator , length : this . _value . length , reverseIterator : this . _value [ Symbol . iterator ] } ) ; } if ( this . isIndexed ) { let index = this . length ; const next = ( ) : { done : boolean ; value ? : A ; } => { if ( index <= 0 ) { return { done : true } ; } index = index - 1 ; return { done : false , value : this . at ( index ) } ; } ; return this . build < A > ( next ) ; } return new ( this . constructor as any ) ( this . toArray . reverse ( ) ) ; } /**
     * The size i.e. the number of elements
     */ get size ( ) : number { if ( typeof this . _value . length !== 'undefined' ) { return this . _value . length ; } let count = 0 ; const it = this [ Symbol . iterator ] ( ) ; while ( true ) { if ( it . next ( ) . done ) return count ; count = count + 1 ; } } /**
     * Creates a Collection returning an interval of the values produced by this Collection
     */ slice ( from , until ) { return Array . isArray ( this . _value ) ? new ( this . constructor as any ) ( this . _value . slice ( from , until ) ) : this . drop ( from ) . take ( until - from ) ; } /**
     * Sums up the elements
     */ get sum ( ) : A { const first = this . head ; return this . tail . foldLeft ( first ) ( ( s , v ) => s + v ) ; } /**
     * Selects all elements but the first
     */ get tail ( ) : Collection < A > { return this . drop ( 1 ) ; } /**
     * Selects the first n elements
     */ take ( n ) { const it = this [ Symbol . iterator ] ( ) ; let i = 0 ; const next = ( ) : { done : boolean ; value ? : A ; } => { if ( i === n ) { return { done : true } ; } const next = it . next ( ) ; if ( next . done ) { return { done : true } ; } i = i + 1 ; return { done : false , value : next . value } ; } ; return this . build < A > ( next ) ; } /**
     * Converts this Collection to an array.
     */ get toArray ( ) : Array < A > { return Array . isArray ( this . _value ) ? this . _value . slice ( ) as Array < A > : arrayFrom ( this . _value [ Symbol . iterator ] ( ) as Iterator < A > ) ; } /**
     * Converts this Collection to an indexed Collection if it not already one
     * by creating an in memory array with the content
     */ get toIndexed ( ) : Collection < A > { if ( this . isIndexed ) return this ; return new ( this . constructor as any ) ( this . toArray ) ; } /**
     * Converts this Collection to a string.
     */ get toString ( ) : string { return this . mkString ( ) ; } }