-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathselect.js
More file actions
235 lines (215 loc) · 6.22 KB
/
select.js
File metadata and controls
235 lines (215 loc) · 6.22 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
"use strict"
module.exports = ndSelect
module.exports.compile = lookupCache
//Macros
var ARRAY = "a"
var RANK = "K"
var CMP = "C"
var DATA = "d"
var OFFSET = "o"
var RND = "R"
var TMP = "T"
var LO = "L"
var HI = "H"
var PIVOT = "X"
function SHAPE(i) {
return "s" + i
}
function STRIDE(i) {
return "t" + i
}
function STEP(i) {
return "u" + i
}
function STEP_CMP(i) {
return "v" + i
}
function INDEX(i) {
return "i" + i
}
function PICK(i) {
return "p" + i
}
function PTR(i) {
return "x" + i
}
//Create new order where index 0 is slowest index
function permuteOrder(order) {
var norder = order.slice()
norder.splice(order.indexOf(0), 1)
norder.unshift(0)
return norder
}
//Generate quick select procedure
function compileQuickSelect(order, useCompare, dtype) {
order = permuteOrder(order)
var dimension = order.length
var useGetter = (dtype === "generic")
var funcName = "ndSelect" + dtype + order.join("_") + "_" + (useCompare ? "cmp" : "lex")
var code = []
//Get arguments for code
var args = [ARRAY, RANK]
if(useCompare) {
args.push(CMP)
}
//Unpack ndarray variables
var vars = [
DATA + "=" + ARRAY + ".data",
OFFSET + "=" + ARRAY + ".offset|0",
RND + "=Math.random",
TMP]
for(var i=0; i<2; ++i) {
vars.push(PTR(i) + "=0")
}
for(var i=0; i<dimension; ++i) {
vars.push(
SHAPE(i) + "=" + ARRAY + ".shape[" + i + "]|0",
STRIDE(i) + "=" + ARRAY + ".stride[" + i + "]|0",
INDEX(i) + "=0")
}
for(var i=1; i<dimension; ++i) {
if(i > 1) {
vars.push(STEP_CMP(i) + "=(" + STRIDE(i) + "-" + SHAPE(i-1) + "*" + STRIDE(i-1) + ")|0",
STEP(order[i]) + "=(" + STRIDE(order[i]) + "-" + SHAPE(order[i-1]) + "*" + STRIDE(order[i-1]) + ")|0")
} else {
vars.push(STEP_CMP(i) + "=" + STRIDE(i),
STEP(order[i]) + "=" + STRIDE(order[i]))
}
}
if(useCompare) {
for(var i=0; i<2; ++i) {
vars.push(PICK(i) + "=" + ARRAY + ".pick(0)")
}
}
vars.push(
PIVOT + "=0",
LO + "=0",
HI + "=" + SHAPE(order[0]) + "-1")
function compare(out, i0, i1) {
if(useCompare) {
code.push(
PICK(0), ".offset=", OFFSET, "+", STRIDE(order[0]), "*(", i0, ");",
PICK(1), ".offset=", OFFSET, "+", STRIDE(order[0]), "*(", i1, ");",
out, "=", CMP, "(", PICK(0), ",", PICK(1), ");")
} else {
code.push(
PTR(0), "=", OFFSET, "+", STRIDE(0), "*(", i0, ");",
PTR(1), "=", OFFSET, "+", STRIDE(0), "*(", i1, ");")
if(dimension > 1) {
code.push("_cmp:")
}
for(var i=dimension-1; i>0; --i) {
code.push("for(", INDEX(i), "=0;",
INDEX(i), "<", SHAPE(i), ";",
INDEX(i), "++){")
}
if(useGetter) {
code.push(out, "=", DATA, ".get(", PTR(0), ")-",
DATA, ".get(", PTR(1), ");")
} else {
code.push(out, "=", DATA, "[", PTR(0), "]-",
DATA, "[", PTR(1), "];")
}
if(dimension > 1) {
code.push("if(", out, ")break _cmp;")
}
for(var i=1; i<dimension; ++i) {
code.push(
PTR(0), "+=", STEP_CMP(i), ";",
PTR(1), "+=", STEP_CMP(i),
"}")
}
}
}
function swap(i0, i1) {
code.push(
PTR(0), "=", OFFSET, "+", STRIDE(order[0]), "*(", i0, ");",
PTR(1), "=", OFFSET, "+", STRIDE(order[0]), "*(", i1, ");")
for(var i=dimension-1; i>0; --i) {
code.push("for(", INDEX(order[i]), "=0;",
INDEX(order[i]), "<", SHAPE(order[i]), ";",
INDEX(order[i]), "++){")
}
if(useGetter) {
code.push(TMP, "=", DATA, ".get(", PTR(0), ");",
DATA, ".set(", PTR(0), ",", DATA, ".get(", PTR(1), "));",
DATA, ".set(", PTR(1), ",", TMP, ");")
} else {
code.push(TMP, "=", DATA, "[", PTR(0), "];",
DATA, "[", PTR(0), "]=", DATA, "[", PTR(1), "];",
DATA, "[", PTR(1), "]=", TMP, ";")
}
for(var i=1; i<dimension; ++i) {
code.push(
PTR(0), "+=", STEP(order[i]), ";",
PTR(1), "+=", STEP(order[i]),
"}")
}
}
code.push(
"while(", LO, "<", HI, "){",
PIVOT, "=(", RND, "()*(", HI, "-", LO, "+1)+", LO, ")|0;")
//Partition array by pivot
swap(PIVOT, HI) // Store pivot temporarily at the end of the array
code.push(
PIVOT, "=", LO, ";", // PIVOT will now be used to keep track of the end of the interval of elements less than the pivot
"for(", INDEX(0), "=", LO, ";",
INDEX(0), "<", HI, ";",
INDEX(0), "++){") // Loop over other elements (unequal to the pivot), note that HI now points to the pivot
compare(TMP, INDEX(0), HI) // Lexicographical compare of element with pivot
code.push("if(", TMP, "<0){")
swap(PIVOT, INDEX(0)) // Swap current element with element at index PIVOT if it is less than the pivot
code.push(PIVOT, "++;")
code.push("}}")
swap(PIVOT, HI) // Store pivot right after all elements that are less than the pivot (implying that all elements >= the pivot are behind the pivot)
//Check pivot bounds
code.push(
"if(", PIVOT, "===", RANK, "){",
LO, "=", PIVOT, ";",
"break;",
"}else if(", RANK, "<", PIVOT, "){",
HI, "=", PIVOT, "-1;",
"}else{",
LO, "=", PIVOT, "+1;",
"}",
"}")
if(useCompare) {
code.push(PICK(0), ".offset=", OFFSET, "+", LO, "*", STRIDE(0), ";",
"return ", PICK(0), ";")
} else {
code.push("return ", ARRAY, ".pick(", LO, ");")
}
//Compile and link js together
var procCode = [
"'use strict';function ", funcName, "(", args, "){",
"var ", vars.join(), ";",
code.join(""),
"};return ", funcName
].join("")
var proc = new Function(procCode)
return proc()
}
var CACHE = {}
function lookupCache(order, useCompare, dtype) {
var typesig = order.join() + useCompare + dtype
var proc = CACHE[typesig]
if(proc) {
return proc
}
return CACHE[typesig] = compileQuickSelect(order, useCompare, dtype)
}
function ndSelect(array, k, compare) {
k |= 0
if((array.dimension === 0) ||
(array.shape[0] <= k) ||
(k < 0)) {
return null
}
var useCompare = !!compare
var proc = lookupCache(array.order, useCompare, array.dtype)
if(useCompare) {
return proc(array, k, compare)
} else {
return proc(array, k)
}
}