• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1/*
2 * Copyright (c) 2021-2025 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16package escompat;
17
18// NOTE: autogenerated file
19
20function asIntOrDefault(n: Number | undefined, def: int): int {
21    if (n === undefined) {
22        return def
23    }
24    return (n!).toInt();
25}
26
27function normalizeIndex(idx: int, len: int): int {
28    if (idx < -len) {
29        return 0
30    }
31    if (idx < 0) {
32        return len + idx
33    }
34    if (idx > len) {
35        return len
36    }
37    return idx
38}
39
40class ArrayKeysIterator<T> implements IterableIterator<number> {
41    private parent: Array<T>
42    private idx: int = 0
43    private isDone: boolean = false
44
45    constructor(parent: Array<T>) {
46        this.parent = parent
47    }
48
49    override next(): IteratorResult<number> {
50        if (this.isDone || this.idx >= this.parent.actualLength) {
51            this.isDone = true
52            return new IteratorResult<number>()
53        }
54        return new IteratorResult<number>(this.idx++ as number)
55    }
56
57    override $_iterator(): IterableIterator<number> {
58        return this
59    }
60}
61
62class FromBuffer {}
63// initialized in _initializerBlock_.ets
64const FROM_BUFFER: FromBuffer;
65
66/**
67 * Represents JS API-compatible Array
68 */
69export class Array<T> implements ReadonlyArray<T>, Iterable<T> {
70    private buffer: FixedArray<NullishType>
71    internal actualLength: int
72
73    override get length(): number {
74        return this.actualLength as number
75    }
76
77    set length(newLen: number) {
78        const len = newLen as int
79        if (len < 0 || len > this.actualLength) {
80            throw new RangeError("can't change length to bigger or negative")
81        }
82        this.actualLength = len
83    }
84
85    public override $_get(index: number): T {
86        return this.$_get(index as int)
87    }
88
89    public $_set(i: number, val: T): void {
90        this.$_set(i as int, val)
91    }
92
93    public native $_get(idx: int): T;
94
95    internal final $_get_unsafe(idx: int): T {
96        return this.buffer[idx] as T
97    }
98
99    public native $_set(idx: int, val: T): void;
100
101    private $_set_unsafe(idx: int, val: T): void {
102        this.buffer[idx] = val
103    }
104
105    /**
106     * Creates a new instance of Array
107     */
108    public constructor(arrayLen: int) {
109        this.buffer = new NullishType[arrayLen]
110        this.actualLength = arrayLen
111    }
112
113    public constructor(arrayLen: number) {
114        this(arrayLen as int)
115    }
116
117    internal constructor(_tag: FromBuffer, buf: FixedArray<NullishType>) {
118        this.buffer = buf
119        this.actualLength = buf.length
120    }
121
122    internal constructor() {
123        this.buffer = new NullishType[4]
124        this.actualLength = 0
125    }
126
127    /**
128     * Creates a new instance of Array based on Object[]
129     *
130     * @param d Array initializer
131     */
132    public constructor(first: T, ...d: T[]) {
133        this.buffer = new NullishType[d.length + 1]
134        this.actualLength = d.length as int + 1
135
136        this.buffer[0] = first
137
138        for (let k: int = 0; k < d.length; k++) {
139            this.$_set_unsafe(k + 1, d[k])
140        }
141    }
142
143    /**
144     * Creates a new instance of Array.
145     *
146     * @param arrayLength amount of elements.
147     *
148     * @param initialValue initial value of elements.
149     *
150     */
151    public static create<T>(arrayLength: number, initialValue: T): Array<T> {
152        let other = new Array<T>(arrayLength as int)
153        other.fill(initialValue)
154        return other
155    }
156
157    /**
158     * Extends Array with new elements to specified length.
159     *
160     * @param arrayLength new length of the array.
161     *
162     * @param initialValue initial value of added elements.
163     *
164     */
165    public extendTo(arrayLength: number, initialValue: T): void {
166        if (arrayLength > Int.MAX_VALUE) {
167            throw new RangeError("arrayLength must be <= int32 max")
168        }
169        const len = arrayLength as int
170        const delta: int = len - this.actualLength
171        if (delta <= 0) {
172            return
173        }
174        this.ensureUnusedCapacity(delta)
175        for (let i: int = 0; i < delta; i++) {
176            this.buffer[this.actualLength + i] = initialValue
177        }
178        this.actualLength = len
179    }
180
181    /**
182     * Shrinks Array to specified length.
183     *
184     * @param arrayLength length at which to shrink.
185     *
186     */
187    public shrinkTo(arrayLength: number): void {
188        if (arrayLength >= this.actualLength) {
189            return
190        }
191        let newLen: int = arrayLength as int
192        if (newLen < 0) {
193            // Convert from signed to unsigned
194            newLen = newLen & Int.MAX_VALUE
195        }
196        const other = this.slice(0, newLen)
197        this.buffer = other.buffer
198        this.actualLength = other.actualLength
199    }
200
201    /**
202     * Creates a new instance of an Array with the specified length
203     *
204     * @param arrayLength The length of the array to be created (optional).
205     *
206     * @returns A new Array instance with the specified length
207     */
208    static $_invoke<T>(): Array<T> {
209        return new Array<T>();
210    }
211
212    /**
213     * Creates a new instance of an Array with the specified length
214     *
215     * @param arrayLength The length of the array to be created (optional).
216     *
217     * @returns A new Array instance with the specified length
218     */
219    static $_invoke<T>(arrayLength?: number): Array<T> {
220        if (arrayLength != undefined) {
221            return new Array<T>(arrayLength);
222        } else {
223            return new Array<T>();
224        }
225    }
226
227    /**
228     * Creates a new instance of an Array with the specified length
229     *
230     * @param items array of values.
231     *
232     * @returns A new Array instance with the specified length
233     */
234    static $_invoke<T>(...items: T[]): Array<T> {
235        if (items.length == 0) {
236            return new Array<T>(0)
237        }
238        return new Array<T>(items[0], ...items.slice(1))
239    }
240
241    /**
242     * Creates a new `Array` instance from `Object[]` primitive array.
243     *
244     * @param iterable an iterable object to convert to an array.
245     *
246     * @returns `Array` intance constructed from `Object[]` primitive array.
247     */
248    public static from<T>(iterable: ArrayLike<T> | Iterable<T>): Array<T> {
249        const ret = new Array<T>()
250        iteratorForEach<T>(iterable.$_iterator(), (x: T): void => {
251            ret.push(x)
252        })
253        return ret
254    }
255
256    /**
257     * Creates a new `Array` instance from `Object[]` ArrayLike.
258     *
259     * @param arr an iterable object to convert to an array.
260     *
261     * @returns `Array` intance constructed from `Object[]` ArrayLike.
262     */
263    public static from<T>(arr: ArrayLike<T>): Array<T> {
264        const ret = new Array<T>(arr.length as int)
265        let i = 0
266        iteratorForEach<T>(arr.$_iterator(), (x: T): void => {
267            ret[i] = x
268            i += 1
269        })
270        return ret
271    }
272
273    /**
274     * Creates a new `Array` instance from `Object[]` primitive array.
275     *
276     * @param iterable an iterable object to convert to an array.
277     *
278     * @param mapfn a mapping function to call on every element of the array.
279     * Every value to be added to the array is first passed through this function, and `mapfn`'s return value
280     * is added to the array instead.
281     *
282     * @returns `Array` intance constructed from `Object[]` primitive array and given function.
283     */
284    public static from<T, U>(iterable: ArrayLike<T> | Iterable<T>, mapfn: (v: T, k: number) => U): Array<U> {
285        const ret = new Array<U>()
286        // NOTE (ikorobkov): Please don't replace idx as int[1] with int-variable, because of value of single variable doesn't change (idx++) into lambda call by unknown reason
287        const idx : FixedArray<int> = new int[1]
288        idx[0] = 0
289        iteratorForEach<T>(iterable.$_iterator(), (x: T): void => {
290            ret.push(mapfn(x, idx[0] as number))
291            idx[0] += 1
292        })
293        return ret
294    }
295
296    public static from<T, U>(values: FixedArray<T>, mapfn: (v: T, k: number) => U): Array<U> {
297        const ret = new Array<U>(values.length)
298        for (let i = 0; i < values.length; ++i) {
299            ret[i] = mapfn(values[i], i)
300        }
301        return ret
302    }
303
304    /**
305    * Creates a new `Array` instance from `Object[]` primitive array.
306    *
307    * @param arr primitive array.
308    *
309    * @returns `Array` intance constructed from `Object[]` primitive array.
310    */
311    public static from<T>(arr: FixedArray<T>): Array<T> {
312        const len = arr.length
313        const ret : FixedArray<NullishType> = new NullishType[len as int]
314        for (let i: int = 0; i < len; i++) {
315            ret[i] = arr[i] as NullishType
316        }
317        return new Array<T>(FROM_BUFFER, ret)
318    }
319
320    private lastIndexOfUndefined(fi: int): int {
321        for (let i = fi; i >= 0; i--) {
322            if (this.$_get_unsafe(i) instanceof undefined) {
323                return i
324            }
325        }
326        return -1
327    }
328
329    private lastIndexOfNull(fi: int): int {
330        for (let i = fi; i >= 0; i--) {
331            if (this.$_get_unsafe(i) instanceof null) {
332                return i
333            }
334        }
335        return -1
336    }
337
338    private lastIndexOfString(val: String, fi: int): int {
339        for (let i = fi; i >= 0; i--) {
340            const tmp = this.$_get_unsafe(i)
341            if (tmp instanceof String) {
342                if (tmp == val) {
343                    return i
344                }
345            }
346        }
347        return -1
348    }
349
350    private lastIndexOfNumber(val: Number, fi: int): int {
351        const unboxedVal: number = val.valueOf()
352        if (isNaN(val)) {
353            return -1
354        }
355        for (let i = fi; i >= 0; i--) {
356            const tmp = this.$_get_unsafe(i)
357            if (tmp instanceof Number) {
358                if (unboxedVal == tmp.unboxed()) {
359                    return i
360                }
361            }
362        }
363        return -1
364    }
365
366    private lastIndexOfFloat(val: Float, fi: int): int {
367        const unboxedVal: float = val.unboxed()
368        if (isNaN(val)) {
369            return -1
370        }
371        for (let i = fi; i >= 0; i--) {
372            const tmp = this.$_get_unsafe(i)
373            if (tmp instanceof Float) {
374                if (unboxedVal == tmp.unboxed()) {
375                        return i
376                }
377            }
378        }
379        return -1
380    }
381
382    private lastIndexOfLong(val: Long, fi: int): int {
383        const unboxedVal: long = val.unboxed()
384        for (let i = fi; i >= 0; i--) {
385            const tmp = this.$_get_unsafe(i)
386            if (tmp instanceof Long) {
387                if (tmp == unboxedVal) {
388                    return i
389                }
390            }
391        }
392        return -1
393    }
394
395    private lastIndexOfInt(val: Int, fi: int): int {
396        const unboxedVal: int = val.unboxed()
397        for (let i = fi; i >= 0; i--) {
398            const tmp = this.$_get_unsafe(i)
399            if (tmp instanceof Int) {
400                if (tmp == unboxedVal) {
401                    return i
402                }
403            }
404        }
405        return -1
406    }
407
408    private lastIndexOfCommon(val: T, fi: int): int {
409        for (let i = fi; i >= 0; i--) {
410            if (val == this.$_get_unsafe(i)) {
411                return i
412            }
413        }
414        return -1
415    }
416
417    private searchUndefined(fi: int, len: int): boolean {
418        for (let i = fi; i < len; i++) {
419            if (this.$_get_unsafe(i) instanceof undefined) {
420                return true
421            }
422        }
423        return false
424    }
425
426    private searchNull(fi: int, len: int): boolean {
427        for (let i = fi; i < len; i++) {
428            if (this.$_get_unsafe(i) instanceof null) {
429                return true
430            }
431        }
432        return false
433    }
434
435    private searchString(val: String, fi: int, len: int): boolean {
436        for (let i = fi; i < len; i++) {
437            const tmp = this.$_get_unsafe(i)
438            if (tmp instanceof String) {
439                if (tmp == val) {
440                    return true
441                }
442            }
443        }
444        return false
445    }
446
447    private searchNumber(val: Number, fi: int, len: int): boolean {
448        const unboxedVal: number = val.valueOf()
449        if (isNaN(unboxedVal)) {
450            for (let i = fi; i < len; i++) {
451                const tmp = this.$_get_unsafe(i)
452                if (tmp instanceof Number) {
453                    if (isNaN(tmp.valueOf())) {
454                        return true
455                    }
456                }
457            }
458        } else {
459            for (let i = fi; i < len; i++) {
460                const tmp = this.$_get_unsafe(i)
461                if (tmp instanceof Number) {
462                    if (unboxedVal == tmp.unboxed()) {
463                        return true
464                    }
465                }
466            }
467        }
468        return false
469    }
470
471    private searchFloat(val: Float, fi: int, len: int): boolean {
472        const unboxedVal: float = val.unboxed()
473        if (isNaN(unboxedVal)) {
474            for (let i = fi; i < len; i++) {
475                const tmp = this.$_get_unsafe(i)
476                if (tmp instanceof Float) {
477                    if (isNaN(tmp.unboxed())) {
478                        return true
479                    }
480                }
481            }
482        } else {
483            for (let i = fi; i < len; i++) {
484                const tmp = this.$_get_unsafe(i)
485                if (tmp instanceof Float) {
486                    if (unboxedVal == tmp.unboxed()) {
487                        return true
488                    }
489                }
490            }
491        }
492        return false
493    }
494
495    private searchLong(val: Long, fi: int, len: int): boolean {
496        const unboxedVal: long = val.unboxed()
497        for (let i = fi; i < len; i++) {
498            const tmp = this.$_get_unsafe(i)
499            if (tmp instanceof Long) {
500                if (tmp == unboxedVal) {
501                    return true
502                }
503            }
504        }
505        return false
506    }
507
508    private searchInt(val: Int, fi: int, len: int): boolean {
509        const unboxedVal: int = val.unboxed()
510        for (let i = fi; i < len; i++) {
511            const tmp = this.$_get_unsafe(i)
512            if (tmp instanceof Int) {
513                if (tmp == unboxedVal) {
514                    return true
515                }
516            }
517        }
518        return false
519    }
520
521    private searchCommon(val: T, fi: int, len: int): boolean {
522        for (let i = fi; i < len; i++) {
523            if (val == this.$_get_unsafe(i)) {
524                return true;
525            }
526        }
527        return false
528    }
529
530    private indexOfUndefined(fi: int, len: int): int {
531        for (let i = fi; i < len; i++) {
532            if (this.$_get_unsafe(i) instanceof undefined) {
533                return i
534            }
535        }
536        return -1
537    }
538
539    private indexOfNull(fi: int, len: int): int {
540        for (let i = fi; i < len; i++) {
541            if (this.$_get_unsafe(i) instanceof null) {
542                return i
543            }
544        }
545        return -1
546    }
547
548    private indexOfString(val: String, fi: int, len: int): int {
549        for (let i = fi; i < len; i++) {
550            const tmp = this.$_get_unsafe(i)
551            if (tmp instanceof String) {
552                if (tmp == val) {
553                    return i
554                }
555            }
556        }
557        return -1
558    }
559
560    private indexOfNumber(val: Number, fi: int, len: int): int {
561        const unboxedVal: number = val.valueOf()
562        if (isNaN(val)) {
563            return -1
564        }
565        for (let i = fi; i < len; i++) {
566            const tmp = this.$_get_unsafe(i)
567            if (tmp instanceof Number) {
568                if (unboxedVal == tmp.unboxed()) {
569                    return i
570                }
571            }
572        }
573        return -1
574    }
575
576    private indexOfFloat(val: Float, fi: int, len: int): int {
577        const unboxedVal: float = val.unboxed()
578        if (isNaN(val)) {
579            return -1
580        }
581        for (let i = fi; i < len; i++) {
582            const tmp = this.$_get_unsafe(i)
583            if (tmp instanceof Float) {
584                if (unboxedVal == tmp.unboxed()) {
585                    return i
586                }
587            }
588        }
589        return -1
590    }
591
592    private indexOfLong(val: Long, fi: int, len: int): int {
593        const unboxedVal: long = val.unboxed()
594        for (let i = fi; i < len; i++) {
595            const tmp = this.$_get_unsafe(i)
596            if (tmp instanceof Long) {
597                if (tmp == unboxedVal) {
598                    return i
599                }
600            }
601        }
602        return -1
603    }
604
605    private indexOfInt(val: Int, fi: int, len: int): int {
606        const unboxedVal: int = val.unboxed()
607        for (let i = fi; i < len; i++) {
608            const tmp = this.$_get_unsafe(i)
609            if (tmp instanceof Int) {
610                if (tmp == unboxedVal) {
611                    return i
612                }
613            }
614        }
615        return -1
616    }
617
618    private indexOfCommon(val: T, fi: int, len: int): int {
619        for (let i = fi; i < len; i++) {
620            if (val == this.$_get_unsafe(i)) {
621                return i
622            }
623        }
624        return -1
625    }
626    /**
627     * Default comparison function for sort algorithm.
628     * Objects are compared as string. Both objects are convereted to string
629     * using `toString()` method and compared using `compareTo() method of `string` class.
630     *
631     * @param a: Object - Object to be compared
632     *
633     * @param b: Object - Object to be compared
634     *
635     * @returns Returns one of values -1, 0, 1 (_less_, _equal_, _greater_ respectively).
636     */
637    private static defaultComparator(a: NullishType, b: NullishType): number {
638        if (a instanceof Number && b instanceof Number) {
639            const x = (a as Number).valueOf()
640            const y = (b as Number).valueOf()
641            if (Number.isInteger(x) && Number.isInteger(y) &&
642                x <= Int.MAX_VALUE / 128 && x >= Int.MIN_VALUE / 128 &&
643                y <= Int.MAX_VALUE / 128 && y >= Int.MIN_VALUE / 128) {
644                let z = x as int
645                let w = y as int
646                return Array.defaultComparatorInts(z, w)
647            }
648        } else if (a instanceof String && b instanceof String) {
649            return a.compareTo(b)
650        }
651        let sa = new String(a)
652        let sb = new String(b)
653        return sa.compareTo(sb)
654    }
655
656    private static defaultComparatorInts(a: int, b: int): number {
657        if (a < 0) {
658            if (b >= 0) {
659                return -1
660            }
661            a *= -1
662            b *= -1
663        } else if (b < 0) {
664            return 1
665        }
666        let aDigs = 1
667        while (10 * aDigs <= a) {
668            aDigs *= 10
669        }
670        let bDigs = 1
671        while (10 * bDigs <= b) {
672            bDigs *= 10
673        }
674
675        while (aDigs > 0 && bDigs > 0) {
676            let r = (a / aDigs) - (b / bDigs)
677            if (r != 0) {
678                return r
679            }
680            aDigs /= 10
681            bDigs /= 10
682        }
683        return (aDigs - bDigs)
684    }
685
686    private static defaultComparatorStr(a: String, b: String) {
687        return a.compareTo(b)
688    }
689
690    /**
691     * Helper function preparing copy of `this` instance of `Array` class' data array.
692     *
693     * @returns Copy of an `Array`'s primitive array data.
694     */
695    private copyArray(): FixedArray<NullishType> {
696        let len: int = this.actualLength
697        let res : FixedArray<NullishType> = new NullishType[len]
698        for (let i = 0; i < len; i++) {
699            res[i] = this.$_get_unsafe(i)
700        }
701        return res
702    }
703
704    private wrap_default_sort(): void {
705        let idxNonUndef = 0
706        type arrType = String | undefined
707        try {
708            let strArr : FixedArray<arrType> = new arrType[this.actualLength]
709            for (let i = 0; i < this.actualLength; i++) {
710                const vl = this.$_get_unsafe(i)
711                if (vl !== undefined) {
712                    if (vl == null) {
713                        this.$_set_unsafe(idxNonUndef, vl as T)
714                        strArr[idxNonUndef] = "null"
715                    } else {
716                        this.$_set_unsafe(idxNonUndef, vl)
717                        strArr[idxNonUndef] = (vl as object).toString() // #26217
718                    }
719                    idxNonUndef++
720                }
721            }
722            let sortTo = idxNonUndef
723            for (let i = idxNonUndef; i < this.actualLength; i++) {
724                this.$_set_unsafe(i, undefined as T)
725            }
726
727            sort_default<NullishType>(this.buffer, strArr, 0, sortTo)
728        }
729        catch (e) {
730            if (e instanceof OutOfMemoryError) {
731                this.slow_default_sort()
732            } else {
733                throw e as Error
734            }
735        }
736    }
737
738    private slow_default_sort(): void {
739        let idxNonUndef = 0
740        const cmp: (l: NullishType, r: NullishType) => number = (l: NullishType, r: NullishType): number => {
741            return Array.defaultComparator(l, r)
742        }
743        for (let i = 0; i < this.actualLength; i++) {
744            const vl = this.$_get_unsafe(i)
745            if (vl !== undefined) {
746                this.$_set_unsafe(idxNonUndef, vl)
747                idxNonUndef++
748            }
749        }
750        let sortTo = idxNonUndef
751        for (let i = idxNonUndef; i < this.actualLength; i++) {
752            this.$_set_unsafe(i, undefined as T)
753        }
754        sort_stable<NullishType>(this.buffer, 0, sortTo, cmp)
755    }
756
757    private move_undefined_end(): int {
758        let writeIndex: int = 0
759        for (let i = 0; i < this.actualLength; i++) {
760            let val = this.$_get_unsafe(i)
761            if (val !== undefined) {
762                if(writeIndex != i) {
763                    this.$_set_unsafe(writeIndex, val)
764                }
765                writeIndex++
766            }
767        }
768        for (let i = writeIndex; i < this.actualLength; i++) {
769            this.$_set_unsafe(i, undefined as T)
770        }
771        return writeIndex
772    }
773
774    /**
775     * Reorders elements of `this` using comparator function.
776     *
777     * @see ECMA-262, 23.1.3.30
778     *
779     * @param comparator function that defines the sort order.
780     *
781     * @returns `this` instance of `Array` class.
782     *
783     * @note Mutating method
784     *
785     * NOTE clarify UTF-16 or UTF-8
786     */
787    public sort(comparator?: (a: T, b: T) => number): this {
788        if (this.actualLength <= 1)
789            return this
790
791        if (comparator == undefined) {
792            this.wrap_default_sort()
793            return this
794        }
795
796        const compareTo = this.move_undefined_end()
797        let cmp: (l: NullishType, r: NullishType) => number = (l: NullishType, r: NullishType): number => {
798            return comparator!(l as T, r as T)
799        }
800        sort_stable<NullishType>(this.buffer, 0, compareTo, cmp)
801        return this
802    }
803
804    /**
805     * Removes the first element from an array and returns that removed element.
806     * This method changes the length of the array.
807     *
808     * @returns shifted element, i.e. that was at index zero
809     */
810    public shift(): T | undefined {
811        if(this.actualLength == 0) {
812            return undefined
813        }
814        let obj = this.$_get_unsafe(0)
815        const other = this.slice(1, this.actualLength)
816        this.buffer = other.buffer
817        this.actualLength = other.actualLength
818        return obj
819    }
820
821    /**
822     * Removes the last element from an array and returns that element.
823     * This method changes the length of the array.
824     *
825     * @returns removed element
826     */
827    public pop(): T | undefined {
828        if(this.actualLength == 0) {
829            return undefined
830        }
831        let obj = this.$_get_unsafe(this.actualLength - 1)
832        this.buffer[this.actualLength - 1] = null
833        this.actualLength--
834        return obj
835    }
836
837    /**
838     * Adds the specified elements to the end of an array and returns the new length of the array.
839     *
840     * @returns new length
841     */
842    public push(...val: T[]): number {
843        this.ensureUnusedCapacity(val.length as int)
844        for (let i = 0; i < val.length; i++) {
845            this.buffer[this.actualLength + i] = val[i]
846        }
847        this.actualLength += val.length
848        return this.actualLength
849    }
850
851    /**
852     * Adds the specified elements to the end of an array and returns the new length of the array.
853     *
854     * @returns new length
855     */
856    public pushECMA(...val: T[]): number {
857        this.ensureUnusedCapacity(val.length as int)
858        for (let i = 0; i < val.length; i++) {
859            this.buffer[this.actualLength + i] = val[i]
860        }
861        this.actualLength += val.length
862        return this.actualLength
863    }
864
865    private ensureUnusedCapacity(cap: int): void {
866        if (this.actualLength + cap > this.buffer.length) {
867            const copy : FixedArray<NullishType> = new NullishType[this.buffer.length * 2 + cap]
868            for (let i = 0; i < this.actualLength; i++) {
869                copy[i] = this.buffer[i]
870            }
871            this.buffer = copy
872        }
873    }
874
875    /**
876     * Changes the contents of an array by removing or replacing existing elements
877     * and/or adding new elements in place.
878     *
879     * @param start index
880     *
881     * @param delete number of items after start index
882     *
883     * @returns an Array with deleted elements
884     */
885    public splice(start: number, delete: Number | undefined, ...items: T[]): Array<T> {
886        return this.splice(start as int, asIntOrDefault(delete, 0), ...items)
887    }
888
889    /**
890     * Changes the contents of an array by removing or replacing existing elements
891     * and/or adding new elements in place.
892     *
893     * @param start index
894     *
895     * @param delete number of items after start index
896     *
897     * @returns an Array with deleted elements
898     */
899    public splice(start: int, delete: int, ...items: T[]): Array<T> {
900        start = normalizeIndex(start, this.actualLength)
901        if (delete < 0) {
902            delete = 0
903        }
904        if (start > this.actualLength - delete) {
905            delete = this.actualLength - start
906        }
907        // this: [left middle right], we must replace middle with `items`
908
909        this.ensureUnusedCapacity(items.length as int - delete)
910        const oldLen = this.actualLength
911        this.actualLength = this.actualLength - delete + items.length as int
912
913        let ret = new Array<T>(delete)
914        let lastSet = start
915        // left part remains unchanged
916        // copy excluded part
917        for (let i = 0; i < delete; i++) {
918            ret.buffer[i] = this.buffer[start + i]
919        }
920        // move right part to the right of the buffer
921        const rightLen = oldLen - start - delete
922        if (items.length > delete) {
923            for (let i = 0; i < rightLen; i++) {
924                this.buffer[this.actualLength - 1 - i] = this.buffer[oldLen - 1 - i]
925            }
926        } else {
927            for (let i = 0; i < rightLen; i++) {
928                this.buffer[start + items.length as int + i] = this.buffer[start + delete + i]
929            }
930        }
931        // insert middle part
932        for (let i = 0; i < items.length; i++) {
933            this.buffer[start + i] = items[i]
934        }
935        return ret
936    }
937
938    /**
939     * Changes the contents of an array by removing or replacing existing elements
940     * and/or adding new elements in place.
941     *
942     * @param start index
943     *
944     * @returns an Array with deleted elements from start to the last element of the current instance
945     */
946    public splice(start: number): Array<T> {
947        return this.splice(start as int)
948    }
949
950    /**
951     * Changes the contents of an array by removing or replacing existing elements
952     * and/or adding new elements in place.
953     *
954     * @param start index
955     *
956     * @returns an Array with deleted elements from start to the last element of the current instance
957     */
958    public splice(start: int): Array<T> {
959        return this.splice(start, this.actualLength)
960    }
961
962    /**
963     * Checks whether the passed value is an Array.
964     *
965     * @param arr
966     *
967     * @returns true is arr is a non-nullish array, false otherwise
968     */
969    public static isArray(o: NullishType): boolean {
970        if (o instanceof Array) {
971            return true
972        }
973        return (Type.of(o) instanceof ArrayType)
974    }
975
976    /**
977     * Creates a new Array instance from a variable number of arguments,
978     * regardless of number or type of the arguments.
979     *
980     * @param values an initilizer
981     *
982     * @returns a newly created Array
983     */
984    public static of<T>(...values: T[]): Array<T> {
985        const ret = new Array<T>()
986        ret.ensureUnusedCapacity(values.length as int)
987        for (let i = 0; i < values.length; i++) {
988            ret.push(values[i])
989        }
990        return ret
991    }
992
993    /**
994     * Adds the specified elements to the beginning of an Array
995     * and returns the new length of the Array.
996     *
997     * @param values data to be added
998     *
999     * @returns new length of the Array
1000     */
1001    public unshift(...values: T[]): number {
1002        let buffer = this.buffer
1003        if (this.buffer.length <= values.length + this.actualLength) {
1004            buffer = new NullishType[this.buffer.length * 2 + values.length]
1005        }
1006        for (let i = 0; i < this.actualLength; i++) {
1007            buffer[this.actualLength + values.length as int - i - 1] = this.buffer[this.actualLength - 1 - i]
1008        }
1009        for (let i = 0; i < values.length; i++) {
1010            buffer[i] = values[i]
1011        }
1012        this.buffer = buffer
1013        this.actualLength += values.length
1014        return this.actualLength
1015    }
1016
1017    /**
1018     * Returns an iterator over all indices
1019     */
1020    public override keys(): IterableIterator<Number> {
1021        return new ArrayKeysIterator<T>(this)
1022    }
1023
1024    /**
1025     * Returns an iterator over all values
1026     */
1027    public override $_iterator(): IterableIterator<T> {
1028        return this.values()
1029    }
1030
1031    // === methods with uncompatible implementation ===
1032    /**
1033     * Returns the elements of an array that meet the condition specified in a callback function.
1034     *
1035     * @param predicate A function that accepts up to three arguments. The filter method calls the predicate function one time for each element in the array.
1036     *
1037     * @returns New `Array` instance constructed from `this` with elements filtered using test function `predicate`.
1038     */
1039    public override filter(predicate: (value: T, index: number, array: Array<T>) => boolean): Array<T> {
1040        const res = new Array<T>()
1041        for (let i: int = 0; i < this.actualLength; i++) {
1042            const val = this.$_get_unsafe(i)
1043            if (predicate(val, i as number, this)) {
1044                res.push(val)
1045            }
1046        }
1047        return res
1048    }
1049
1050    /**
1051     * Creates a new Array with all sub-array elements concatenated
1052     * into it recursively up to the specified depth.
1053     *
1054     * @param depth
1055     *
1056     * @returns a flattened Array with respect to depth
1057     */
1058    public flat<U>(depth: number): Array<U> {
1059        return this.flat<U>(depth as int)
1060    }
1061
1062    /**
1063     * Creates a new Array with all sub-array elements concatenated
1064     * into it recursively up to the specified depth.
1065     *
1066     * @param depth
1067     *
1068     * @returns a flattened Array with respect to depth
1069     */
1070    public flat<U>(depth: int): Array<U> {
1071        let ret = new Array<U>()
1072        this.flatImpl<U>(depth, ret)
1073        return ret
1074    }
1075
1076    private flatImpl<U>(depth: int, to: Array<U>) {
1077        throw new Error("not implemented")
1078    }
1079
1080    /**
1081     * Creates a new Array with all sub-array elements concatenated
1082     *
1083     * @returns a flattened Array
1084     */
1085    public flat<U>(): Array<U> {
1086        return this.flat<U>(1)
1087    }
1088
1089    /**
1090     * Applies flat and than map
1091     *
1092     * fn a function to apply
1093     *
1094     * @return new Array after map and than flat
1095     */
1096    // NOTE(ivan-tyulyandin): TBD, flatMap may be not subset, see ReadonlyArray
1097    public flatMap<U>(fn: (v: T, k: number, arr: Array<T>) => U): Array<U> {
1098        let mapped: Array<U> = this.map<U>(fn)
1099        return mapped.flat<U>()
1100    }
1101
1102    // === methods common among all arrays ===
1103
1104    /**
1105     * Takes an integer value and returns the item at that index,
1106     * allowing for positive and negative integers. Negative integers count back
1107     * from the last item in the array.
1108     *
1109     * @param index Zero-based index of the array element to be returned.
1110     * Negative index counts back from the end of the array — if `index` < 0, index + `array.length()` is accessed.
1111     *
1112     * @returns The element in the array matching the given index.
1113     * Returns undefined if `index` < `-length()` or `index` >= `length()`.
1114     */
1115    public override at(index: number): T | undefined {
1116        return this.at(index as int)
1117    }
1118
1119    /**
1120     * Creates a new `Array` from this `Array` instance and given `Array` instance.
1121     *
1122     * @param other to concatenate into a new array.
1123     *
1124     * @returns New `Array` instance, constructed from `this` and given `other` instances of `Array` class.
1125     */
1126    // public override concat(...items: (T | ConcatArray<T>)[]): Array<T> {
1127    //     throw new Error("not implemented")
1128    // }
1129
1130    public concat(...items: FixedArray<ConcatArray<T>>): Array<T> {
1131        let totalAdd = this.actualLength;
1132        for (let i = 0; i < items.length; i++) {
1133            totalAdd += items[i].length as int
1134        }
1135
1136        const buf : FixedArray<NullishType> = new NullishType[totalAdd];
1137
1138        for (let i = 0; i < this.actualLength; i++) {
1139            buf[i] = this.$_get_unsafe(i);
1140        }
1141
1142        let insertTo = this.actualLength;
1143        for (let i = 0; i < items.length; i++) {
1144            const arr = items[i]
1145            const len = arr.length as int
1146            for (let j = 0; j < len; j++) {
1147                buf[insertTo++] = arr.$_get(j)
1148            }
1149        }
1150
1151        return new Array<T>(FROM_BUFFER, buf);
1152    }
1153
1154    /**
1155     * Takes an integer value and returns the item at that index,
1156     * allowing for positive and negative integers. Negative integers count back
1157     * from the last item in the array.
1158     *
1159     * @param index Zero-based index of the array element to be returned.
1160     * Negative index counts back from the end of the array — if `index` < 0, index + `array.length()` is accessed.
1161     *
1162     * @returns The element in the array matching the given index.
1163     * Returns undefined if `index` < `-length()` or `index` >= `length()`.
1164     */
1165    public at(index: int): T | undefined {
1166        let len = this.actualLength;
1167        let k: int;
1168        if (index >= 0) {
1169            k = index;
1170        } else {
1171            k = len + index;
1172        }
1173
1174        if (k < 0 || k >= len) {
1175            return undefined;
1176        }
1177
1178        return this.$_get_unsafe(k);
1179    }
1180
1181    /**
1182     * Makes a shallow copy of the Array part to another location in the same Array and returns it without modifying its length.
1183     *
1184     * @param target index at which to copy the sequence
1185     *
1186     * @param start index at which to start copying elements from
1187     *
1188     * @param end index at which to end copying elements from
1189     *
1190     * @returns this array after transformation
1191     */
1192    public copyWithin(target: number, start: number, end?: Number): this {
1193        this.copyWithin(target as int, start as int, asIntOrDefault(end, this.actualLength));
1194        return this;
1195    }
1196
1197    /**
1198     * Makes a shallow copy of the Array part to another location in the same Array and returns it without modifying its length.
1199     *
1200     * @param target index at which to copy the sequence
1201     *
1202     * @param start index at which to start copying elements from
1203     *
1204     * @param end index at which to end copying elements from
1205     *
1206     * @returns this array after transformation
1207     */
1208    public copyWithin(target: int, start: int, end: int): this {
1209        target = normalizeIndex(target, this.actualLength)
1210        start = normalizeIndex(start, this.actualLength)
1211        end = normalizeIndex(end, this.actualLength)
1212
1213        if (end <= start) {
1214            return this;
1215        }
1216
1217        if (target <= start) {
1218            while (start < end) {
1219                const read = this.$_get_unsafe(start++);
1220                this.$_set_unsafe(target++, read);
1221            }
1222        } else {
1223            let len = end - start;
1224            if (target + len > this.actualLength) {
1225                len = this.actualLength - target
1226            }
1227            for (let i = 0; i < len; i++) {
1228                const read = this.$_get_unsafe(start + len - 1 - i);
1229                this.$_set_unsafe(target + len - 1 - i, read);
1230            }
1231        }
1232
1233        return this;
1234    }
1235
1236    /**
1237     * Makes a shallow copy of the Array part to another location in the same Array and returns it without modifying its length.
1238     *
1239     * @param target index at which to copy the sequence
1240     *
1241     * @param start index at which to start copying elements from
1242     *
1243     * @returns this array after transformation
1244     */
1245    public copyWithin(target: int, start: int): this {
1246        this.copyWithin(target, start, this.actualLength);
1247        return this;
1248    }
1249
1250    /**
1251     * Makes a shallow copy of the Array part to another location in the same Array and returns it without modifying its length.
1252     *
1253     * @param target index at which to copy the sequence
1254     *
1255     * @returns this array after transformation
1256     */
1257    public copyWithin(target: int): this {
1258        this.copyWithin(target, 0, this.actualLength);
1259        return this;
1260    }
1261
1262    /**
1263     * Changes all elements in the Array to a static value, from a start index to an end index
1264     *
1265     * @param value to fill the array with
1266     *
1267     * @param start index at which to start filling
1268     *
1269     * @param end index at which to end filling, but not including
1270     *
1271     * @returns this array after transformation
1272     */
1273    public fill(value: T, start?: Number, end?: Number): this {
1274        this.fill(value, asIntOrDefault(start, 0), asIntOrDefault(end, this.actualLength));
1275        return this;
1276    }
1277
1278    /**
1279     * Changes all elements in the Array to a static value, from a start index to an end index
1280     *
1281     * @param value to fill the array with
1282     *
1283     * @param start index at which to start filling
1284     *
1285     * @param end index at which to end filling, but not including
1286     *
1287     * @returns this array after transformation
1288     */
1289    public native fill(value: T, start: int, end: int): this;
1290
1291    /**
1292     * Returns the value of the first element in the array where predicate is true, and undefined
1293     * otherwise.
1294     *
1295     * @param predicate find calls predicate once for each element of the array, in ascending
1296     * order, until it finds one where predicate returns true. If such an element is found, find
1297     * immediately returns that element value. Otherwise, find returns undefined.
1298     *
1299     * @returns the value of the first element in the array or undefined
1300     */
1301    public override find(predicate: (value: T, index: number, array: Array<T>) => boolean): T | undefined {
1302        const res = this.findIndex(predicate)
1303        if (res == -1) {
1304            return undefined
1305        }
1306        return this.$_get_unsafe(res as int);
1307    }
1308
1309    /**
1310     * Returns the index of the first element in the array where predicate is true, and -1
1311     * otherwise.
1312     *
1313     * @param predicate find calls predicate once for each element of the array, in ascending
1314     * order, until it finds one where predicate returns true. If such an element is found,
1315     * findIndex immediately returns that element index. Otherwise, findIndex returns -1.
1316     *
1317     * @returns found element index or -1 otherwise
1318     */
1319    public override findIndex(predicate: (value: T, index: number, array: Array<T>) => boolean): number {
1320        for (let i = 0; i < this.actualLength; i++) {
1321            if (predicate(this.$_get_unsafe(i), i as number, this)) {
1322                return i;
1323            }
1324        }
1325        return -1;
1326    }
1327
1328    /**
1329     * Iterates the array in reverse order and returns the value of the first element
1330     * that satisfies the provided testing function
1331     *
1332     * @param predicate testing function
1333     *
1334     * @returns found element or undefined otherwise
1335     */
1336    public override findLast(predicate: (elem: T, index: number, array: Array<T>) => boolean): T | undefined {
1337        for (let i = this.actualLength - 1; i >= 0; i--) {
1338            const val = this.$_get_unsafe(i);
1339            if (predicate(val, i as number, this)) {
1340                return val;
1341            }
1342        }
1343        return undefined;
1344    }
1345
1346    /**
1347     * Determines whether all the members of an array satisfy the specified test.
1348     *
1349     * @param predicate A function that accepts up to three arguments. The every method calls
1350     * the predicate function for each element in the array until the predicate returns a value
1351     * which is coercible to the Boolean value false, or until the end of the array.
1352     *
1353     * @returns `true` if `predicate` returns a `true` value for every array element. Otherwise, `false`.
1354     */
1355    public override every(predicate: (value: T, index: number, array: Array<T>) => boolean): boolean {
1356        let curArrLength = this.actualLength
1357        for (let i = 0; i < curArrLength && i < this.actualLength; i++) {
1358            if (!predicate(this.$_get_unsafe(i), i as number, this)) {
1359                return false
1360            }
1361        }
1362        return true;
1363    }
1364
1365    /**
1366     * Determines whether the specified callback function returns true for any element of an array.
1367     *
1368     * @param predicate A function that accepts up to three arguments. The some method calls
1369     * the predicate function for each element in the array until the predicate returns a value
1370     * which is coercible to the Boolean value true, or until the end of the array.
1371     *
1372     * @returns `true` if `predicate` returns a `true` value for at least one array element. Otherwise, `false`.
1373     */
1374    public override some(predicate: (value: T, index: number, array: Array<T>) => boolean): boolean {
1375        for (let i = 0; i < this.actualLength; i++) {
1376            if (predicate(this.$_get_unsafe(i), i as number, this)) {
1377                return true
1378            }
1379        }
1380        return false
1381    }
1382
1383    /**
1384     * Iterates the array in reverse order and returns the index of
1385     * the first element that satisfies the provided testing function.
1386     * If no elements satisfy the testing function, -1 is returned.
1387     *
1388     * @param predicate testing function
1389     *
1390     * @returns index of first element satisfying to predicate, -1 if no such element
1391     */
1392    public override findLastIndex(predicate: (element: T, index: number, array: Array<T>) => boolean): number {
1393        for (let i = this.actualLength - 1; i >= 0; i--) {
1394            if (predicate(this.$_get_unsafe(i), i as number, this)) {
1395                return i
1396            }
1397        }
1398        return -1
1399    }
1400
1401    /**
1402     * Calls the specified callback function for all the elements in an array. The return value of the callback function is the accumulated result, and is provided as an argument in the next call to the callback function.
1403     *
1404     * @param callbackfn A function that accepts up to four arguments. The reduce method calls the callbackfn function one time for each element in the array.
1405     *
1406     * @returns a result after applying callbackfn over all elements of the Array
1407     */
1408    public override reduce(callbackfn: (previousValue: T, currentValue: T, index: number, array: Array<T>) => T): T {
1409        if (this.actualLength == 0) {
1410            throw new TypeError("Reduce of empty array with no initial value")
1411        }
1412        let acc: T = this.$_get_unsafe(0);
1413        for (let i = 1; i < this.actualLength; i++) {
1414            acc = callbackfn(acc, this.$_get_unsafe(i), i as number, this)
1415        }
1416        return acc
1417    }
1418
1419    /**
1420     * Calls the specified callback function for all the elements in an array. The return value of the callback function is the accumulated result, and is provided as an argument in the next call to the callback function.
1421     *
1422     * @param callbackfn A function that accepts up to four arguments. The reduce method calls the callbackfn function one time for each element in the array.
1423     *
1424     * @param initialValue If initialValue is specified, it is used as the initial value to start the accumulation. The first call to the callbackfn function provides this value as an argument instead of an array value.
1425     *
1426     * @returns a result after applying callbackfn over all elements of the Array
1427     */
1428    public override reduce<U = T>(callbackfn: (previousValue: U, currentValue: T, index: number, array: Array<T>) => U, initialValue: U): U {
1429        let acc = initialValue
1430        for (let i = 0; i < this.actualLength; i++) {
1431            acc = callbackfn(acc, this.$_get_unsafe(i), i as number, this)
1432        }
1433        return acc
1434    }
1435
1436    /**
1437     * Calls the specified callback function for all the elements in an array, in descending order. The return value of the callback function is the accumulated result, and is provided as an argument in the next call to the callback function.
1438     *
1439     * @param callbackfn A function that accepts up to four arguments. The reduceRight method calls the callbackfn function one time for each element in the array.
1440     *
1441     * @param initialValue If initialValue is specified, it is used as the initial value to start the accumulation. The first call to the callbackfn function provides this value as an argument instead of an array value.
1442     *
1443     * @returns a result after applying callbackfn over all elements of the Array
1444     */
1445    public override reduceRight<U>(callbackfn: (previousValue: U, currentValue: T, index: number, array: Array<T>) => U, initialValue: U): U {
1446        let acc = initialValue
1447        for (let i = this.actualLength - 1; i >= 0; i--) {
1448            acc = callbackfn(acc, this.$_get_unsafe(i), i as number, this)
1449        }
1450        return acc
1451    }
1452
1453    /**
1454     * Performs the specified action for each element in an array.
1455     *
1456     * @param callbackfn A function that accepts up to three arguments. forEach calls the callbackfn function one time for each element in the array.
1457     */
1458    public override forEach(callbackfn: (value: T, index: number, array: Array<T>) => void): void {
1459        const len0 = this.actualLength;
1460        for (let i = 0; i < len0; i++) {
1461            callbackfn(this.$_get_unsafe(i), i as number, this)
1462        }
1463    }
1464
1465    /**
1466     * Creates a new `Array` object and populates it with elements of `this` instance of `Array` class
1467     * selected from `start` to `end` (`end` not included) where `start` and `end` represent the index of items in that array.
1468     *
1469     * @param start zero-based index at which to start extraction
1470     *
1471     * @param end zero-based index at which to end extraction. `slice()` extracts up to but not including end.
1472     *
1473     * @returns `Array` instance, constructed from extracted elements of `this` instance.
1474     */
1475    public override slice(start?: Number, end?: Number): Array<T> {
1476        const len: int = this.actualLength;
1477        return this.slice(asIntOrDefault(start, 0), asIntOrDefault(end, len))
1478    }
1479
1480    /**
1481     * Creates a new `Array` object and populates it with elements of `this` instance of `Array` class
1482     * selected from `start` to `end` (`end` not included) where `start` and `end` represent the index of items in that array.
1483     *
1484     * @param start zero-based index at which to start extraction
1485     *
1486     * @param end zero-based index at which to end extraction. `slice()` extracts up to but not including end.
1487     *
1488     * @returns `Array` instance, constructed from extracted elements of `this` instance.
1489     */
1490    public slice(start: int, end: int): Array<T> {
1491        const len: int = this.actualLength;
1492        const relStart = normalizeIndex(start, len)
1493        const relEnd = normalizeIndex(end, len)
1494
1495        let count = relEnd - relStart;
1496        if (count < 0) {
1497            count = 0;
1498        }
1499        let res : FixedArray<NullishType> = new NullishType[count]
1500        for (let i = 0; i < count; i++) {
1501            res[i] = this.$_get_unsafe(relStart + i);
1502        }
1503
1504        return new Array<T>(FROM_BUFFER, res)
1505    }
1506
1507    /**
1508     * Creates a new `Array` object and populates it with elements of `this` instance of `Array` class
1509     * selected from `start` to `Int.MAX_VALUE`, which means 'to the end of an array'.
1510     *
1511     * @param start zero-based index at which to start extraction
1512     *
1513     * @returns `Array` instance, constructed from extracted elements of `this` instance.
1514     */
1515    public slice(start: int): Array<T> {
1516        return this.slice(start, Int.MAX_VALUE as int);
1517    }
1518
1519    /**
1520     * Returns the last index at which a given element can be found in the array,
1521     * or -1 if it is not present. The array is searched backwards, starting at fromIndex.
1522     *
1523     * @param searchElement element to locate in the array.
1524     *
1525     * @param fromIndex zero-based index at which to start searching backwards.
1526     * Negative index counts back from the end of the array — if `fromIndex` < 0, `fromIndex` + `length()` is used.
1527     * If `fromIndex` < `-length()`, the array is not searched and -1 is returned.
1528     * If `fromIndex` >= `length()` then `array.length - 1` is used, causing the entire array to be searched.
1529     * If `fromIndex` is undefined then `fromIndex = 0`.
1530     * If `fromIndex` is ommited then `fromIndex = length()-1`.
1531     *
1532     * @returns The last index of the element in the array; -1 if not found.
1533     */
1534     public native lastIndexOf(searchElement: T, fromIndex: int): int;
1535
1536    /**
1537     * Returns the last index at which a given element can be found in the array,
1538     * or -1 if it is not present. The array is searched backwards, starting at fromIndex.
1539     *
1540     * @param searchElement element to locate in the array.
1541     *
1542     * @param fromIndex zero-based index at which to start searching backwards.
1543     * Negative index counts back from the end of the array — if `fromIndex` < 0, `fromIndex` + `length()` is used.
1544     * If `fromIndex` < `-length()`, the array is not searched and -1 is returned.
1545     * If `fromIndex` >= `length()` then `array.length - 1` is used, causing the entire array to be searched.
1546     * If `fromIndex` is undefined then `fromIndex = 0`.
1547     * If `fromIndex` is ommited then `fromIndex = length()-1`.
1548     *
1549     * @returns The last index of the element in the array; -1 if not found.
1550     */
1551    public lastIndexOf(searchElement: T, fromIndex: number|undefined): number {
1552        return this.lastIndexOf(searchElement, asIntOrDefault(fromIndex, 0));
1553    }
1554
1555    /**
1556     * Returns the last index at which a given element can be found in the array,
1557     * or -1 if it is not present. The array is searched backwards, starting at fromIndex.
1558     *
1559     * @param searchElement element to locate in the array.
1560     *
1561     * @param fromIndex zero-based index at which to start searching backwards.
1562     * Negative index counts back from the end of the array — if `fromIndex` < 0, `fromIndex` + `length()` is used.
1563     * If `fromIndex` < `-length()`, the array is not searched and -1 is returned.
1564     * If `fromIndex` >= `length()` then `array.length - 1` is used, causing the entire array to be searched.
1565     * If `fromIndex` is undefined then `fromIndex = 0`.
1566     * If `fromIndex` is ommited then `fromIndex = length()-1`.
1567     *
1568     * @returns The last index of the element in the array; -1 if not found.
1569     */
1570    public native lastIndexOf(searchElement: T): number;
1571
1572    /**
1573     * Creates and returns a new string by concatenating all of the elements in an `Array`,
1574     * separated by a specified separator string.
1575     * If the array has only one item, then that item will be returned without using the separator.
1576     *
1577     * @param sep specifies a separator
1578     *
1579     * @returns A string with all array elements joined. If `length()` is 0, the empty string is returned.
1580     */
1581    public override join(sep?: String): string {
1582        if (this.actualLength == 0) {
1583            return ""
1584        }
1585        const sepReal = sep === undefined ? "," : sep!
1586        // NOTE(aleksander-sotov) We can't call String constructor here due to internal issue 18583
1587        const first_el = this.$_get_unsafe(0)
1588        let first_str = ""
1589        if (first_el !== undefined && first_el !== null) {
1590            first_str = new String(first_el)
1591        }
1592        let sb = new StringBuilder(first_str)
1593        for (let i: int = 1; i < this.actualLength; i++) {
1594            const tmp = this.$_get_unsafe(i)
1595            sb.append(sepReal);
1596            // NOTE(aleksander-sotov) We can't call String constructor here due to internal issue 18583
1597            if (tmp !== undefined && tmp !== null) {
1598                sb.append(new String(tmp))
1599            } else {
1600                sb.append("")
1601            }
1602        }
1603
1604        return sb.toString();
1605    }
1606
1607    /**
1608     * Returns a string representing the specified array and its elements.
1609     *
1610     * @returns string representation
1611     */
1612    public override toString(): string {
1613        return this.join(",");
1614    }
1615
1616    /**
1617     * Returns a locale string representing the specified array and its elements.
1618     *
1619     * @param locales
1620     *
1621     * @param options
1622     *
1623     * @returns string representation
1624     */
1625    public toLocaleString(locales: Object, options: Object): string {
1626        throw new Error("Array.toLocaleString: not implemented")
1627    }
1628
1629    /**
1630     * Returns a locale string representing the specified array and its elements.
1631     *
1632     * @param options
1633     *
1634     * @returns string representation
1635     */
1636    public toLocaleString(locales: Object): string {
1637        return this.toLocaleString(new Object(), new Object())
1638    }
1639
1640    /**
1641     * Returns a locale string representing the specified array and its elements.
1642     *
1643     * @returns string representation
1644     */
1645    public override toLocaleString(): string {
1646        const sb = new StringBuilder()
1647        const len = this.actualLength;
1648        for (let i = 0; i < len; i++) {
1649            if (i != 0) {
1650                sb.append(",")
1651            }
1652            let x = this.$_get_unsafe(i) as NullishType;
1653            if ((null !== x) && (undefined !== x)) {
1654                sb.append((x! as object).toLocaleString()) // #26217
1655            }
1656        }
1657        return sb.toString()
1658    }
1659
1660    /**
1661     * Copying version of the splice() method.
1662     *
1663     * @param start index
1664     *
1665     * @param delete number of items after start index
1666     *
1667     * @returns a new Array with some elements removed and/or replaced at a given index.
1668     */
1669    public toSpliced(start?: Number, delete?: Number): Array<T> {
1670        const len = this.actualLength;
1671        return this.toSpliced(asIntOrDefault(start, len), asIntOrDefault(delete, len))
1672    }
1673
1674    /**
1675     * Copying version of the splice() method.
1676     *
1677     * @param start index
1678     *
1679     * @param delete number of items after start index
1680     *
1681     * @returns a new Array with some elements removed and/or replaced at a given index.
1682     */
1683    public toSpliced(start: number, delete: number, ...items: FixedArray<T>): Array<T> {
1684        const len = this.actualLength;
1685        return this.toSpliced(start as int, delete as int, ...items)
1686    }
1687
1688    /**
1689     * Copying version of the splice() method.
1690     *
1691     * @param start index
1692     *
1693     * @param delete number of items after start index
1694     *
1695     * @returns a new Array with some elements removed and/or replaced at a given index.
1696     */
1697    public toSpliced(start: int, delete: int, ...items: FixedArray<T>): Array<T> {
1698        const len = this.actualLength;
1699        start = normalizeIndex(start, len);
1700        if (delete < 0) {
1701            delete = 0;
1702        } else if (delete > len) {
1703            delete = len;
1704        }
1705        if (start > len - delete) {
1706            delete = len - start
1707        }
1708        const res : FixedArray<NullishType> = new NullishType[len - delete + items.length];
1709        for (let i = 0; i < start; i++) {
1710            res[i] = this.$_get_unsafe(i)
1711        }
1712        for (let i = 0; i < items.length; i++) {
1713            res[start + i] = items[i]
1714        }
1715        for (let i = start + delete; i < len; i++) {
1716            res[i - delete + items.length] = this.$_get_unsafe(i)
1717        }
1718        return new Array<T>(FROM_BUFFER, res);
1719    }
1720
1721    /**
1722     * Copying version of the splice() method.
1723     *
1724     * @param start index
1725     *
1726     * @returns a new Array with some elements removed and/or replaced at a given index.
1727     */
1728    public toSpliced(start: int): Array<T> {
1729        return this.toSpliced(start, this.actualLength)
1730    }
1731
1732    /**
1733     * Checks whether an Array includes a certain value among its entries,
1734     * returning true or false as appropriate.
1735     *
1736     * @param val value to search
1737     *
1738     * @param fromIndex start index
1739     *
1740     * @returns true if val is in Array
1741     */
1742    public override includes(val: T, fromIndex?: Number): boolean {
1743        const len = this.actualLength;
1744        const fi = normalizeIndex(asIntOrDefault(fromIndex, 0), len);
1745        if (val instanceof String) {
1746            return this.searchString(val, fi, len)
1747        } else if (val instanceof Number) {
1748            return this.searchNumber(val, fi, len)
1749        } else if (val instanceof Float) {
1750            return this.searchFloat(val, fi, len)
1751        } else if (val instanceof Long) {
1752            return this.searchLong(val, fi, len)
1753        } else if (val instanceof Int) {
1754            return this.searchInt(val, fi, len)
1755        } else if (val === undefined) {
1756            return this.searchUndefined(fi, len)
1757        } else if (val == null) {
1758            return this.searchNull(fi, len)
1759        } else {
1760            return this.searchCommon(val, fi, len)
1761        }
1762    }
1763
1764    /**
1765     * Returns the first index at which a given element
1766     * can be found in the array, or -1 if it is not present.
1767     *
1768     * @param val value to search
1769     *
1770     * @param fromIndex index to search from
1771     *
1772     * @returns index of val, -1 otherwise
1773     */
1774    public native indexOf(val: T, fromIndex: int): int;
1775
1776    /**
1777     * Returns the first index at which a given element
1778     * can be found in the array, or -1 if it is not present.
1779     *
1780     * @param val value to search
1781     *
1782     * @param fromIndex index to search from
1783     *
1784     * @returns index of val, -1 otherwise
1785     */
1786    public override indexOf(val: T, fromIndex?: Number): number {
1787        return this.indexOf(val, asIntOrDefault(fromIndex, 0))
1788    }
1789
1790    public native indexOf(val: T): number;
1791
1792    /**
1793     * Copying version of the sort() method.
1794     * It returns a new array with the elements sorted in ascending order.
1795     *
1796     * @returns sorted copy of hte current instance using default comparator
1797     */
1798    public toSorted(): Array<T> {
1799        let arr = new Array<T>(FROM_BUFFER, this.copyArray());
1800        arr.sort()
1801        return arr
1802    }
1803
1804    /**
1805     * Copying version of the sort() method.
1806     * It returns a new array with the elements sorted in ascending order.
1807     *
1808     * @param comparator function to compare to elements of the Array
1809     *
1810     * @returns sorted copy of the current instance comparator
1811     */
1812    public toSorted(comparator: (a: T, b: T) => number): Array<T> {
1813        let arr = new Array<T>(FROM_BUFFER, this.copyArray());
1814        arr.sort(comparator)
1815        return arr
1816    }
1817
1818    /**
1819     * Modifies `this` instance of `Array` class and populates
1820     * it with same elements ordered towards the direction opposite to that previously stated.
1821     *
1822     * @note Mutating method
1823     */
1824    public reverse(): this {
1825        for (let i = 0; i < this.actualLength / 2; i++) {
1826            const tmp = this.$_get_unsafe(i);
1827            const idx_r = this.actualLength - 1 - i;
1828            const val_r = this.$_get_unsafe(idx_r);
1829            this.$_set_unsafe(i, val_r);
1830            this.$_set_unsafe(idx_r, tmp);
1831        }
1832        return this;
1833    }
1834
1835    /**
1836     * Copying version of the reverse() method.
1837     * It returns a new array with the elements in reversed order.
1838     *
1839     * @returns reversed copy of the current Array
1840     */
1841    public toReversed(): Array<T> {
1842        let arr : FixedArray<NullishType> = new NullishType[this.actualLength]
1843        for (let i = 0; i < this.actualLength; i++) {
1844            arr[this.actualLength - 1 - i] = this.$_get_unsafe(i)
1845        }
1846        return new Array<T>(FROM_BUFFER, arr)
1847    }
1848
1849    /**
1850     * Copying version of using the bracket notation to change the value of a given index.
1851     * It returns a new Array with the element at the given index replaced with the given value.
1852     *
1853     * @param index to replace
1854     *
1855     * @param value new value
1856     *
1857     * @returns a new Array with the element at the given index replaced with the given value
1858     */
1859    public with(index: number, value: T): Array<T> {
1860        return this.with(index as int, value)
1861    }
1862
1863    /**
1864     * Copying version of using the bracket notation to change the value of a given index.
1865     * It returns a new Array with the element at the given index replaced with the given value.
1866     *
1867     * @param index to replace
1868     *
1869     * @param value new value
1870     *
1871     * @returns a new Array with the element at the given index replaced with the given value
1872     */
1873    public with(index: int, value: T): Array<T> {
1874        if (index < 0) {
1875            index += this.actualLength;
1876        }
1877        if (index >= this.actualLength) {
1878            throw new RangeError("Invalid index")
1879        }
1880        let arr = new Array<T>(FROM_BUFFER, this.copyArray());
1881        arr.$_set_unsafe(index, value);
1882        return arr
1883    }
1884
1885    /**
1886     * Returns an iterator over all values
1887     */
1888    public override values(): IterableIterator<T> {
1889        return new ArrayValuesIterator_T<T>(this);
1890    }
1891
1892    /**
1893     * Returns an iterable of key, value pairs for every entry in the array
1894     */
1895    public override entries(): IterableIterator<[number, T]> {
1896        return new ArrayEntriesIterator_T<T>(this);
1897    }
1898
1899    /**
1900     * Calls a defined callback function on each element of an array, and returns an array that contains the results.
1901     *
1902     * @param callbackfn A function that accepts up to three arguments. The map method calls the callbackfn function one time for each element in the array.
1903     *
1904     * @returns `Array` instance, constructed from `this` and given function.
1905     */
1906    public override map<U>(callbackfn: (value: T, index: number, array: Array<T>) => U): Array<U> {
1907        const len = this.actualLength;
1908        let res : FixedArray<NullishType> = new NullishType[len];
1909        for (let i = 0; i < this.actualLength; i++) {
1910            res[i] = callbackfn(this.$_get_unsafe(i), i as number, this);
1911        }
1912        return new Array<U>(FROM_BUFFER, res);
1913    }
1914
1915    /**
1916     * Calls the specified callback function for all the elements in an array, in descending order. The return value of the callback function is the accumulated result, and is provided as an argument in the next call to the callback function.
1917     *
1918     * @param callbackfn A function that accepts up to four arguments. The reduceRight method calls the callbackfn function one time for each element in the array.
1919     *
1920     * @returns a result after applying callbackfn over all elements of the Array
1921     */
1922    public override reduceRight(callbackfn: (previousValue: T, currentValue: T, index: number, array: Array<T>) => T): T {
1923        if (this.actualLength == 0) {
1924            throw new TypeError("Reduce of empty array with no initial value")
1925        }
1926        let acc: T = this.$_get_unsafe(this.actualLength - 1);
1927        for (let i = this.actualLength - 2; i >= 0; i--) {
1928            if (this.actualLength > i) {
1929                acc = callbackfn(acc, this.$_get_unsafe(i), i as number, this)
1930            }
1931        }
1932        return acc
1933    }
1934
1935}
1936
1937class ArrayValuesIterator_T<T> implements IterableIterator<T> {
1938    private parent: Array<T>
1939    private idx: int = 0
1940    private isDone: boolean = false
1941
1942    constructor(parent: Array<T>) {
1943        this.parent = parent
1944    }
1945
1946    override next(): IteratorResult<T> {
1947        if (this.isDone || this.idx >= this.parent.actualLength) {
1948            this.isDone = true
1949            return new IteratorResult<T>()
1950        }
1951        return new IteratorResult<T>(this.parent.$_get_unsafe(this.idx++))
1952    }
1953
1954    override $_iterator(): IterableIterator<T> {
1955        return this;
1956    }
1957
1958    public __Iterator_getLength(): int {
1959        return this.parent.length as int
1960    }
1961}
1962
1963class ArrayEntriesIterator_T<T> implements IterableIterator<[number, T]> {
1964    private parent: Array<T>
1965    private idx: int = 0
1966    private isDone: boolean = false
1967
1968    constructor(parent: Array<T>) {
1969        this.parent = parent
1970    }
1971
1972    override next(): IteratorResult<[number, T]> {
1973        if (this.isDone || this.idx >= this.parent.actualLength) {
1974            this.isDone = true
1975            return new IteratorResult<[number, T]>()
1976        }
1977        const i = this.idx++;
1978        const vl: [number, T] = [i as number, this.parent.$_get_unsafe(i)]
1979        return new IteratorResult<[number, T]>(vl);
1980    }
1981
1982    override $_iterator(): IterableIterator<[number, T]> {
1983        return this;
1984    }
1985
1986    public __Iterator_getLength(): int {
1987        return this.parent.length as int
1988    }
1989}
1990