/******************************************************************************
 * Copyright 2021 TypeFox GmbH
 * This program and the accompanying materials are made available under the
 * terms of the MIT License, which is available in the project root.
 ******************************************************************************/ /**
 * A stream is a read-only sequence of values. While the contents of an array can be accessed
 * both sequentially and randomly (via index), a stream allows only sequential access.
 *
 * The advantage of this is that a stream can be evaluated lazily, so it does not require
 * to store intermediate values. This can boost performance when a large sequence is
 * processed via filtering, mapping etc. and accessed at most once. However, lazy
 * evaluation means that all processing is repeated when you access the sequence multiple
 * times; in such a case, it may be better to store the resulting sequence into an array.
 */ export interface Stream < T > extends Iterable < T > { /**
     * Returns an iterator for this stream. This is the same as calling the `Symbol.iterator` function property.
     */ iterator ( ) : IterableIterator < T > ; /**
     * Determines whether this stream contains no elements.
     */ isEmpty ( ) : boolean ; /**
     * Determines the number of elements in this stream.
     */ count ( ) : number ; /**
     * Collects all elements of this stream into an array.
     */ toArray ( ) : T [ ] ; /**
     * Collects all elements of this stream into a Set.
     */ toSet ( ) : Set < T > ; param keyFn param valueFn toMap < K = T , V = T > ( keyFn ? , valueFn ? ) : Map < K , V > ; /**
     * Returns a string representation of a stream.
     */ toString ( ) : string ; param other concat < T2 > ( other ) : Stream < T | T2 > ; param separator join ( separator ? ) : string ; param searchElement param fromIndex indexOf ( searchElement , fromIndex ? ) : number ; param predicate every < S extends T > ( predicate ) : this is Stream < S > ; every ( predicate ) : boolean ; param predicate some ( predicate ) : boolean ; param callbackfn forEach ( callbackfn ) : void ; param callbackfn map < U > ( callbackfn ) : Stream < U > ; param predicate filter < S extends T > ( predicate ) : Stream < S > ; filter ( predicate ) : Stream < T > ; param callbackfn param initialValue reduce ( callbackfn ) : T | undefined ; reduce < U = T > ( callbackfn , initialValue ) : U ; param callbackfn param initialValue reduceRight ( callbackfn ) : T | undefined ; reduceRight < U = T > ( callbackfn , initialValue ) : U ; param predicate find < S extends T > ( predicate ) : S | undefined ; find ( predicate ) : T | undefined ; param predicate findIndex ( predicate ) : number ; param searchElement includes ( searchElement ) : boolean ; param callback flatMap < U > ( callbackfn ) : Stream < U > ; param depth flat < D extends number = 1 > ( depth ? ) : FlatStream < T , D > ; /**
     * Returns the first element in the stream, or `undefined` if the stream is empty.
     */ head ( ) : T | undefined ; param skipCount tail ( skipCount ? ) : Stream < T > ; param maxSize limit ( maxSize ) : Stream < T > ; param by distinct < Key = T > ( by ? ) : Stream < T > ; } export type FlatStream < T , Depth extends number > = { 'done' : Stream < T > ; 'recur' : T extends Iterable < infer Content > ? FlatStream < Content , MinusOne < Depth > > : Stream < T > ; } [ Depth extends 0 ? 'done' : 'recur' ] ; export type MinusOne < N extends number > = [ - 1 , 0 , 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , 10 , 11 , 12 , 13 , 14 , 15 , 16 , 17 , 18 , 19 , 20 ] [ N ] ; /**
 * The default implementation of `Stream` works with two input functions:
 *  - The first function creates the initial state of an iteration.
 *  - The second function gets the current state as argument and returns an `IteratorResult`.
 */ export class StreamImpl < S , T > implements Stream < T > { protected readonly startFn : ( ) => S ; protected readonly nextFn : ( state : S ) => IteratorResult < T > ; constructor ( startFn , nextFn ) { this . startFn = startFn ; this . nextFn = nextFn ; } iterator ( ) { const iterator = { state : this . startFn ( ) , next : ( ) => this . nextFn ( iterator . state ) , [ Symbol . iterator ] : ( ) => iterator } ; return iterator ; } [ Symbol . iterator ] ( ) { return this . iterator ( ) ; } isEmpty ( ) { const iterator = this . iterator ( ) ; return ! ! iterator . next ( ) . done ; } count ( ) { const iterator = this . iterator ( ) ; let count = 0 ; let next = iterator . next ( ) ; while ( ! next . done ) { count ++ ; next = iterator . next ( ) ; } return count ; } toArray ( ) { const result = [ ] ; const iterator = this . iterator ( ) ; let next ; do { next = iterator . next ( ) ; if ( next . value !== undefined ) { result . push ( next . value ) ; } } while ( ! next . done ) ; return result ; } toSet ( ) { return new Set ( this ) ; } toMap < K = T , V = T > ( keyFn ? , valueFn ? ) { const entryStream = this . map ( element => < [ K , V ] > [ keyFn ? keyFn ( element ) : element , valueFn ? valueFn ( element ) : element ] ) ; return new Map ( entryStream ) ; } toString ( ) { return this . join ( ) ; } concat < T2 > ( other ) { const iterator = other [ Symbol . iterator ] ( ) ; return new StreamImpl < { first : S ; firstDone : boolean ; } , T | T2 > ( ( ) => ( { first : this . startFn ( ) , firstDone : false } ) , state => { let result ; if ( ! state . firstDone ) { do { result = this . nextFn ( state . first ) ; if ( ! result . done ) { return result ; } } while ( ! result . done ) ; state . firstDone = true ; } do { result = iterator . next ( ) ; if ( ! result . done ) { return result ; } } while ( ! result . done ) ; return DONE_RESULT ; } ) ; } join ( separator = ',' ) { const iterator = this . iterator ( ) ; let value = '' ; let result ; let addSeparator = false ; do { result = iterator . next ( ) ; if ( ! result . done ) { if ( addSeparator ) { value += separator ; } value += toString ( result . value ) ; } addSeparator = true ; } while ( ! result . done ) ; return value ; } indexOf ( searchElement , fromIndex = 0 ) { const iterator = this . iterator ( ) ; let index = 0 ; let next = iterator . next ( ) ; while ( ! next . done ) { if ( index >= fromIndex && next . value === searchElement ) { return index ; } next = iterator . next ( ) ; index ++ ; } return - 1 ; } every ( predicate ) { const iterator = this . iterator ( ) ; let next = iterator . next ( ) ; while ( ! next . done ) { if ( ! predicate ( next . value ) ) { return false ; } next = iterator . next ( ) ; } return true ; } some ( predicate ) { const iterator = this . iterator ( ) ; let next = iterator . next ( ) ; while ( ! next . done ) { if ( predicate ( next . value ) ) { return true ; } next = iterator . next ( ) ; } return false ; } forEach ( callbackfn ) { const iterator = this . iterator ( ) ; let index = 0 ; let next = iterator . next ( ) ; while ( ! next . done ) { callbackfn ( next . value , index ) ; next = iterator . next ( ) ; index ++ ; } } map < U > ( callbackfn ) { return new StreamImpl < S , U > ( this . startFn , ( state ) => { const { done , value } = this . nextFn ( state ) ; if ( done ) { return DONE_RESULT ; } else { return { done : false , value : callbackfn ( value ) } ; } } ) ; } filter ( predicate ) { return new StreamImpl < S , T > ( this . startFn , state => { let result ; do { result = this . nextFn ( state ) ; if ( ! result . done && predicate ( result . value ) ) { return result ; } } while ( ! result . done ) ; return DONE_RESULT ; } ) ; } reduce < U > ( callbackfn , initialValue ? ) { const iterator = this . iterator ( ) ; let previousValue = initialValue ; let next = iterator . next ( ) ; while ( ! next . done ) { if ( previousValue === undefined ) { previousValue = next . value ; } else { previousValue = callbackfn ( previousValue , next . value ) ; } next = iterator . next ( ) ; } return previousValue ; } reduceRight < U > ( callbackfn , initialValue ? ) { return this . recursiveReduce ( this . iterator ( ) , callbackfn , initialValue ) ; } protected recursiveReduce < U > ( iterator , callbackfn , initialValue ? ) { const next = iterator . next ( ) ; if ( next . done ) { return initialValue ; } const previousValue = this . recursiveReduce ( iterator , callbackfn , initialValue ) ; if ( previousValue === undefined ) { return next . value ; } return callbackfn ( previousValue , next . value ) ; } find ( predicate ) { const iterator = this . iterator ( ) ; let next = iterator . next ( ) ; while ( ! next . done ) { if ( predicate ( next . value ) ) { return next . value ; } next = iterator . next ( ) ; } return undefined ; } findIndex ( predicate ) { const iterator = this . iterator ( ) ; let index = 0 ; let next = iterator . next ( ) ; while ( ! next . done ) { if ( predicate ( next . value ) ) { return index ; } next = iterator . next ( ) ; index ++ ; } return - 1 ; } includes ( searchElement ) { const iterator = this . iterator ( ) ; let next = iterator . next ( ) ; while ( ! next . done ) { if ( next . value === searchElement ) { return true ; } next = iterator . next ( ) ; } return false ; } flatMap < U > ( callbackfn ) { type FlatMapState = { this : S ; iterator ? : Iterator < U , undefined > ; } ; return new StreamImpl < FlatMapState , U > ( ( ) => ( { this : this . startFn ( ) } ) , ( state ) => { do { if ( state . iterator ) { const next = state . iterator . next ( ) ; if ( next . done ) { state . iterator = undefined ; } else { return next ; } } const { done , value } = this . nextFn ( state . this ) ; if ( ! done ) { const mapped = callbackfn ( value ) ; if ( isIterable ( mapped ) ) { state . iterator = mapped [ Symbol . iterator ] ( ) ; } else { return { done : false , value : mapped } ; } } } while ( state . iterator ) ; return DONE_RESULT ; } ) ; } flat < D extends number = 1 > ( depth ? ) { if ( depth === undefined ) { depth = 1 as D ; } if ( depth <= 0 ) { return this as unknown as FlatStream < T , D > ; } const stream = depth > 1 ? this . flat ( depth - 1 ) as StreamImpl < S , unknown > : this ; type FlatMapState = { this : S ; iterator ? : Iterator < T , undefined > ; } ; return new StreamImpl < FlatMapState , T > ( ( ) => ( { this : stream . startFn ( ) } ) , ( state ) => { do { if ( state . iterator ) { const next = state . iterator . next ( ) ; if ( next . done ) { state . iterator = undefined ; } else { return next ; } } const { done , value } = stream . nextFn ( state . this ) ; if ( ! done ) { if ( isIterable ( value ) ) { state . iterator = value [ Symbol . iterator ] ( ) as Iterator < T > ; } else { return { done : false , value } ; } } } while ( state . iterator ) ; return DONE_RESULT ; } ) as unknown as FlatStream < T , D > ; } head ( ) { const iterator = this . iterator ( ) ; const result = iterator . next ( ) ; if ( result . done ) { return undefined ; } return result . value ; } tail ( skipCount = 1 ) { return new StreamImpl < S , T > ( ( ) => { const state = this . startFn ( ) ; for ( let i = 0 ; i < skipCount ; i ++ ) { const next = this . nextFn ( state ) ; if ( next . done ) { return state ; } } return state ; } , this . nextFn ) ; } limit ( maxSize ) { return new StreamImpl < { size : number ; state : S ; } , T > ( ( ) => ( { size : 0 , state : this . startFn ( ) } ) , state => { state . size ++ ; if ( state . size > maxSize ) { return DONE_RESULT ; } return this . nextFn ( state . state ) ; } ) ; } distinct < Key = T > ( by ? ) { const set = new Set < T | Key > ( ) ; return this . filter ( e => { const value = by ? by ( e ) : e ; if ( set . has ( value ) ) { return false ; } else { set . add ( value ) ; return true ; } } ) ; } } function toString ( item ) { if ( typeof item === 'string' ) { return item as string ; } if ( typeof item === 'undefined' ) { return 'undefined' ; } if ( typeof ( item as any ) . toString === 'function' ) { return ( item as any ) . toString ( ) ; } return Object . prototype . toString . call ( item ) ; } function isIterable < T > ( obj ) { return ! ! obj && typeof ( obj as Iterable < T > ) [ Symbol . iterator ] === 'function' ; } /**
 * An empty stream of any type.
 */ export const EMPTY_STREAM = new StreamImpl < undefined , any > ( ( ) => undefined , ( ) => DONE_RESULT ) ; /**
 * Use this `IteratorResult` when implementing a `StreamImpl` to indicate that there are no more elements in the stream.
 */ export const DONE_RESULT = Object . freeze ( { done : true , value : undefined } ) ; /**
 * Create a stream from one or more iterables or array-likes.
 */ export function stream < T > ( ... collections ) { if ( collections . length === 1 ) { const collection = collections [ 0 ] ; if ( collection instanceof StreamImpl ) { return collection as Stream < T > ; } if ( isIterable ( collection ) ) { return new StreamImpl < Iterator < T , undefined > , T > ( ( ) => collection [ Symbol . iterator ] ( ) , ( iterator ) => iterator . next ( ) ) ; } if ( typeof collection . length === 'number' ) { return new StreamImpl < { index : number ; } , T > ( ( ) => ( { index : 0 } ) , ( state ) => { if ( state . index < collection . length ) { return { done : false , value : collection [ state . index ++ ] } ; } else { return DONE_RESULT ; } } ) ; } } if ( collections . length > 1 ) { type State = { collIndex : number ; iterator ? : Iterator < T , undefined > ; array ? : ArrayLike < T > ; arrIndex : number ; } ; return new StreamImpl < State , T > ( ( ) => ( { collIndex : 0 , arrIndex : 0 } ) , ( state ) => { do { if ( state . iterator ) { const next = state . iterator . next ( ) ; if ( ! next . done ) { return next ; } state . iterator = undefined ; } if ( state . array ) { if ( state . arrIndex < state . array . length ) { return { done : false , value : state . array [ state . arrIndex ++ ] } ; } state . array = undefined ; state . arrIndex = 0 ; } if ( state . collIndex < collections . length ) { const collection = collections [ state . collIndex ++ ] ; if ( isIterable ( collection ) ) { state . iterator = collection [ Symbol . iterator ] ( ) ; } else if ( collection && typeof collection . length === 'number' ) { state . array = collection ; } } } while ( state . iterator || state . array || state . collIndex < collections . length ) ; return DONE_RESULT ; } ) ; } return EMPTY_STREAM ; } /**
 * A tree iterator adds the ability to prune the current iteration.
 */ export interface TreeIterator < T > extends IterableIterator < T > { /**
     * Skip the whole subtree below the last returned element. The iteration continues as if that
     * element had no children.
     */ prune ( ) : void ; } /**
 * A tree stream is used to stream the elements of a tree, for example an AST or CST.
 */ export interface TreeStream < T > extends Stream < T > { iterator ( ) : TreeIterator < T > ; } /**
 * The default implementation of `TreeStream` takes a root element and a function that computes the
 * children of its argument. The root is not included in the stream.
 */ export class TreeStreamImpl < T > extends StreamImpl < { iterators : Array < Iterator < T > > ; pruned : boolean ; } , T > implements TreeStream < T > { constructor ( root , children ) { super ( ( ) => ( { iterators : [ children ( root ) [ Symbol . iterator ] ( ) ] , pruned : false } ) , state => { if ( state . pruned ) { state . iterators . pop ( ) ; state . pruned = false ; } while ( state . iterators . length > 0 ) { const iterator = state . iterators [ state . iterators . length - 1 ] ; const next = iterator . next ( ) ; if ( next . done ) { state . iterators . pop ( ) ; } else { state . iterators . push ( children ( next . value ) [ Symbol . iterator ] ( ) ) ; return next ; } } return DONE_RESULT ; } ) ; } iterator ( ) { const iterator = { state : this . startFn ( ) , next : ( ) => this . nextFn ( iterator . state ) , prune : ( ) => { iterator . state . pruned = true ; } , [ Symbol . iterator ] : ( ) => iterator } ; return iterator ; } } /**
 * A set of utility functions that reduce a stream to a single value.
 */ export namespace Reduction { /**
     * Compute the sum of a number stream.
     */ export function sum ( stream ) { return stream . reduce ( ( a , b ) => a + b , 0 ) ; } /**
     * Compute the product of a number stream.
     */ export function product ( stream ) { return stream . reduce ( ( a , b ) => a * b , 0 ) ; } /**
     * Compute the minimum of a number stream. Returns `undefined` if the stream is empty.
     */ export function min ( stream ) { return stream . reduce ( ( a , b ) => Math . min ( a , b ) ) ; } /**
     * Compute the maximum of a number stream. Returns `undefined` if the stream is empty.
     */ export function max ( stream ) { return stream . reduce ( ( a , b ) => Math . max ( a , b ) ) ; } }