ndarray.js 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349
  1. var iota = require("iota-array")
  2. var isBuffer = require("is-buffer")
  3. var hasTypedArrays = ((typeof Float64Array) !== "undefined")
  4. function compare1st(a, b) {
  5. return a[0] - b[0]
  6. }
  7. function order() {
  8. var stride = this.stride
  9. var terms = new Array(stride.length)
  10. var i
  11. for(i=0; i<terms.length; ++i) {
  12. terms[i] = [Math.abs(stride[i]), i]
  13. }
  14. terms.sort(compare1st)
  15. var result = new Array(terms.length)
  16. for(i=0; i<result.length; ++i) {
  17. result[i] = terms[i][1]
  18. }
  19. return result
  20. }
  21. function compileConstructor(dtype, dimension) {
  22. var className = ["View", dimension, "d", dtype].join("")
  23. if(dimension < 0) {
  24. className = "View_Nil" + dtype
  25. }
  26. var useGetters = (dtype === "generic")
  27. if(dimension === -1) {
  28. //Special case for trivial arrays
  29. var code =
  30. "function "+className+"(a){this.data=a;};\
  31. var proto="+className+".prototype;\
  32. proto.dtype='"+dtype+"';\
  33. proto.index=function(){return -1};\
  34. proto.size=0;\
  35. proto.dimension=-1;\
  36. proto.shape=proto.stride=proto.order=[];\
  37. proto.lo=proto.hi=proto.transpose=proto.step=\
  38. function(){return new "+className+"(this.data);};\
  39. proto.get=proto.set=function(){};\
  40. proto.pick=function(){return null};\
  41. return function construct_"+className+"(a){return new "+className+"(a);}"
  42. var procedure = new Function(code)
  43. return procedure()
  44. } else if(dimension === 0) {
  45. //Special case for 0d arrays
  46. var code =
  47. "function "+className+"(a,d) {\
  48. this.data = a;\
  49. this.offset = d\
  50. };\
  51. var proto="+className+".prototype;\
  52. proto.dtype='"+dtype+"';\
  53. proto.index=function(){return this.offset};\
  54. proto.dimension=0;\
  55. proto.size=1;\
  56. proto.shape=\
  57. proto.stride=\
  58. proto.order=[];\
  59. proto.lo=\
  60. proto.hi=\
  61. proto.transpose=\
  62. proto.step=function "+className+"_copy() {\
  63. return new "+className+"(this.data,this.offset)\
  64. };\
  65. proto.pick=function "+className+"_pick(){\
  66. return TrivialArray(this.data);\
  67. };\
  68. proto.valueOf=proto.get=function "+className+"_get(){\
  69. return "+(useGetters ? "this.data.get(this.offset)" : "this.data[this.offset]")+
  70. "};\
  71. proto.set=function "+className+"_set(v){\
  72. return "+(useGetters ? "this.data.set(this.offset,v)" : "this.data[this.offset]=v")+"\
  73. };\
  74. return function construct_"+className+"(a,b,c,d){return new "+className+"(a,d)}"
  75. var procedure = new Function("TrivialArray", code)
  76. return procedure(CACHED_CONSTRUCTORS[dtype][0])
  77. }
  78. var code = ["'use strict'"]
  79. //Create constructor for view
  80. var indices = iota(dimension)
  81. var args = indices.map(function(i) { return "i"+i })
  82. var index_str = "this.offset+" + indices.map(function(i) {
  83. return "this.stride[" + i + "]*i" + i
  84. }).join("+")
  85. var shapeArg = indices.map(function(i) {
  86. return "b"+i
  87. }).join(",")
  88. var strideArg = indices.map(function(i) {
  89. return "c"+i
  90. }).join(",")
  91. code.push(
  92. "function "+className+"(a," + shapeArg + "," + strideArg + ",d){this.data=a",
  93. "this.shape=[" + shapeArg + "]",
  94. "this.stride=[" + strideArg + "]",
  95. "this.offset=d|0}",
  96. "var proto="+className+".prototype",
  97. "proto.dtype='"+dtype+"'",
  98. "proto.dimension="+dimension)
  99. //view.size:
  100. code.push("Object.defineProperty(proto,'size',{get:function "+className+"_size(){\
  101. return "+indices.map(function(i) { return "this.shape["+i+"]" }).join("*"),
  102. "}})")
  103. //view.order:
  104. if(dimension === 1) {
  105. code.push("proto.order=[0]")
  106. } else {
  107. code.push("Object.defineProperty(proto,'order',{get:")
  108. if(dimension < 4) {
  109. code.push("function "+className+"_order(){")
  110. if(dimension === 2) {
  111. code.push("return (Math.abs(this.stride[0])>Math.abs(this.stride[1]))?[1,0]:[0,1]}})")
  112. } else if(dimension === 3) {
  113. code.push(
  114. "var s0=Math.abs(this.stride[0]),s1=Math.abs(this.stride[1]),s2=Math.abs(this.stride[2]);\
  115. if(s0>s1){\
  116. if(s1>s2){\
  117. return [2,1,0];\
  118. }else if(s0>s2){\
  119. return [1,2,0];\
  120. }else{\
  121. return [1,0,2];\
  122. }\
  123. }else if(s0>s2){\
  124. return [2,0,1];\
  125. }else if(s2>s1){\
  126. return [0,1,2];\
  127. }else{\
  128. return [0,2,1];\
  129. }}})")
  130. }
  131. } else {
  132. code.push("ORDER})")
  133. }
  134. }
  135. //view.set(i0, ..., v):
  136. code.push(
  137. "proto.set=function "+className+"_set("+args.join(",")+",v){")
  138. if(useGetters) {
  139. code.push("return this.data.set("+index_str+",v)}")
  140. } else {
  141. code.push("return this.data["+index_str+"]=v}")
  142. }
  143. //view.get(i0, ...):
  144. code.push("proto.get=function "+className+"_get("+args.join(",")+"){")
  145. if(useGetters) {
  146. code.push("return this.data.get("+index_str+")}")
  147. } else {
  148. code.push("return this.data["+index_str+"]}")
  149. }
  150. //view.index:
  151. code.push(
  152. "proto.index=function "+className+"_index(", args.join(), "){return "+index_str+"}")
  153. //view.hi():
  154. code.push("proto.hi=function "+className+"_hi("+args.join(",")+"){return new "+className+"(this.data,"+
  155. indices.map(function(i) {
  156. return ["(typeof i",i,"!=='number'||i",i,"<0)?this.shape[", i, "]:i", i,"|0"].join("")
  157. }).join(",")+","+
  158. indices.map(function(i) {
  159. return "this.stride["+i + "]"
  160. }).join(",")+",this.offset)}")
  161. //view.lo():
  162. var a_vars = indices.map(function(i) { return "a"+i+"=this.shape["+i+"]" })
  163. var c_vars = indices.map(function(i) { return "c"+i+"=this.stride["+i+"]" })
  164. code.push("proto.lo=function "+className+"_lo("+args.join(",")+"){var b=this.offset,d=0,"+a_vars.join(",")+","+c_vars.join(","))
  165. for(var i=0; i<dimension; ++i) {
  166. code.push(
  167. "if(typeof i"+i+"==='number'&&i"+i+">=0){\
  168. d=i"+i+"|0;\
  169. b+=c"+i+"*d;\
  170. a"+i+"-=d}")
  171. }
  172. code.push("return new "+className+"(this.data,"+
  173. indices.map(function(i) {
  174. return "a"+i
  175. }).join(",")+","+
  176. indices.map(function(i) {
  177. return "c"+i
  178. }).join(",")+",b)}")
  179. //view.step():
  180. code.push("proto.step=function "+className+"_step("+args.join(",")+"){var "+
  181. indices.map(function(i) {
  182. return "a"+i+"=this.shape["+i+"]"
  183. }).join(",")+","+
  184. indices.map(function(i) {
  185. return "b"+i+"=this.stride["+i+"]"
  186. }).join(",")+",c=this.offset,d=0,ceil=Math.ceil")
  187. for(var i=0; i<dimension; ++i) {
  188. code.push(
  189. "if(typeof i"+i+"==='number'){\
  190. d=i"+i+"|0;\
  191. if(d<0){\
  192. c+=b"+i+"*(a"+i+"-1);\
  193. a"+i+"=ceil(-a"+i+"/d)\
  194. }else{\
  195. a"+i+"=ceil(a"+i+"/d)\
  196. }\
  197. b"+i+"*=d\
  198. }")
  199. }
  200. code.push("return new "+className+"(this.data,"+
  201. indices.map(function(i) {
  202. return "a" + i
  203. }).join(",")+","+
  204. indices.map(function(i) {
  205. return "b" + i
  206. }).join(",")+",c)}")
  207. //view.transpose():
  208. var tShape = new Array(dimension)
  209. var tStride = new Array(dimension)
  210. for(var i=0; i<dimension; ++i) {
  211. tShape[i] = "a[i"+i+"]"
  212. tStride[i] = "b[i"+i+"]"
  213. }
  214. code.push("proto.transpose=function "+className+"_transpose("+args+"){"+
  215. args.map(function(n,idx) { return n + "=(" + n + "===undefined?" + idx + ":" + n + "|0)"}).join(";"),
  216. "var a=this.shape,b=this.stride;return new "+className+"(this.data,"+tShape.join(",")+","+tStride.join(",")+",this.offset)}")
  217. //view.pick():
  218. code.push("proto.pick=function "+className+"_pick("+args+"){var a=[],b=[],c=this.offset")
  219. for(var i=0; i<dimension; ++i) {
  220. code.push("if(typeof i"+i+"==='number'&&i"+i+">=0){c=(c+this.stride["+i+"]*i"+i+")|0}else{a.push(this.shape["+i+"]);b.push(this.stride["+i+"])}")
  221. }
  222. code.push("var ctor=CTOR_LIST[a.length+1];return ctor(this.data,a,b,c)}")
  223. //Add return statement
  224. code.push("return function construct_"+className+"(data,shape,stride,offset){return new "+className+"(data,"+
  225. indices.map(function(i) {
  226. return "shape["+i+"]"
  227. }).join(",")+","+
  228. indices.map(function(i) {
  229. return "stride["+i+"]"
  230. }).join(",")+",offset)}")
  231. //Compile procedure
  232. var procedure = new Function("CTOR_LIST", "ORDER", code.join("\n"))
  233. return procedure(CACHED_CONSTRUCTORS[dtype], order)
  234. }
  235. function arrayDType(data) {
  236. if(isBuffer(data)) {
  237. return "buffer"
  238. }
  239. if(hasTypedArrays) {
  240. switch(Object.prototype.toString.call(data)) {
  241. case "[object Float64Array]":
  242. return "float64"
  243. case "[object Float32Array]":
  244. return "float32"
  245. case "[object Int8Array]":
  246. return "int8"
  247. case "[object Int16Array]":
  248. return "int16"
  249. case "[object Int32Array]":
  250. return "int32"
  251. case "[object Uint8Array]":
  252. return "uint8"
  253. case "[object Uint16Array]":
  254. return "uint16"
  255. case "[object Uint32Array]":
  256. return "uint32"
  257. case "[object Uint8ClampedArray]":
  258. return "uint8_clamped"
  259. case "[object BigInt64Array]":
  260. return "bigint64"
  261. case "[object BigUint64Array]":
  262. return "biguint64"
  263. }
  264. }
  265. if(Array.isArray(data)) {
  266. return "array"
  267. }
  268. return "generic"
  269. }
  270. var CACHED_CONSTRUCTORS = {
  271. "float32":[],
  272. "float64":[],
  273. "int8":[],
  274. "int16":[],
  275. "int32":[],
  276. "uint8":[],
  277. "uint16":[],
  278. "uint32":[],
  279. "array":[],
  280. "uint8_clamped":[],
  281. "bigint64": [],
  282. "biguint64": [],
  283. "buffer":[],
  284. "generic":[]
  285. }
  286. ;(function() {
  287. for(var id in CACHED_CONSTRUCTORS) {
  288. CACHED_CONSTRUCTORS[id].push(compileConstructor(id, -1))
  289. }
  290. });
  291. function wrappedNDArrayCtor(data, shape, stride, offset) {
  292. if(data === undefined) {
  293. var ctor = CACHED_CONSTRUCTORS.array[0]
  294. return ctor([])
  295. } else if(typeof data === "number") {
  296. data = [data]
  297. }
  298. if(shape === undefined) {
  299. shape = [ data.length ]
  300. }
  301. var d = shape.length
  302. if(stride === undefined) {
  303. stride = new Array(d)
  304. for(var i=d-1, sz=1; i>=0; --i) {
  305. stride[i] = sz
  306. sz *= shape[i]
  307. }
  308. }
  309. if(offset === undefined) {
  310. offset = 0
  311. for(var i=0; i<d; ++i) {
  312. if(stride[i] < 0) {
  313. offset -= (shape[i]-1)*stride[i]
  314. }
  315. }
  316. }
  317. var dtype = arrayDType(data)
  318. var ctor_list = CACHED_CONSTRUCTORS[dtype]
  319. while(ctor_list.length <= d+1) {
  320. ctor_list.push(compileConstructor(dtype, ctor_list.length-1))
  321. }
  322. var ctor = ctor_list[d+1]
  323. return ctor(data, shape, stride, offset)
  324. }
  325. module.exports = wrappedNDArrayCtor