node-red-contrib-prib-functions 0.20.4 → 0.21.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -4,22 +4,22 @@
4
4
 
5
5
  * Data Analysis - statistical metrics that has real time option
6
6
  * Matrix
7
- * Transform
8
- * Test
7
+ * Transform
8
+ * Test
9
9
  * Load Injector
10
- * Monitor Flow
11
- * append
12
- * Spawn Process
13
- * Host Available
14
- * node.js os metrics
15
- * Levenshtein Distance
10
+ * Monitor Flow
11
+ * append
12
+ * Spawn Process
13
+ * Host Available
14
+ * node.js os metrics
15
+ * Levenshtein Distance
16
16
 
17
17
  ------------------------------------------------------------
18
18
 
19
19
  ## Data Analysis
20
20
 
21
21
  Real time metrics which are recalculated on single of data point and posted in msg.result.
22
- Key and value can be selected from msg.payload.
22
+ Key and value can be selected from msg.payload. Includes lag (seasonal) along with delta(defference).
23
23
  Sending message with topic"@stats" places message with all stats on second port.
24
24
  If realtime metrics then a third port is shown where the message is sent if it is an outlier
25
25
  being outside 3 standard deviations from mean. This can be changed to median and number of deviations.
@@ -27,7 +27,6 @@ being outside 3 standard deviations from mean. This can be changed to median and
27
27
  ![Data Analysis Realtime](documentation/DataAnalysisRealtime.JPG "Data Analysis Realtime")
28
28
  ![Data Analysis Pearson R](documentation/DataAnalysisPearsonR.JPG "Data Analysis Pearson R")
29
29
 
30
-
31
30
  A set of data analysis functions that can be run over an array of data
32
31
 
33
32
  Single value metrics:
@@ -78,7 +77,7 @@ example:
78
77
 
79
78
  Define a matrix and perform various functions
80
79
 
81
- * Define / Define Empty / Create / Create Like/ clone"
80
+ * Define / Define Empty / Create / Create Like/ clone
82
81
  * Add / Add Row to Row / Add to Cell / Add Row / Subtract Cell
83
82
  * Multiple / Multiple Cell / Divide Cell / Divide Row
84
83
  * Transpose
@@ -110,48 +109,48 @@ Messages generates a message for each row or record.
110
109
 
111
110
  Transformations:
112
111
 
113
- * Array to CSV
114
- * Array to HTML
115
- * Array to ISO8385
116
- * Array to Messages
117
- * Array to xlsx / xlsx object (excel uses [xlsx][7])
118
- * AVRO to JSON (uses [avsc][6])
112
+ * Array to CSV
113
+ * Array to HTML
114
+ * Array to ISO8385
115
+ * Array to Messages
116
+ * Array to xlsx / xlsx object (excel uses [xlsx][7])
117
+ * AVRO to JSON (uses [avsc][6])
119
118
  * Buffer to comprossed
120
- * Confluence to JSON
119
+ * Confluence to JSON
121
120
  * Compressed to Buffer
122
121
  * Compressed to String
123
122
  * COmpressed to JSON
124
- * CSV to Array
125
- * CSV to HTML
126
- * CSV to Messages
127
- * CSVWithHeader to Array
128
- * CSVWithHeader to HTML
129
- * CSVWithHeader to JSON
130
- * ISO8385 to Array
131
- * ISO8385 to JSON
132
- * JSON to Array
133
- * JSON to Confluence
134
- * JSON to CSV
135
- * JSON to AVRO (uses [avsc][6])
136
- * JSON to ISO8385
137
- * JSON to Messages
138
- * JSON to String
139
- * JSON to xlsx / xlsx object (excel uses [xlsx][7])
140
- * JSON to XML (uses [fast-xml-parser][4])
141
- * String to JSON
142
- * path to Basename
143
- * path to Dirname
144
- * path to Extname
145
- * path to Format
146
- * path to Is Absolute
147
- * path to Join
148
- * path to Parse
149
- * path to Normalize
150
- * path to Resolve
151
- * snappy compress (uses [snappy][5], must install separately)
152
- * snappy uncompress (uses [snappy][5], must install separately)
153
- * xlsx / xlsx object to array/JSON (excel uses [xlsx][7])
154
- * XML to JSON (uses [fast-xml-parser][4])
123
+ * CSV to Array
124
+ * CSV to HTML
125
+ * CSV to Messages
126
+ * CSVWithHeader to Array
127
+ * CSVWithHeader to HTML
128
+ * CSVWithHeader to JSON
129
+ * ISO8385 to Array
130
+ * ISO8385 to JSON
131
+ * JSON to Array
132
+ * JSON to Confluence
133
+ * JSON to CSV
134
+ * JSON to AVRO (uses [avsc][6])
135
+ * JSON to ISO8385
136
+ * JSON to Messages
137
+ * JSON to String
138
+ * JSON to xlsx / xlsx object (excel uses [xlsx][7])
139
+ * JSON to XML (uses [fast-xml-parser][4])
140
+ * String to JSON
141
+ * path to Basename
142
+ * path to Dirname
143
+ * path to Extname
144
+ * path to Format
145
+ * path to Is Absolute
146
+ * path to Join
147
+ * path to Parse
148
+ * path to Normalize
149
+ * path to Resolve
150
+ * snappy compress (uses [snappy][5], must install separately)
151
+ * snappy uncompress (uses [snappy][5], must install separately)
152
+ * xlsx / xlsx object to array/JSON (excel uses [xlsx][7])
153
+ * XML to JSON (uses [fast-xml-parser][4])
155
154
 
156
155
  Note, snappy needs to be installed separately as can have issues with auto install as build binaries.
157
156
 
@@ -219,7 +218,6 @@ Test example:
219
218
 
220
219
  ![Load Injector example](documentation/LoadInjectorTest.JPG "Load Injector example")
221
220
 
222
-
223
221
  ------------------------------------------------------------
224
222
 
225
223
  ## Monitor Flow
@@ -235,34 +233,29 @@ Test example:
235
233
 
236
234
  ------------------------------------------------------------
237
235
 
238
- ## Spawn Process
236
+ ## Host Available
239
237
 
240
- Spawn process as per node.js manual with ability to set working directory, environment variables
241
- and argument passed to process. STDOUT and STDERR are sent as individual messages.
242
- RC port is sent a message on closure.
243
- Takes in messages that starts a process with ability to add environment values.
244
- Message can be sent to kill the process.
238
+ Test if host is available sending msg to up or down port so action can be taken.
239
+ Message only sent on state change or if message is sent which doesn't have topic refreshHostAvailable.
240
+ This topic forces a check rather than time check which can be set.
245
241
 
246
- ![Spawn Process](documentation/SpawnProcess.JPG "Spawn Process")
242
+ ![Host Available](documentation/hostAvailable.JPG "Host Available")
247
243
 
248
244
  Test example:
249
245
 
250
- ![Spawn Process example](documentation/SpawnProcessTest.JPG "Spawn Process example")
251
-
246
+ ![Host Available example](documentation/hostAvailableTest.JPG "Host Available example")
252
247
 
253
248
  ------------------------------------------------------------
254
249
 
255
- ## Host Available
250
+ ## Monitor System
256
251
 
257
- Test if host is available sending msg to up or down port so action can be taken.
258
- Message only sent on state change or if message is sent which doesn't have topic refreshHostAvailable.
259
- This topic forces a check rather than time check which can be set.
252
+ System monitoring metrics
260
253
 
261
- ![Host Available](documentation/hostAvailable.JPG "Host Available")
254
+ ![Monitor System](documentation/monitorSystem.JPG "Monitor System")
262
255
 
263
256
  Test example:
264
257
 
265
- ![Host Available example](documentation/hostAvailableTest.JPG "Host Available example")
258
+ ![Monitor System example](documentation/monitorSystemTest.JPG "Monitor System example")
266
259
 
267
260
  ------------------------------------------------------------
268
261
 
@@ -278,6 +271,22 @@ Test example:
278
271
 
279
272
  ------------------------------------------------------------
280
273
 
274
+ ## Spawn Process
275
+
276
+ Spawn process as per node.js manual with ability to set working directory, environment variables
277
+ and argument passed to process. STDOUT and STDERR are sent as individual messages.
278
+ RC port is sent a message on closure.
279
+ Takes in messages that starts a process with ability to add environment values.
280
+ Message can be sent to kill the process.
281
+
282
+ ![Spawn Process](documentation/SpawnProcess.JPG "Spawn Process")
283
+
284
+ Test example:
285
+
286
+ ![Spawn Process example](documentation/SpawnProcessTest.JPG "Spawn Process example")
287
+
288
+ ------------------------------------------------------------
289
+
281
290
  # Install
282
291
 
283
292
  Run the following command in the root directory of your Node-RED install
@@ -292,15 +301,16 @@ Test/example flow in test/generalTest.json
292
301
 
293
302
  # Version
294
303
 
304
+ 0.21.0 Add lag/seasonal to real time data analystics
305
+
306
+ 0.20.3 Add difference + monitor system
307
+
295
308
  0.19.0 Improve load injector, fix bug in test comparing buffers, add compression tranforms
296
309
 
297
310
  0.18.0 Add matrix node
298
311
 
299
312
  0.17.0 Add finished wire to load injector
300
313
 
301
- 0.16.0 fix data analysis variance and stddev, add sample, add tests
302
-
303
-
304
314
 
305
315
  # Author
306
316
 
@@ -11,7 +11,7 @@
11
11
  outputs: {value:(["realtime","realtimePredict"].includes(this.action)?3:2),required:true},
12
12
  outliersBase: {value:"avg",required:false},
13
13
  outliersStdDevs: {value:"3",required:false},
14
- term: {value:"10",required:false},
14
+ term: {value:10,required:false},
15
15
  keyProperty: {value:"msg.topic",required:false},
16
16
  dataProperty: {value:"msg.payload",required:false},
17
17
  dataProperties: {value:["msg.payload[0]","msg.payload[1]"],required:false}
@@ -28,6 +28,7 @@
28
28
  },
29
29
  oneditprepare: function() {
30
30
  const node=this;
31
+ node.lag??=1
31
32
  $("#node-input-keyProperty").change(function() {
32
33
  if( [null,""].includes(node.keyProperty) ) {
33
34
  $(this).val("msg.topic");
@@ -46,7 +47,7 @@
46
47
  }
47
48
  });
48
49
  $("#node-input-action").change(function() {
49
- if(["differenceSeasonal","differenceSeasonalSecondOrder"].includes( $(this).val() )) {
50
+ if(["differenceSeasonal","differenceSeasonalSecondOrder","realtime","realtimePredict"].includes( $(this).val() )) {
50
51
  $(".form-row-http-in-lag").show();
51
52
  } else {
52
53
  $(".form-row-http-in-lag").hide();
@@ -69,10 +70,17 @@
69
70
  if(node.term>1) $(this).val(1);
70
71
  });
71
72
  } else {
73
+ $("#node-input-lag").attr({min:1,max:1000,step:1});
74
+ $("#node-input-lag").change(function() {
75
+ const v=$(this).val();
76
+ if(v<1) $(this).val(1);
77
+ if(v>1000) $(this).val(1000);
78
+ });
72
79
  $("#node-input-term").attr({min:1,max:100,step:1});
73
80
  $("#node-input-term").change(function() {
74
- if(node.term<1) $(this).val(1);
75
- if(node.term>100) $(this).val(100);
81
+ const v=$(this).val()
82
+ if(v<1) $(this).val(1);
83
+ if(v>100) $(this).val(100);
76
84
  });
77
85
  }
78
86
  if(["realtime","realtimePredict"].includes( $(this).val() )) {
@@ -94,8 +102,8 @@
94
102
  $(".node-input-dataProperties-container-row").hide();
95
103
  $(".form-row-http-in-dataProperty").show();
96
104
  }
97
- $("#node-input-term").change();
98
105
  $("#node-input-lag").change();
106
+ $("#node-input-term").change();
99
107
  });
100
108
 
101
109
  node.dataProperties.forEach((r)=>addDataProperty(r));
@@ -248,12 +256,18 @@ Data is not persisted so metrics start from zero sample set on node recycle.
248
256
  <p>
249
257
  If real-time stats then a message can send directive instruction in topic:
250
258
  <dl>
251
- <dt>"@predict <key>"<\dt><dd></dd>Send send predictions to second port for selected key.
252
- <dt>"@stats"<\dt><dd></dd>send all stored metrics and retained datapoints to second port.
253
- <dt>"@stats reset"<\dt><dd>Reset all stats and "@stats reset <key>" will reset a particular data point.</dd>
254
- <dt>"@stats set"<\dt><dd>Set stats with ith msg.payload and "@stats set <a data point>" will set a particular data point with msg.payload.</dd>
259
+ <dt>"@predict <key>"</dt><dd></dd>Send send predictions to second port for selected key.
260
+ <dt>"@stats"</dt><dd></dd>send all stored metrics and retained datapoints to second port.
261
+ <dt>"@stats reset"</dt><dd>Reset all stats and "@stats reset <key>" will reset a particular data point.</dd>
262
+ <dt>"@stats set"</dt><dd>Set stats with ith msg.payload and "@stats set &lt;a data point&gt;" will set a particular data point with msg.payload.</dd>
255
263
  </dl>
256
264
  </p>
265
+ <dl>
266
+ <dt>Lag</dt><dd>If greater that 1 generates seasonal difference with degree lag</dd>
267
+ <dt>Term</dt><dd>The depth of moving Average</dd>
268
+ </dl>
269
+ <p>
270
+ </p>
257
271
  <p>
258
272
  Outliers are not within:
259
273
  <ul>
@@ -69,10 +69,10 @@ EMA.prototype.sample=function(value) {
69
69
  return this;
70
70
  }
71
71
 
72
- function setDataPoint(value,term,node,dp) {
73
- if(logger.active) logger.send({label:"setDataPoint",value:value,term,dp});
74
- if(!dp.values) {
75
- Object.assign(dp,{
72
+ function setDataPoint(value,term,node,dataPoint) {
73
+ if(logger.active) logger.send({label:"setDataPoint",value:value,term,dataPoint});
74
+ if(!dataPoint.values) {
75
+ Object.assign(dataPoint,{
76
76
  values:[],
77
77
  avg:0,
78
78
  count:0,
@@ -83,43 +83,44 @@ function setDataPoint(value,term,node,dp) {
83
83
  sum:0,
84
84
  sumSquared:0,
85
85
  sumCubed:0,
86
- term:term,
86
+ term:term??node.term,
87
87
  weightedMovingSum:0,
88
88
  exponentialWeightedMoving:[new EMA(0.25),new EMA(0.5),new EMA(0.75)]
89
89
  });
90
- }
91
- ;
92
- const count=++dp.count,values=dp.values;
90
+ };
91
+ const count=++dataPoint.count,values=dataPoint.values;
93
92
  values.push(value);
94
- dp.isMaxSize=(values.length>dp.term);
95
- dp.removedValue=(dp.isMaxSize?values.shift():0);
96
- const removedValue=dp.removedValue;
97
- dp.max=Math.max(dp.max||value,value);
98
- dp.min=Math.min(dp.min||value,value);
99
- dp.range=dp.max-dp.min;
100
- dp.sum+=value;
101
- dp.sumSquared+=Math.pow(value,2);
102
- dp.sumCubed+=Math.pow(value,3);
103
- dp.movingSum+=value-removedValue;
104
- dp.movingSumSquared+=Math.pow(value,2)-Math.pow(removedValue,2);
105
- dp.movingSumCubed+=Math.pow(value,3)-Math.pow(removedValue,3);
106
- dp.avg=dp.sum/count;
107
- const avg=dp.avg;
108
- dp.normalised=dp.range ? (value-avg)/dp.range : 0;
109
- dp.movingAvg=dp.movingSum/values.length;
110
- dp.variance=dp.sumSquared/count - Math.pow(avg,2);
111
- dp.stdDev=Math.sqrt(dp.variance);
112
- dp.movingVariance=dp.movingSumSquared/values.length - Math.pow(dp.movingAvg,2);
113
- dp.movingStdDev=Math.sqrt(dp.movingVariance);
114
- dp.median=functions.median(values);
115
- dp.standardized=( (value-avg)/dp.stdDev )||0;
116
- dp.movingStandardized=( (value-dp.movingAvg)/dp.movingStdDev )||0;
117
- dp.skewness=(dp.sumCubed-3*avg*dp.variance-Math.pow(avg,3))/dp.variance*dp.stdDev;
118
- dp.movingSkewness=(dp.movingSumCubed-3*dp.movingAvg*dp.movingVariance-Math.pow(dp.movingAvg,3))/dp.movingVariance*dp.stdDev;
119
- dp.outlier=node.outliersFunction(node,dp,value);
120
- dp.weightedMovingSum+=count*value;
121
- dp.weightedMovingAvg=(dp.weightedMovingAvg*2/count)/(count+1);
122
- dp.exponentialWeightedMoving.forEach(c=>c.sample(value));
93
+ const movingTerm=Math.min(values.length,dataPoint.term)
94
+ dataPoint.isMaxSize=(values.length>dataPoint.maxSize);
95
+ dataPoint.removedMovingValue=(dataPoint.isMaxSize?values[values.length-dataPoint.term]:0);
96
+ dataPoint.removedValue=(dataPoint.isMaxSize?values.shift():0);
97
+ const removedMovingValue=dataPoint.removedMovingValue;
98
+ dataPoint.max=Math.max(dataPoint.max||value,value);
99
+ dataPoint.min=Math.min(dataPoint.min||value,value);
100
+ dataPoint.range=dataPoint.max-dataPoint.min;
101
+ dataPoint.sum+=value;
102
+ dataPoint.sumSquared+=Math.pow(value,2);
103
+ dataPoint.sumCubed+=Math.pow(value,3);
104
+ dataPoint.movingSum+=value-removedMovingValue;
105
+ dataPoint.movingSumSquared+=Math.pow(value,2)-Math.pow(removedMovingValue,2);
106
+ dataPoint.movingSumCubed+=Math.pow(value,3)-Math.pow(removedMovingValue,3);
107
+ dataPoint.avg=dataPoint.sum/count;
108
+ const avg=dataPoint.avg;
109
+ dataPoint.normalised=dataPoint.range ? (value-avg)/dataPoint.range : 0;
110
+ dataPoint.movingAvg=dataPoint.movingSum/movingTerm;
111
+ dataPoint.variance=dataPoint.sumSquared/count - Math.pow(avg,2);
112
+ dataPoint.stdDev=Math.sqrt(dataPoint.variance);
113
+ dataPoint.movingVariance=dataPoint.movingSumSquared/movingTerm - Math.pow(dataPoint.movingAvg,2);
114
+ dataPoint.movingStdDev=Math.sqrt(dataPoint.movingVariance);
115
+ dataPoint.median=functions.median(values);
116
+ dataPoint.standardized=( (value-avg)/dataPoint.stdDev )||0;
117
+ dataPoint.movingStandardized=( (value-dataPoint.movingAvg)/dataPoint.movingStdDev )||0;
118
+ dataPoint.skewness=(dataPoint.sumCubed-3*avg*dataPoint.variance-Math.pow(avg,3))/dataPoint.variance*dataPoint.stdDev;
119
+ dataPoint.movingSkewness=(dataPoint.movingSumCubed-3*dataPoint.movingAvg*dataPoint.movingVariance-Math.pow(dataPoint.movingAvg,3))/dataPoint.movingVariance*dataPoint.stdDev;
120
+ dataPoint.outlier=node.outliersFunction(node,dataPoint,value);
121
+ dataPoint.weightedMovingSum+=count*value;
122
+ dataPoint.weightedMovingAvg=(dataPoint.weightedMovingAvg*2/count)/(count+1);
123
+ dataPoint.exponentialWeightedMoving.forEach(c=>c.sample(value));
123
124
  }
124
125
  function getColumns(node) {
125
126
  if(node.columns) {
@@ -223,9 +224,9 @@ functions={
223
224
  }
224
225
  }
225
226
  node.samples++;
226
- for(let v,dp,i=0; i<node.dataProperties.length; i++) {
227
- v=d[i];
228
- dp=node.dataPoints[i];
227
+ for(let i=0; i<node.dataProperties.length; i++) {
228
+ const v=d[i];
229
+ const dp=node.dataPoints[i];
229
230
  dp.sum+=v;
230
231
  dp.sumSquared+=v*v;
231
232
  for(let j=i+1; j<node.dataProperties.length; j++) {
@@ -280,10 +281,23 @@ functions={
280
281
  }
281
282
  setDataPoint(d.value,term,node,dp);
282
283
  if(dp.delta) {
283
- setDataPoint(d.value-dp.values[dp.values.length-2],term,node,dp.delta);
284
+ if(dp.values.length>1)
285
+ setDataPoint(d.value-dp.values[dp.values.length-2],term,node,dp.delta);
284
286
  } else {
285
287
  dp.delta={};
286
288
  }
289
+ if(node.lag>1) {
290
+ const vectorSize=dp.values.length
291
+ if(dp.lag) {
292
+ if(node.lag<=vectorSize){
293
+ setDataPoint(d.value-dp.values[vectorSize-node.lag],term,node,dp.lag)
294
+ const values=dp.lag.values
295
+ if(values.length>1) setDataPoint(values[values.length-1]-values[values.length-2],term,node,dp.lag.delta)
296
+ }
297
+ } else {
298
+ dp.lag={delta:{}}
299
+ }
300
+ }
287
301
  return dp;
288
302
  },
289
303
  sampleVariance:(d)=>{
@@ -310,11 +324,15 @@ module.exports = function (RED) {
310
324
  function dataAnalysisNode(n) {
311
325
  RED.nodes.createNode(this, n);
312
326
  const node=Object.assign(this,
313
- {outliersStdDevs:3,crossNormalisedDeltas:crossNormalisedDeltas.bind(this)},
327
+ {outliersStdDevs:3,crossNormalisedDeltas:crossNormalisedDeltas.bind(this),lag:1,term:3},
314
328
  n,
315
329
  {maxErrorDisplay:10,dataPoint:{}}
316
330
  );
317
331
  try{
332
+ node.lag=Number(node.lag)
333
+ if(Number(node.term)==NaN)throw Error("term not a number, value:"+JSON.stringify(node.term))
334
+ node.term=Number(node.term)
335
+ node.maxSize=Math.max(node.term,node.lag)
318
336
  if(functions.hasOwnProperty(node.action)) {
319
337
  node.actionfunction=functions[node.action];
320
338
  } else {
@@ -349,6 +367,7 @@ module.exports = function (RED) {
349
367
  node.getData=eval(node.getDatafunction);
350
368
  node.status({fill:"green",shape:"ring",text:"Ready"});
351
369
  } catch(ex) {
370
+ if(logger.active) logger.send({label:"initialise error",action:node.action,message:ex.message,stack:ex.stack});
352
371
  logger.send({label:"initialise error",node:n});
353
372
  node.error(ex);
354
373
  node.status({fill:"red",shape:"ring",text:"Invalid setup "+ex.message});
@@ -388,6 +407,7 @@ module.exports = function (RED) {
388
407
  throw Error("unknown");
389
408
  }
390
409
  } catch(ex) {
410
+ if(logger.active) logger.send({label:"error input",action:node.action,message:ex.message,stack:ex.stack});
391
411
  node.error(msg.topic+" failed "+ex.message);
392
412
  }
393
413
  return;
@@ -404,10 +424,10 @@ module.exports = function (RED) {
404
424
  break;
405
425
  }
406
426
  } catch(ex) {
407
- if(logger.active) logger.send({label:"error input",action:node.action,message:ex.message,stack:ex.stack});
408
427
  msg.error=ex.message;
409
428
  if(node.maxErrorDisplay) {
410
429
  --node.maxErrorDisplay;
430
+ logger.send({label:"error input",action:node.action,message:ex.message,stack:ex.stack});
411
431
  if(node.action=="realtime") {
412
432
  node.error(node.action+" error: "+ex.message);
413
433
  } else {
Binary file
package/matrix/matrix.js CHANGED
@@ -1,8 +1,54 @@
1
1
  const logger = new (require("node-red-contrib-logger"))("Matrix");
2
2
  logger.sendInfo("Copyright 2022 Jaroslav Peter Prib");
3
3
 
4
+ const typedArrays= {Array:Array,Int8Array:Int8Array,Uint8Array:Uint8Array,Uint8ClampedArray:Uint8ClampedArray,Int16Array:Int16Array,Uint16Array:Uint16Array,
5
+ Int32Array:Int32Array,Uint32Array:Uint32Array,Float32Array:Float32Array,
6
+ Float64Array:Float64Array,BigInt64Array:BigInt64Array,BigUint64Array:BigUint64Array}
7
+
8
+ Object.keys(typedArrays).map(t=>typedArrays[t]).forEach(object=>{
9
+ if(object.prototype.setAll==null)
10
+ Object.defineProperty(object.prototype, "setAll", {
11
+ value(call,size=this.length) {
12
+ let i=size
13
+ while(i) this[--i]=call();
14
+ return this;
15
+ },
16
+ writable: true,
17
+ configurable: true
18
+ })
19
+ if(object.prototype.setOne==null)
20
+ Object.defineProperty(object.prototype, "setOne", {
21
+ value(call,size=this.length) {
22
+ let i=size
23
+ while(i) this[--i]=1;
24
+ return this;
25
+ },
26
+ writable: true,
27
+ configurable: true
28
+ })
29
+ if(object.prototype.setZero==null)
30
+ Object.defineProperty(object.prototype, "setZero", {
31
+ value(call,size=this.length) {
32
+ let i=size
33
+ while(i) this[--i]=0;
34
+ return this;
35
+ },
36
+ writable: true,
37
+ configurable: true
38
+ })
39
+ if(object.prototype.setRandom==null)
40
+ Object.defineProperty(object.prototype, "setRandom", {
41
+ value(size=this.length) {
42
+ return this.setAll(Math.random,size)
43
+ },
44
+ writable: true,
45
+ configurable: true
46
+ })
47
+ })
48
+
4
49
  const zeroFloat32Value=1e-6;
5
- function Matrix(rows,columns,fill) {
50
+ function Matrix(rows,columns,fill,dataType="Float32Array") {
51
+ this.dataType=dataType
6
52
  if(rows instanceof Array) {
7
53
  this.rows=rows.length;
8
54
  if(this.rows==0) throw Error("expected rows")
@@ -15,11 +61,19 @@ function Matrix(rows,columns,fill) {
15
61
  }
16
62
  if(rows instanceof Object) {
17
63
  Object.assign(this,rows);
64
+ this.dataType??="Float32Array"
18
65
  } else {
19
66
  this.rows=rows;
20
67
  this.columns=columns;
21
68
  }
69
+ if(this.columns) this.columns=parseInt(this.columns);
70
+ if(this.rows)this.rows=parseInt(this.rows)
71
+ if(this.rowsMax) this.rowsMax=parseInt(this.rowsMax)
22
72
  this.createVector();
73
+ if(fill) {
74
+ if(fill instanceof Function) this.setAll(fill)
75
+ else this.vector.set(fill)
76
+ }
23
77
  return this;
24
78
  }
25
79
  Matrix.prototype.add=function(matrix){
@@ -52,7 +106,7 @@ Matrix.prototype.addRow2Row=function(rowA,rowB,factor=1,startColumn=0,endColumn=
52
106
  return this;
53
107
  }
54
108
  Matrix.prototype.backwardSubstitution=function(){
55
- const vector=new Float32Array(this.rows);
109
+ const vector=new typedArrays[this.dataType](this.rows);
56
110
  for(let row=this.rows-1; row>=0; row--) {
57
111
  vector[row] = this.get(row,this.rows);
58
112
  for(let column=row+1; column<this.rows; column++) {
@@ -101,15 +155,17 @@ Matrix.prototype.createVector=function(){
101
155
  this.sizeMax=this.rowsMax*this.columns;
102
156
  }
103
157
  if(this.columns==null) throw Error("columns not specified")
104
- this.size=this.rows*this.columns
105
- if(this.sizeMax==null) this.sizeMax=this.size
106
158
  } else {
107
159
  if(this.columns==null) throw Error("columns not specified")
160
+ if(this.columns==0) throw Error("columns = 0")
108
161
  if(this.rows==null){
109
162
  this.rows=0;
110
163
  }
111
164
  }
112
- this.vector=new Float32Array(this.sizeMax);
165
+ this.size=this.rows*this.columns
166
+ if(this.sizeMax==null) this.sizeMax=this.size
167
+ if(this.sizeMax==null) throw Error("max size not specified or calculated")
168
+ this.vector=new typedArrays[this.dataType](this.sizeMax);
113
169
  return this;
114
170
  }
115
171
  Matrix.prototype.divideCell=function(row,column,value){
@@ -472,7 +528,6 @@ Matrix.prototype.multiplyRow=function(row,factor){
472
528
  return this;
473
529
  }
474
530
  Matrix.prototype.norm=function(){
475
-
476
531
  return Math.sqrt(this.reduce((aggregate,cell)=>aggregate+cell*cell))
477
532
  }
478
533
  Matrix.prototype.reduce=function(call,aggregate=0){
@@ -560,17 +615,48 @@ Matrix.prototype.set=function(row,column,value){
560
615
  this.vector[this.getIndex(row,column)]=value;
561
616
  return this;
562
617
  }
618
+ Matrix.prototype.setAll=function(call){
619
+ for(let offset=0,row=0;row<this.rows;row++) {
620
+ for(let column=0;column<this.columns;column++) {
621
+ this.vector[offset]=call.apply(this,[row,column,this.vector[offset],this.vector,offset,this]);
622
+ offset++
623
+ }
624
+ }
625
+ this.rows=this.rowsMax
626
+ this.size=this.sizeMax
627
+ return this;
628
+ }
563
629
  Matrix.prototype.setDeterminant=function(){
564
630
  this.determinant=this.getDeterminantUsingRowEchelonForm();
565
631
  return this.determinant;
566
632
  }
633
+ Matrix.prototype.setIdentity=function(){
634
+ if(this.columns!=this.rowsMax) throw Error("number of columns not equal rows")
635
+ this.setZero()
636
+ for(let offset=0;offset<this.size;offset+=this.columns+1) this.vector[offset]=1;
637
+ return this;
638
+ }
639
+ Matrix.prototype.setWithFunction=function(f){
640
+ this.vector[f](this.sizeMax)
641
+ this.rows=this.rowsMax
642
+ this.size=this.sizeMax;
643
+ return this;
644
+ }
645
+ Matrix.prototype.setOne=function(){
646
+ return this.setWithFunction("setOne")
647
+ }
648
+ Matrix.prototype.setRandom=function(){
649
+ return this.setWithFunction("setRandom")
650
+ }
567
651
  Matrix.prototype.setRow=function(vector,row){
568
652
  this.vector.set(vector, row*this.columns);
569
653
  return this;
570
654
  }
571
655
  Matrix.prototype.setRunningSum=function(){
572
656
  this.forEachCellLowerTriangle((cell,row,column,vector,offset,object)=>vector[offset]=1);
573
- return this;
657
+ }
658
+ Matrix.prototype.setZero=function(){
659
+ return this.setWithFunction("setZero")
574
660
  }
575
661
  Matrix.prototype.substract=function(matrix){
576
662
  this.forEachCellPairSet(matrix,(cellA,cellB)=>cellA-cellB)
@@ -610,5 +696,4 @@ Matrix.prototype.transpose=function(){
610
696
  this.forEachCell((cell,row,column)=>matrix.set(column,row,cell))
611
697
  return matrix;
612
698
  }
613
-
614
699
  module.exports=Matrix;