Skip to content

Commit

Permalink
🐛 TRY: positive index only
Browse files Browse the repository at this point in the history
  • Loading branch information
wolfram77 committed May 13, 2023
1 parent d8f8d11 commit 5b85860
Show file tree
Hide file tree
Showing 4 changed files with 45 additions and 53 deletions.
5 changes: 3 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,9 @@ from the [jsDelivr CDN].
> provided as a convenience for access elements from the end of the array.
> However, negative indices can be thought of as referring to a virtual mirrored
> version of the original array, which can be counter-intuitive and make it
> harder to reason about the behavior of functions that use them. We are working
> on a solution to this problem. Any suggestions are welcome.
> harder to reason about the behavior of functions that use them. Prevents the
> direct use of `+` operator. We are working on a solution to this problem. Any
> suggestions are welcome.
[array]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array
[jsDelivr CDN]: https://cdn.jsdelivr.net/npm/extra-array.web/index.js
Expand Down
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
{
"name": "extra-array",
"version": "4.1.13",
"version": "4.1.14",
"description": "An array is a collection of values, stored contiguously.",
"main": "index.js",
"module": "index.mjs",
"sideEffects": false,
"private": true,
"exports": {
"require": "./index.js",
"import": "./index.mjs"
Expand Down
86 changes: 38 additions & 48 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -309,23 +309,23 @@ export function ientries<T>(x: T[]): IEntries<T> {
// -------------

/**
* Get zero-based index for an element in array.
* Get unmirrored index for an element in array.
* @param x an array
* @param i ±index
* @returns i' | x[i'] = x[i]; i' ∈ [0, |x|]
* @param i mirrored ±index
* @returns i' | x.at(i') = x.at(i); i' ∈ [0, |x|]
*/
export function index<T>(x: T[], i: number): number {
var X = x.length;
return i>=0? Math.min(i, X) : Math.max(X+i, 0);
return i>=0? Math.min(i, X) : (i>=-X? X+i : X);
}


/**
* Get zero-based index range for part of array.
* Get unmirrored index range for part of array.
* @param x an array
* @param i begin ±index [0]
* @param I end ±index (exclusive) [|x|]
* @returns [i', I'] | i' ≤ I'; i', I' ∈ [0, |x|]
* @param i begin mirrored ±index [0]
* @param I end mirrored ±index (exclusive) [|x|]
* @returns [i', I'] | x.slice(i', I') = x.slice(i, I); i' ≤ I'; i', I' ∈ [0, |x|]
*/
export function indexRange<T>(x: T[], i: number=0, I: number=x.length): [number, number] {
var X = x.length;
Expand Down Expand Up @@ -354,13 +354,10 @@ export function isEmpty<T>(x: T[]): boolean {
/**
* Find the length of an array.
* @param x an array
* @param i begin ±index [0]
* @param I end ±index (exclusive) [X]
* @returns |x[i..I]|
* @returns |x|
*/
export function length<T>(x: T[], i: number=0, I: number=x.length): number {
var [i, I] = indexRange(x, i, I);
return I-i;
export function length<T>(x: T[]): number {
return x.length;
}
export {length as size};

Expand All @@ -370,10 +367,11 @@ export {length as size};
* @param x an array
* @param n new length
* @param vd default value
* @returns resized x
* @returns x | x[|x|..n] = vd; |x| = n
*/
export function resize$<T>(x: T[], n: number, vd: T) {
var X = x.length; x.length = n;
var X = x.length;
x.length = n;
if (n>X) x.fill(vd, X);
return x;
}
Expand Down Expand Up @@ -403,7 +401,7 @@ export function clear$<T>(x: T[]) {
* @returns x[i]
*/
export function get<T>(x: T[], i: number): T {
return x[index(x, i)];
return x[i];
}
export {get as at};

Expand All @@ -419,7 +417,10 @@ export {get as at};
* @returns [x[i₀], x[i₁], ...] | [i₀, i₁, ...] = is
*/
export function getAll<T>(x: T[], is: number[]): T[] {
return is.map(i => get(x, i));
var a = [];
for (var i of is)
a.push(x[i]);
return a;
}


Expand All @@ -431,7 +432,7 @@ export function getAll<T>(x: T[], is: number[]): T[] {
*/
export function getPath(x: any[], p: number[]): any {
for (var i of p)
x = is(x)? get(x, i) : undefined;
x = Array.isArray(x)? x[i] : undefined;
return x;
}

Expand All @@ -444,8 +445,8 @@ export function getPath(x: any[], p: number[]): any {
*/
export function hasPath(x: any[], p: number[]): boolean {
for (var i of p) {
if (!is(x)) return false;
x = get(x, i);
if (!Array.isArray(x)) return false;
x = x[i];
}
return true;
}
Expand All @@ -472,7 +473,7 @@ export {set as with};
* @returns x | x[i] = v
*/
export function set$<T>(x: T[], i: number, v: T): T[] {
x[index(x, i)] = v;
x[i] = v;
return x;
}

Expand All @@ -489,8 +490,9 @@ export function set$<T>(x: T[], i: number, v: T): T[] {
* @returns x | x[i₀][i₁][...] = v; [i₀, i₁, ...] = p
*/
export function setPath$(x: any[], p: number[], v: any): any[] {
var y = getPath(x, p.slice(0, -1));
if (is(y)) set$(y, last(p), v);
var P = p.length;
var y = getPath(x, p.slice(0, P-1));
if (Array.isArray(y)) y[p[P-1]] = v;
return x;
}

Expand All @@ -515,26 +517,11 @@ export function swap<T>(x: T[], i: number, j: number): T[] {
* @returns x | x[i] ⇔ x[j]
*/
export function swap$<T>(x: T[], i: number, j: number): T[] {
var i = index(x, i), j = index(x, j);
var t = x[i]; x[i] = x[j]; x[j] = t;
return x;
}


/**
* Exchange two values!
* @param x an array (updated!)
* @param i an +ve index
* @param j another +ve index
* @returns x | x[i] ⇔ x[j]
*/
function swapRaw$<T>(x: T[], i: number, j: number): T[] {
var t = x[i]; x[i] = x[j]; x[j] = t;
return x;
}
// NOTE: May also be called swapUnchecked$().


/**
* Exchange two ranges of values.
* @param x an array
Expand All @@ -545,8 +532,6 @@ function swapRaw$<T>(x: T[], i: number, j: number): T[] {
* @returns x' | x' = x; x'[i..I] = x[j..J]; x'[j..J] = x[i..I]
*/
export function swapRanges<T>(x: T[], i: number, I: number, j: number, J: number): T[] {
var [i, I] = indexRange(x, i, I);
var [j, J] = indexRange(x, j, J);
if (j<i) [i, I, j, J] = [j, J, i, I];
if (j<I) return x.slice(); // Skip if ranges overlap!
return x.slice(0, i).concat(x.slice(j, J), x.slice(i, j), x.slice(I));
Expand Down Expand Up @@ -691,7 +676,7 @@ export function sort$<T, U=T>(x: T[], fc: CompareFunction<T|U> | null=null, fm:
if (!fm && !fs) return x.sort(fc);
var X = x.length;
var fm = fm || IDENTITY;
var fs = fs || swapRaw$;
var fs = fs || swap$;
return rangedPartialIntroSort$(x, 0, X, X, fc, fm, fs);
}

Expand Down Expand Up @@ -724,7 +709,7 @@ export function rangedSort<T, U=T>(x: T[], i: number, I: number, fc: CompareFunc
export function rangedSort$<T, U=T>(x: T[], i: number, I: number, fc: CompareFunction<T|U> | null=null, fm: MapFunction<T, T|U> | null=null, fs: SwapFunction<T> | null=null): T[] {
var fc = fc || COMPARE;
var fm = fm || IDENTITY;
var fs = fs || swapRaw$;
var fs = fs || swap$;
var [i, I] = indexRange(x, i, I);
return rangedPartialIntroSort$(x, i, I, I-i, fc, fm, fs);
}
Expand Down Expand Up @@ -788,7 +773,7 @@ export function rangedPartialSort<T, U=T>(x: T[], i: number, I: number, n: numbe
export function rangedPartialSort$<T, U=T>(x: T[], i: number, I: number, n: number, fc: CompareFunction<T|U> | null=null, fm: MapFunction<T, T|U> | null=null, fs: SwapFunction<T> | null=null): T[] {
var fc = fc || COMPARE;
var fm = fm || IDENTITY;
var fs = fs || swapRaw$;
var fs = fs || swap$;
var [i, I] = indexRange(x, i, I);
return rangedPartialIntroSort$(x, i, I, n, fc, fm, fs);
}
Expand Down Expand Up @@ -956,7 +941,7 @@ function rangedMaxHeapify$<T, U=T>(x: T[], i: number, I: number, r: number, fc:
*/
function rangedPartialInsertionSort$<T, U=T>(x: T[], i: number, I: number, n: number, fc: CompareFunction<T|U>, fm: MapFunction<T, T|U>, fs: SwapFunction<T>): T[] {
// NOTE: Insertion sort does not support partial sorting, so we ignore n.
if (fs===swapRaw$) return rangedPartialInsertionSortSwapless$(x, i, I, n, fc, fm, fs);
if (fs===swap$) return rangedPartialInsertionSortSwapless$(x, i, I, n, fc, fm, fs);
else return rangedPartialInsertionSortSwap$ (x, i, I, n, fc, fm, fs);
}

Expand Down Expand Up @@ -1223,14 +1208,14 @@ export function searchMinimumValues<T, U=T>(x: T[], n: number, fc: CompareFuncti
// Create a max heap of indices.
var IH = Math.min(n, X);
var ih = fromRange(0, IH);
rangedBuildMaxHeap$(ih, 0, IH, fc, i => fm(x[i], i, x), swapRaw$);
rangedBuildMaxHeap$(ih, 0, IH, fc, i => fm(x[i], i, x), swap$);
var wr = fm(x[ih[0]], ih[0], x);
// Search for minimum values, and update heap.
for (var i=n; i<X; ++i) {
var w = fm(x[i], i, x);
if (fc(w, wr) >= 0) continue;
ih[0] = i;
rangedMaxHeapify$(ih, 0, IH, 0, fc, i => fm(x[i], i, x), swapRaw$);
rangedMaxHeapify$(ih, 0, IH, 0, fc, i => fm(x[i], i, x), swap$);
var wr = fm(x[ih[0]], ih[0], x);
}
// Sort max heap in ascending order.
Expand Down Expand Up @@ -1370,6 +1355,8 @@ export function middle<T>(x: T[], i: number, n: number=1): T[] {
* @returns x[i..I]
*/
export function slice<T>(x: T[], i: number=0, I: number=x.length): T[] {
var i = Math.max(i, 0);
var I = Math.max(I, 0);
return x.slice(i, I);
}

Expand All @@ -1382,8 +1369,11 @@ export function slice<T>(x: T[], i: number=0, I: number=x.length): T[] {
* @returns x = x[i..I]
*/
export function slice$<T>(x: T[], i: number=0, I: number=x.length): T[] {
var X = x.length;
var i = Math.min(Math.max(i, 0), X);
var I = Math.min(Math.max(I, 0), X);
x.copyWithin(0, i, I);
x.length = length(x, i, I);
x.length = I-i;
return x;
}
// #endregion
Expand Down

0 comments on commit 5b85860

Please sign in to comment.