├── LICENSE ├── QR.hhs ├── QR_test.hhs ├── README.md ├── all.hhs ├── any_missing.hhs ├── any_missing_test.hhs ├── bounds.hhs ├── bounds_test.hhs ├── ceil.hhs ├── ceil_test.hhs ├── check_input.hhs ├── check_input_test.hhs ├── cholesky.hhs ├── cholesky_test.hhs ├── correcoef.hhs ├── correcoef_test.hhs ├── cov.hhs ├── cov_test.hhs ├── cubic_equation.hhs ├── cubic_equation_test ├── deep_copy.hhs ├── det.hhs ├── det_test.hhs ├── dot_divide.hhs ├── dot_divide_test.hhs ├── dot_multiply.hhs ├── dot_multiply_test.hhs ├── factorial.hhs ├── factorial_test.hhs ├── fillmissing.hhs ├── fillmissing_test.hhs ├── flatten.hhs ├── floor.hhs ├── floor_test.hhs ├── gcd.hhs ├── gcd_test.hhs ├── hankel.hhs ├── hankel_test.hhs ├── hedgehog-package.json ├── http-server └── server.js ├── identity.hhs ├── identity_test.hhs ├── inv.hhs ├── iqr.hhs ├── iqr_test.hhs ├── is_diag.hhs ├── is_diag_test.hhs ├── is_local_max.hhs ├── is_local_max_test.hhs ├── is_local_min.hhs ├── is_local_min_test.hhs ├── is_missing.hhs ├── is_missing_test.hhs ├── is_number.hhs ├── is_symmetric.hhs ├── is_symmetric_test.hhs ├── is_tril.hhs ├── is_tril_test.hhs ├── is_triu.hhs ├── is_triu_test.hhs ├── lsolve.hhs ├── lsolve_test.hhs ├── lu.hhs ├── lu_test.hhs ├── lusolve.hhs ├── lusolve_test.hhs ├── magic.hhs ├── magic_test.hhs ├── max.hhs ├── mean.hhs ├── mean_test.hhs ├── median.hhs ├── median_test.hhs ├── min.hhs ├── mink.hhs ├── mink_test.hhs ├── missing.hhs ├── missing_test.hhs ├── multinomial.hhs ├── multinomial_test.hhs ├── ndim.hhs ├── ndim_test.hhs ├── normalize.hhs ├── normalize_test.hhs ├── package-lock.json ├── pascal.hhs ├── pascal_test.hhs ├── percentile.hhs ├── percentile_test.hhs ├── quadratic_equation.hhs ├── quadratic_equation_test.hhs ├── quantile.hhs ├── quantile_test.hhs ├── quartic_equation.hhs ├── quartic_equation_test.hhs ├── reshape.hhs ├── reshape_test.hhs ├── resize.hhs ├── resize_test.hhs ├── rmmissing.hhs ├── rmmissing_test.hhs ├── rms.hhs ├── rms_test.hhs ├── shape.hhs ├── smooth.hhs ├── smooth_test.hhs ├── standard_missing.hhs ├── standard_missing_test.hhs ├── sum.hhs ├── test.hhs ├── toeplitz.hhs ├── trace.hhs ├── trace_test.hhs ├── transpose.hhs ├── transpose_test.hhs ├── usolve.hhs ├── usolve_test.hhs ├── vander.hhs └── vander_test.hhs /QR.hhs: -------------------------------------------------------------------------------- 1 | /** 2 | * @author Jason Reynolds 3 | * @param input - the matrix to be decomposed via the QR method 4 | * @returns - 2 object values of type Mat, Q and R, the matrices it decomposes input into, and input = Q*R 5 | * 6 | * simple js wrapper for QR function 7 | * function that takes in a raw or object matrix, decomposes it using QR method, and return the Q and R as objects 8 | */ 9 | 10 | 11 | 12 | 13 | function QR(input) { 14 | 15 | *import math: deep_copy 16 | 17 | // argument number 18 | if (arguments.length === 0) { 19 | throw new Error('Exception occurred in QR - no argument given'); 20 | } 21 | else if (arguments.length > 1) { 22 | throw new Error('Exception occurred in QR - wrong argument number'); 23 | } 24 | 25 | // type check 26 | if (!(Array.isArray(input)) && !(input instanceof Mat) && !(input instanceof Tensor)) { 27 | throw new Error('Exception occurred in QR - input is not a Mat, Tensor or JS Array'); 28 | } 29 | 30 | //declare raw_in as input.clone() if Mat type otherwise, input itself 31 | let in_type = (input instanceof Mat) || (input instanceof Tensor) 32 | let raw_in = (in_type) ? input.clone() : deep_copy(input); 33 | 34 | //in the case of input being a Mat, degrade to JS array for 'raw_in' to apply for both JS arrays and Mat objects 35 | if (in_type) { 36 | raw_in = raw_in.val; 37 | } 38 | //assign result the object/values obtained from mathjs.qr 39 | let result = mathjs.qr(raw_in); 40 | //return said values from object 41 | return { 42 | Q: mat(result.Q), 43 | R: mat(result.R) 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /QR_test.hhs: -------------------------------------------------------------------------------- 1 | /** 2 | * @author Jason Reynolds 3 | * @param 4 | * @returns 5 | * 6 | * Function unit test for QR, makes sure it decomposes the right answer for both Q and R with hedgehog's built in tolerance level 7 | */ 8 | 9 | 10 | 11 | function QR_test() { 12 | 13 | *import math: QR 14 | 15 | if (QR([[1, -1, 4], [1, 4, -2], [1, 4, 2], [1, -1, 0]]).R.val === [[2, 3, 2], [0, 5, -2], [0, 0, 4], [0, 0, 0]]) { 16 | 17 | } 18 | 19 | else { 20 | throw 'unit test failed for QR_test.' 21 | } 22 | 23 | 24 | //built in tolerance in hedgehog lab fixes the 0.49999 issue 25 | if (QR([[1, -1, 4], [1, 4, -2], [1, 4, 2], [1, -1, 0]]).Q.val === [[0.5, -0.5, 0.5, 0.5], [0.5, 0.5, -0.5, 0.5], [0.5, 0.5, 0.5, -0.5], [0.5, -0.5, -0.5, -0.5]]) { 26 | 27 | } 28 | 29 | else { 30 | throw 'unit test failed for QR_test.' 31 | } 32 | 33 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # This repo is archived 2 | ### Please contribute this repo at [https://github.com/Hedgehog-Computing/hedgehog-lab/tree/dev/hedgehog-libs/math](https://github.com/Hedgehog-Computing/hedgehog-lab/tree/dev/hedgehog-libs/math) 3 | 4 | ------- 5 | 6 | ## math 7 | 8 | The official Math library for [**Hedgehog Lab**](https://github.com/Hedgehog-Computing/hedgehog-lab), maintained by [**Hedgehog Computing**](https://github.com/Hedgehog-Computing). 9 | 10 | ### Quick Start 11 | 12 | 1. Open Hedgehog Lab at [https://hhlab.dev/](https://hhlab.dev/) 13 | 2. import math library and call the function 14 | ```js 15 | 16 | // Import the function 17 | *import math:inv 18 | 19 | // Initialize 2D array A 20 | let A = [[1,2],[4,5]] 21 | 22 | // Print the inverse of 2D array A 23 | print(inv(A)) 24 | ``` 25 | 26 | and you will see the result on the right: 27 | ``` 28 | [[-1.6666666666666667,0.6666666666666666], 29 | [1.3333333333333333,-0.3333333333333333]] 30 | ``` 31 | 32 | ### Documentation 33 | 34 | Document of functions to be implemented: (updated upon pulls and merges) https://github.com/Hedgehog-Computing/math/issues/6 35 | 36 | Document of example function for how to write, say, a mathjs wrapper function: https://github.com/Hedgehog-Computing/math/issues/16 37 | 38 | Document of styling guide, a brief description of a few guidelines we'd like followed for contributions: 39 | https://github.com/Hedgehog-Computing/Hedgehog-Script-Style-Guide 40 | 41 | Thank you! 42 | 43 | ### Community 44 | 45 | [**Discord Server**](https://discord.gg/hGhsanhJaK) 46 | 47 | [**Twitter**](https://twitter.com/HedgehogLabHQ) 48 | -------------------------------------------------------------------------------- /all.hhs: -------------------------------------------------------------------------------- 1 | /** 2 | * @author Jason Reynolds 3 | * @param 4 | * @returns 5 | * 6 | * Function for importing all math routines/functions, done by calling star import math:all 7 | */ 8 | 9 | *import math:cholesky 10 | *import math:det 11 | //import math:diagn --> base 12 | *import math:dot_divide 13 | *import math:dot_multiply 14 | *import math:factorial 15 | *import math:flatten 16 | *import math:inv 17 | *import math:is_number 18 | *import math:is_symmetric 19 | *import math:is_tril 20 | *import math:is_triu 21 | *import math:lsolve 22 | *import math:lu 23 | *import math:lusolve 24 | *import math:max 25 | *import math:min 26 | *import math:ndim 27 | //import math:ones --> base 28 | //import math:power --> base 29 | //import math:floor UNCOMMENT WHEN REMOVED FROM BASE 30 | //import math:ceil UNCOMMENT WHEN REMOVED FROM BASE 31 | *import math:QR 32 | *import math:shape 33 | *import math:sum 34 | *import math:test 35 | *import math:trace 36 | *import math:transpose 37 | *import math:usolve 38 | *import math:mink 39 | 40 | *import math:gcd 41 | *import math:mean 42 | *import math:median 43 | *import math:multinomial 44 | *import math:reshape 45 | *import math:is_diag 46 | *import math:magic 47 | 48 | *import math:check_input 49 | *import math:bounds 50 | *import math:iqr 51 | *import math:quantile 52 | *import math:percentile 53 | *import math:rms 54 | *import math:correcoef 55 | *import math:cov 56 | *import math:quadratic_equation 57 | *import math:cubic_equation 58 | *import math:quartic_equation 59 | *import math:vander 60 | *import math:resize 61 | *import math:normalize 62 | *import math:is_local_min 63 | *import math:is_local_max 64 | *import math:smooth 65 | *import math:any_missing 66 | *import math:is_missing 67 | *import math:rmmissing 68 | *import math:fillmissing 69 | *import math:missing 70 | *import math:hankel 71 | *import math:standard_missing 72 | *import math:toeplitz 73 | *import math:pascal 74 | 75 | -------------------------------------------------------------------------------- /any_missing.hhs: -------------------------------------------------------------------------------- 1 | /** 2 | * @author Jianan Lin (林家南) 3 | * @param input - an array, matrix, tensor that you want to determine missing 4 | * @returns - true if missing, false else 5 | * A missing element is NaN, null, '', or undefined. Notice [] and {} are not missing. 6 | * 7 | */ 8 | 9 | 10 | function any_missing(input) { 11 | 12 | *import math: ndim 13 | *import math: deep_copy 14 | 15 | // argument check 16 | if (arguments.length === 0) { 17 | throw new Error('Exception occurred in any_missing - no argument given'); 18 | } 19 | 20 | if (arguments.length > 1) { 21 | throw new Error('Exception occurred in any_missing - wrong argument number'); 22 | } 23 | 24 | if (!(Array.isArray(input)) && !(input instanceof Mat) && !(input instanceof Tensor)) { 25 | throw new Error('Exception occurred in any_missing - input must be an array, matrix or tensor'); 26 | } 27 | 28 | let in_type = input instanceof Mat || input instanceof Tensor; 29 | let raw_in = in_type ? input.clone().val : deep_copy(input); 30 | return anymissing_helper(raw_in); 31 | 32 | function anymissing_helper(raw_in) { 33 | if (ndim(raw_in) === 1) { 34 | for (let i = 0; i < raw_in.length; i++) { 35 | if (raw_in[i] || raw_in[i] === 0) { 36 | continue; 37 | } 38 | else { 39 | return true; 40 | } 41 | } 42 | return false; 43 | } 44 | 45 | else { 46 | for (let i = 0; i < raw_in.length; i++) { 47 | if (anymissing_helper(raw_in[i]) === false) { 48 | continue; 49 | } 50 | else { 51 | return true; 52 | } 53 | } 54 | return false; 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /any_missing_test.hhs: -------------------------------------------------------------------------------- 1 | /** 2 | * @author Jianan Lin 3 | * Unit test for anymissing 4 | * 5 | */ 6 | 7 | 8 | function any_missing_test() { 9 | 10 | *import math: any_missing 11 | 12 | let a = [NaN, 1, 0, 0, 1]; 13 | let b = mat([a, a]); 14 | let c = new Tensor([[a, a], [a, a]]); 15 | 16 | if (!(any_missing(a) === true)) { 17 | throw new Error('Unit test failed for any_missing for array'); 18 | } 19 | 20 | if (!(any_missing(b) === true)) { 21 | throw new Error('Unit test failed for any_missing for matrix'); 22 | } 23 | 24 | if (!(any_missing(c) === true)) { 25 | throw new Error('Unit test failed for any_missing for tensor'); 26 | } 27 | 28 | //print('any_missing test pass'); 29 | } 30 | -------------------------------------------------------------------------------- /bounds.hhs: -------------------------------------------------------------------------------- 1 | /** 2 | * @Author Jianan Lin (林家南) 3 | * @param input - a series of number, or an array, matrix, tensor to get the min and max 4 | * @param dim - 0 (bound of all), 1 (bound of column), 2(bound of row) 5 | * @returns - 1-d array or 2-d matrix [min, max], [[min1, min2], [max1, max2]], [[min1, max1], [min2, max2]] 6 | * 7 | */ 8 | 9 | 10 | function bounds(input, dim = 0) { 11 | 12 | *import math: ndim 13 | *import math: deep_copy 14 | *import math: flatten 15 | 16 | if (arguments.length === 0) { 17 | throw new Error('Exception occurred in bounds - no argument given'); 18 | } 19 | 20 | // bounds(1, 2, 3, 4, 5, 6) 21 | if (typeof arguments[0] === 'number') { 22 | let result = [arguments[0], arguments[0]]; 23 | 24 | for (let i = 1; i < arguments.length; i++) { 25 | if (typeof arguments[i] === 'number') { 26 | if (arguments[i] > result[1]) { 27 | result[1] = arguments[i]; 28 | } 29 | else if (arguments[i] < result[0]) { 30 | result[0] = arguments[i]; 31 | } 32 | } 33 | 34 | else { 35 | throw new Error('Exception occurred in bounds - argument must be number'); 36 | } 37 | } 38 | return result; 39 | } 40 | 41 | // bounds([1, 2, 3], 0) 42 | if (arguments.length > 2) { 43 | throw new Error('Exception occurred in bounds - wrong argument number'); 44 | } 45 | 46 | if (!(Array.isArray(input)) && !(input instanceof Mat) && !(input instanceof Tensor)) { 47 | throw new Error('Exception occurred in bounds - input must be an array, matrix or tensor'); 48 | } 49 | 50 | if (dim !== 0 && dim !== 1 && dim !== 2) { 51 | throw new Error('Exception occurred in bounds - dim must be 0, 1 or 2'); 52 | } 53 | 54 | let in_type = (input instanceof Mat) || (input instanceof Tensor); 55 | let raw_in = in_type ? input.clone().val : deep_copy(input); 56 | 57 | // find bounds of all 58 | if (dim === 0) { 59 | raw_in = flatten(raw_in); 60 | if (raw_in.length === 0) { 61 | return [0, 0]; 62 | } 63 | 64 | let result = [raw_in[0], raw_in[0]]; 65 | for (let i = 1; i < raw_in.length; i++) { 66 | if (raw_in[i] > result[1]) { 67 | result[1] = raw_in[i]; 68 | } 69 | else if (raw_in[i] < result[0]) { 70 | result[0] = raw_in[i]; 71 | } 72 | } 73 | return result; 74 | } 75 | 76 | // dim = 1, get the bound of each column 77 | else if (dim === 1) { 78 | if (ndim(raw_in) !== 2) { 79 | throw new Error('Exception occurred in bounds - input must be 2-dimensional'); 80 | } 81 | 82 | let m = raw_in.length; 83 | let n = raw_in[0].length; 84 | 85 | if (m * n === 0) { 86 | throw new Error('Exception occurred in bounds - wrong size of input'); 87 | } 88 | // raw_in = [[1, 2, 3, 4, 5, 6]] 89 | else if (m === 1) { 90 | return mat([raw_in[0], raw_in[0]]); 91 | } 92 | 93 | let result = []; 94 | let min_result = []; 95 | let max_result = []; 96 | 97 | for (let j = 0; j < n; j++) { 98 | let min_temp = raw_in[0][j]; 99 | let max_temp = raw_in[0][j]; 100 | 101 | for (let i = 1; i < m; i++) { 102 | if (raw_in[i][j] > max_temp) { 103 | max_temp = raw_in[i][j]; 104 | } 105 | else if (raw_in[i][j] < min_temp) { 106 | min_temp = raw_in[i][j]; 107 | } 108 | } 109 | min_result.push(min_temp); 110 | max_result.push(max_temp); 111 | } 112 | result = [min_result, max_result]; 113 | return mat(result); 114 | } 115 | 116 | // dim = 2, get the bound of each row 117 | else { 118 | if (ndim(raw_in) !== 2) { 119 | throw new Error('Exception occurred in bounds - input must be 2-dimensional'); 120 | } 121 | 122 | let m = raw_in.length; 123 | let n = raw_in[0].length; 124 | 125 | if (m * n === 0) { 126 | throw new Error('Exception occurred in bounds - wrong size of input'); 127 | } 128 | 129 | let result = []; 130 | for (let i = 0; i < m; i++) { 131 | let min_temp = raw_in[i][0]; 132 | let max_temp = raw_in[i][0]; 133 | 134 | for (let j = 1; j < n; j++) { 135 | if (raw_in[i][j] > max_temp) { 136 | max_temp = raw_in[i][j]; 137 | } 138 | else if (raw_in[i][j] < min_temp) { 139 | min_temp = raw_in[i][j]; 140 | } 141 | } 142 | result.push([min_temp, max_temp]); 143 | } 144 | return mat(result); 145 | } 146 | } 147 | -------------------------------------------------------------------------------- /bounds_test.hhs: -------------------------------------------------------------------------------- 1 | /** 2 | * @author Jianan Lin 3 | * Unit test for bounds function 4 | */ 5 | 6 | function bounds_test() { 7 | *import math: bounds 8 | 9 | if (!(bounds(1, 2, 3, 4, 5) === [1, 5])) { 10 | throw new Error('Unit test failed for bounds for numbers'); 11 | } 12 | 13 | let a = [1, 2, 3, 4, 5]; 14 | if (!(bounds(a, 0) === [1, 5])) { 15 | throw new Error('Unit test failed for bounds for array'); 16 | } 17 | 18 | let b = mat([[1, 2, 3], [4, 5, 6]]); 19 | if (!(bounds(b, 0) === [1, 6])) { 20 | throw new Error('Unit test failed for bounds for matrix'); 21 | } 22 | if (!(bounds(b, 1) === mat([[1, 2, 3], [4, 5, 6]]))) { 23 | throw new Error('Unit test failed for bounds for matrix'); 24 | } 25 | if (!(bounds(b, 2) === mat([[1, 3], [4, 6]]))) { 26 | throw new Error('Unit test failed for bounds for matrix'); 27 | } 28 | 29 | //print('Bounds test pass'); 30 | } 31 | -------------------------------------------------------------------------------- /ceil.hhs: -------------------------------------------------------------------------------- 1 | /** 2 | * @author Blake Wang 3 | * @params input - The structure with elements to be rounded toward positive infinity. Can be a number, 1d array, 2d array, Mat, Tensor. 4 | * @returns a number rounded to positive infinity if input is a number, a Mat with all elements rounded to positive infinity if input is a 1d array, 2d array, Mat or Tensor. 5 | * 6 | * 7 | * 8 | */ 9 | 10 | 11 | 12 | function ceil(input) { 13 | 14 | *import math: is_number 15 | 16 | // arguments length 17 | if (arguments.length === 0) { 18 | throw new Error('Exception occurred in ceil - no argument given'); 19 | } 20 | else if (arguments.length > 1) { 21 | throw new Error('Exception occurred in ceil - wrong arguments number'); 22 | } 23 | 24 | // type check 25 | if (!is_number(input) && !(Array.isArray(input)) && !(input instanceof Mat) && !(input instanceof Tensor)) { 26 | throw new Error('Exception occurred in ceil - input is not a number, Mat, Tensor or JS Array'); 27 | } 28 | 29 | // if input is a Mat, Tensor or 2d array 30 | let in_type = ((input instanceof Mat || input instanceof Tensor)) 31 | if (in_type || (input instanceof Array && input[0] instanceof Array)) { 32 | // matrix & tensor to js array 33 | if (in_type) { 34 | input = input.val; 35 | } 36 | // loop through all elements to round element toward negative infinity 37 | for (let i = 0; i < input.length; i++) { 38 | for (let j = 0; j < input[i].length; j++) { 39 | if (is_number(input[i][j])) { 40 | if (input[i][j] >= 0) { 41 | input[i][j] = (input[i][j] % 1 === 0 ? parseInt(input[i][j]) : parseInt(input[i][j]) + 1); 42 | } else { 43 | input[i][j] = parseInt(input[i][j]); 44 | } 45 | } else { 46 | input[i][j] = NaN; 47 | } 48 | } 49 | } 50 | return mat(input); 51 | } 52 | 53 | // when input is a 1d array 54 | if (input instanceof Array && !Array.isArray(input[0])) { 55 | for (let i = 0; i < input.length; i++) { 56 | if (is_number(input[i])) { 57 | if (input[i] >= 0) { 58 | input[i] = (input[i] % 1 === 0 ? parseInt(input[i]) : parseInt(input[i]) + 1); 59 | } else { 60 | input[i] = parseInt(input[i]); 61 | } 62 | } else { 63 | input[i] = NaN; 64 | } 65 | } 66 | return mat(input); 67 | } 68 | 69 | // when input is a number 70 | if (is_number(input)) { 71 | if (input >= 0) { 72 | return (input % 1 === 0 ? parseInt(input) : parseInt(input) + 1); 73 | } else { 74 | return parseInt(input); 75 | } 76 | } else { 77 | return NaN; 78 | } 79 | } 80 | 81 | 82 | -------------------------------------------------------------------------------- /ceil_test.hhs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | function ceil_test() { 5 | 6 | *import math: ceil 7 | 8 | // test scenario 1: integer test 9 | // testset setup 10 | let testSet1 = [1, -1, 0, -0, 1.1, -1.1, 1.0, -1.0]; 11 | let expectedVal1 = [1, -1, 0, -0, 2, -1, 1, -1]; 12 | // test process 13 | for (let i = 0; i < testSet1.length; i++) { 14 | if (floor(testSet1[i]) !== expectedVal1[i]) { 15 | throw new Error("ceil integer test has failed on testcase: " + testSet1[i] + 16 | "\noutput " + floor(testSet1[i]) + " while " + expectedVal1[i] + " is expected."); 17 | } 18 | } 19 | 20 | // test scenario 2: 1d array test 21 | // testset setup 22 | let testSet2 = [1, -1, 0, -0, 1.1, -1.1, 1.0, -1.0]; 23 | let expectedVal2 = mat([[1, -1, 0, -0, 2, -1, 1, -1]]); 24 | // test process 25 | if (!(floor(testSet2) === expectedVal2)) { 26 | throw new Error("ceil 1d array test has failed"); 27 | } 28 | 29 | // test scenario 3: 2d array test 30 | // testset setup 31 | let testSet3 = [[1, -1, 0, -0], [1.1, -1.1, 1.0, -1.0]]; 32 | let expectedVal3 = mat([[1, -1, 0, -0], [2, -1, 1, -1]]); 33 | // test process 34 | if (!(floor(testSet3) === expectedVal3)) { 35 | throw new Error("ceil 2d array test has failed"); 36 | } 37 | 38 | // test scenario 4: Mat test 39 | // testset setup 40 | let testSet4 = mat([[1, -1, 0, -0], [1.1, -1.1, 1.0, -1.0]]); 41 | let expectedVal4 = mat([[1, -1, 0, -0], [2, -1, 1, -1]]); 42 | // test process 43 | if (!(floor(testSet4) === expectedVal4)) { 44 | throw new Error("ceil Mat test has failed"); 45 | } 46 | 47 | } -------------------------------------------------------------------------------- /check_input.hhs: -------------------------------------------------------------------------------- 1 | /** 2 | * @author Jing Stone 3 | * @param input input must be a JS array 4 | * @return true if input is 2d matrix of 1d list 5 | * check the input array if it is a matrix or a vector(1d list) 6 | * 7 | */ 8 | 9 | function check_input(input) 10 | { 11 | if(!Array.isArray(input)) return false; 12 | 13 | let inputLength = input.length; 14 | // empty array [] 15 | if(inputLength === 0) return true; 16 | // check if it is a vector 17 | if(typeof input[0] === 'number') 18 | { 19 | for(let i = 0; i < inputLength; i++) 20 | { 21 | if(!(typeof input[i] === 'number')) 22 | { 23 | return false; 24 | } 25 | } 26 | } 27 | // check if it is a matrix 28 | else 29 | { 30 | let col = input.length; 31 | let row = input[0].length; 32 | // not allowed [[]] 33 | if(row === 0) return false; 34 | // a matrix should have the same length of column and row 35 | // while the column can not equal to row 36 | for(let i = 0; i < col; i++) 37 | { 38 | if(!Array.isArray(input[i])) return false; 39 | if(!input[i].length === row) return false; 40 | for(let j = 0; j < row; j++) 41 | { 42 | if(!(typeof input[i][j] === 'number')) 43 | { 44 | return false; 45 | } 46 | } 47 | } 48 | } 49 | return true; 50 | } -------------------------------------------------------------------------------- /check_input_test.hhs: -------------------------------------------------------------------------------- 1 | /** 2 | * @author Jing Stone 3 | * Unit test for check_input function 4 | */ 5 | 6 | function check_input_test() 7 | { 8 | *import math: check_input 9 | let A1 = []; 10 | let A2 = [1]; 11 | let A3 = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; 12 | let A4 = [[1], [2], [3], [4]]; 13 | let A5 = [[1], []]; 14 | let A6 = [[]]; 15 | let A7 = [1, [2], 3, 4]; 16 | let A8 = [[1, 2], [2, 3], [3, 4], [[2, 2], [2, 2]]]; 17 | let A9 = [[1, ], 4]; 18 | let A10 = [[1], [, 2], [2]]; 19 | let A11= [[1, 2, 3], [2, 3, 4], [4, 5, 6]]; 20 | let A12 = [[3, 4, 5], [4, 5, 6]]; 21 | 22 | function test(input, output) 23 | { 24 | if(!(check_input(input) === output)) 25 | { 26 | throw new Error('Unit test failed for check_input.') 27 | } 28 | } 29 | 30 | test(A1, true); 31 | test(A2, true); 32 | test(A3, true); 33 | test(A4, true); 34 | test(A5, false); 35 | test(A6, false); 36 | test(A7, false); 37 | test(A8, false); 38 | test(A9, false); 39 | test(A10, false); 40 | test(A11, true); 41 | test(A12, true); 42 | } -------------------------------------------------------------------------------- /cholesky.hhs: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * Function for decomposing a matrix via cholesky method, returning the L in the L*L^T part. 4 | * @author Xinran Wang, Jason Reynolds 5 | * @param inputMatrix the matrix to be decomposed into L 6 | * @returns the cholesky decomposition's L part 7 | */ 8 | 9 | 10 | 11 | 12 | function cholesky(inputMatrix) { 13 | 14 | *import math: transpose 15 | *import math: is_symmetric 16 | 17 | // argument number 18 | if (arguments.length === 0) { 19 | throw new Error('Exception occurred in cholesky - no argument given'); 20 | } 21 | else if (arguments.length > 1) { 22 | throw new Error('Exception occurred in cholesky - wrong argument number'); 23 | } 24 | 25 | // type check 26 | if (!(Array.isArray(inputMatrix)) && !(inputMatrix instanceof Mat) && !(inputMatrix instanceof Tensor)) { 27 | throw new Error('Exception occurred in cholesky - inputMatrix is not a Mat, Tensor or JS Array'); 28 | } 29 | 30 | //Cholesky: if the input is Mat, check rows/cols and symmetricity 31 | if (inputMatrix instanceof Mat) { 32 | L: Mat; 33 | if (!(inputMatrix.rows === inputMatrix.cols) || inputMatrix.rows === 0 || inputMatrix.cols === 0) { 34 | throw new Error('Wrong dimension for' + inputMatrix); 35 | } 36 | 37 | if (!(inputMatrix === transpose(inputMatrix))) { 38 | throw new Error('Matrix is not symmetric:' + inputMatrix) 39 | } 40 | 41 | if (!is_symmetric(inputMatrix)) { 42 | throw new Error('Not symmetric') 43 | } 44 | 45 | //dimension n 46 | const n = inputMatrix.rows; 47 | 48 | //matrix L 49 | const L = new Mat().zeros(n, n); 50 | 51 | //iteration 52 | for (let i = 0; i < n; i++) { 53 | for (let k = 0; k < i + 1; k++) { 54 | let sum = 0; 55 | for (let j = 0; j < k; j++) { 56 | sum += L.val[i][j] * L.val[k][j]; 57 | } 58 | 59 | if (i === k) { 60 | L.val[i][k] = Math.sqrt(inputMatrix.val[i][i] - sum); 61 | } else { 62 | L.val[i][k] = (1.0 / L.val[k][k]) * (inputMatrix.val[i][k] - sum); 63 | } 64 | } 65 | } 66 | return L; 67 | } 68 | 69 | //in the case the array is raw 70 | //consider the tensor 71 | if (inputMatrix instanceof Tensor) { 72 | inputMatrix = inputMatrix.val; 73 | } 74 | 75 | if (!(inputMatrix.length === inputMatrix[0].length) || inputMatrix.length === 0 || inputMatrix[0].length === 0) { 76 | throw new Error('Wrong dimensions for ' + inputMatrix); 77 | } 78 | if (!(is_symmetric(inputMatrix))) { 79 | throw new Error('Not symmetric'); 80 | } 81 | 82 | const n_2 = inputMatrix.length; 83 | const L_2 = zeros(n_2).val; 84 | //const L_2 = Array(n_2).fill(0).map(() => Array(n_2).fill(0)); 85 | 86 | for (let x = 0; x < n_2; x++) { 87 | for (let y = 0; y < x + 1; y++) { 88 | let sum2 = 0; 89 | for (let z = 0; z < y; z++) { 90 | sum2 += L_2[x][z] * L_2[y][z]; 91 | } 92 | 93 | if (x === y) { 94 | L_2[x][y] = Math.sqrt(inputMatrix[x][x] - sum2); 95 | } else { 96 | L_2[x][y] = (1.0 / L_2[y][y]) * (inputMatrix[x][y] - sum2); 97 | } 98 | } 99 | } 100 | return (mat(L_2)); 101 | 102 | } 103 | -------------------------------------------------------------------------------- /cholesky_test.hhs: -------------------------------------------------------------------------------- 1 | /** 2 | * @author Jason Reynolds 3 | * @param - none 4 | * @returns - none 5 | * 6 | * Simple unit test for cholesky. Tests both JS arrays and Mat objects. 7 | */ 8 | 9 | 10 | function cholesky_test() { 11 | 12 | *import math: cholesky 13 | 14 | //see if the cholesky value returns the correct results for a raw array 15 | if ((!(cholesky([[2, 1], [1, 2]]) === [[1.414, 0], [0.707, 1.225]]))) { 16 | throw new Error('Cholesky unit test has failed for raw array.'); 17 | } 18 | //check for mat object 19 | if ((!(cholesky(new Mat([[2, 1], [1, 2]])) === [[1.414, 0], [0.707, 1.225]]))) { 20 | throw new Error('Cholesky unit test has failed for Mat object.'); 21 | } 22 | // already throws an error within cholesky.hhs? 23 | /*if (cholesky([2,1],[0,0])... is not null?) { 24 | throw new Error('Cholesky unit test failed for symmetric check.'); 25 | }*/ 26 | } -------------------------------------------------------------------------------- /correcoef.hhs: -------------------------------------------------------------------------------- 1 | /** 2 | * @Author Jianan Lin (林家南) 3 | * @param A an array / matrix / tensor 4 | * @param B an array / matrix / tensor 5 | * @returns - a number = correlation coefficient of A and B 6 | * up to now only support one dimension correlatioin coefficient 7 | * need to add support for matrix calculation after some work 8 | * 9 | */ 10 | 11 | 12 | function correcoef(A, B) { 13 | 14 | *import math: ndim 15 | *import math: deep_copy 16 | *import math: flatten 17 | 18 | if (arguments.length === 0) { 19 | throw new Error('Exception occurred in correcoef - no argument given'); 20 | } 21 | 22 | if (arguments.length > 2) { 23 | throw new Error('Exception occurred in correcoef - no argument given'); 24 | } 25 | 26 | if (!(Array.isArray(A)) && !(A instanceof Mat) && !(A instanceof Tensor)) { 27 | throw new Error('Exception occurred in correcoef - A must be an array, matrix or tensor'); 28 | } 29 | 30 | if (!(Array.isArray(B)) && !(B instanceof Mat) && !(B instanceof Tensor)) { 31 | throw new Error('Exception occurred in correcoef - A must be an array, matrix or tensor'); 32 | } 33 | 34 | let in_type_A = (A instanceof Mat) || (A instanceof Tensor); 35 | let raw_in_A = in_type_A ? A.clone().val : deep_copy(A); 36 | if (ndim(raw_in_A) > 1) { 37 | raw_in_A = flatten(raw_in_A); 38 | } 39 | if (raw_in_A.length === 0) { 40 | throw new Error('Exception occurred in correcoef - A cannot be empty'); 41 | // return[0] 42 | } 43 | 44 | let in_type_B = (B instanceof Mat) || (B instanceof Tensor); 45 | let raw_in_B = in_type_B ? B.clone().val : deep_copy(B); 46 | if (ndim(raw_in_B) > 1) { 47 | raw_in_B = flatten(raw_in_B); 48 | } 49 | if (raw_in_B.length === 0) { 50 | throw new Error('Exception occurred in correcoef - B cannot be empty'); 51 | // return[0] 52 | } 53 | 54 | if (raw_in_A.length !== raw_in_B.length) { 55 | throw new Error('Exception occurred in correcoef - A and B must have the same length'); 56 | } 57 | 58 | let sumA = 0; 59 | for (let i = 0; i < raw_in_A.length; i++) { 60 | sumA = sumA + raw_in_A[i]; 61 | } 62 | let meanA = sumA / raw_in_A.length; 63 | 64 | let sumB = 0; 65 | for (let i = 0; i < raw_in_B.length; i++) { 66 | sumB = sumB + raw_in_B[i]; 67 | } 68 | let meanB = sumA / raw_in_B.length; 69 | 70 | let result1 = 0, result2 = 0, result3 = 0; 71 | for (let i = 0; i < raw_in_A.length; i++) { 72 | result1 = result1 + (raw_in_A[i] - meanA) * (raw_in_B[i] - meanB); 73 | result2 = result2 + (raw_in_A[i] - meanA) * (raw_in_A[i] - meanA); 74 | result3 = result3 + (raw_in_B[i] - meanB) * (raw_in_B[i] - meanB); 75 | } 76 | result2 = mathjs.sqrt(result2); 77 | result3 = mathjs.sqrt(result3); 78 | return result1 / result2 / result3; 79 | } 80 | -------------------------------------------------------------------------------- /correcoef_test.hhs: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author Jianan Lin 3 | * test for correcoef function 4 | */ 5 | 6 | 7 | function correcoef_test() { 8 | 9 | *import math: correcoef 10 | 11 | let a = [0, 1, 2, 3]; 12 | 13 | if (!(correcoef(a, a) === 1)) { 14 | throw new Error('Unit test failed for correcoef for number'); 15 | } 16 | 17 | //print('correcoef test pass'); 18 | } 19 | -------------------------------------------------------------------------------- /cov.hhs: -------------------------------------------------------------------------------- 1 | /** 2 | * @Author Jianan Lin (林家南) 3 | * @param A an array / matrix / tensor 4 | * @param B an array / matrix / tensor 5 | * @returns - a number = covariance of A and B 6 | * up to now only support one dimension covariance 7 | * need to add support for matrix calculation after some work 8 | * 9 | */ 10 | 11 | 12 | function cov(A, B) { 13 | 14 | *import math: ndim 15 | *import math: deep_copy 16 | *import math: flatten 17 | 18 | if (arguments.length === 0) { 19 | throw new Error('Exception occurred in cov - no argument given'); 20 | } 21 | 22 | if (arguments.length > 2) { 23 | throw new Error('Exception occurred in cov - no argument given'); 24 | } 25 | 26 | if (!(Array.isArray(A)) && !(A instanceof Mat) && !(A instanceof Tensor)) { 27 | throw new Error('Exception occurred in cov - A must be an array, matrix or tensor'); 28 | } 29 | 30 | if (!(Array.isArray(B)) && !(B instanceof Mat) && !(B instanceof Tensor)) { 31 | throw new Error('Exception occurred in cov - A must be an array, matrix or tensor'); 32 | } 33 | 34 | let in_type_A = (A instanceof Mat) || (A instanceof Tensor); 35 | let raw_in_A = in_type_A ? A.clone().val : deep_copy(A); 36 | if (ndim(raw_in_A) > 1) { 37 | raw_in_A = flatten(raw_in_A); 38 | } 39 | if (raw_in_A.length === 0) { 40 | throw new Error('Exception occurred in cov - A cannot be empty'); 41 | // return[0] 42 | } 43 | 44 | let in_type_B = (B instanceof Mat) || (B instanceof Tensor); 45 | let raw_in_B = in_type_B ? B.clone().val : deep_copy(B); 46 | if (ndim(raw_in_B) > 1) { 47 | raw_in_B = flatten(raw_in_B); 48 | } 49 | if (raw_in_B.length === 0) { 50 | throw new Error('Exception occurred in cov - B cannot be empty'); 51 | // return[0] 52 | } 53 | 54 | if (raw_in_A.length !== raw_in_B.length) { 55 | throw new Error('Exception occurred in cov - A and B must have the same length'); 56 | } 57 | 58 | if (raw_in_A.length === 1) { 59 | return 0; 60 | } 61 | 62 | let sumA = 0; 63 | for (let i = 0; i < raw_in_A.length; i++) { 64 | sumA = sumA + raw_in_A[i]; 65 | } 66 | let meanA = sumA / raw_in_A.length; 67 | 68 | let sumB = 0; 69 | for (let i = 0; i < raw_in_B.length; i++) { 70 | sumB = sumB + raw_in_B[i]; 71 | } 72 | let meanB = sumA / raw_in_B.length; 73 | 74 | let result1 = 0; 75 | for (let i = 0; i < raw_in_A.length; i++) { 76 | result1 = result1 + (raw_in_A[i] - meanA) * (raw_in_B[i] - meanB); 77 | } 78 | return result1 / (raw_in_A.length - 1); 79 | } 80 | -------------------------------------------------------------------------------- /cov_test.hhs: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author Jianan Lin 3 | * test for cov function 4 | */ 5 | 6 | 7 | function cov_test() { 8 | 9 | *import math: cov 10 | 11 | let a = [0, 1, 2]; 12 | 13 | if (!(cov(a, a) === 1)) { 14 | throw new Error('Unit test failed for correcoef for number'); 15 | } 16 | 17 | //print('cov test pass'); 18 | } 19 | -------------------------------------------------------------------------------- /cubic_equation.hhs: -------------------------------------------------------------------------------- 1 | /** 2 | * @Author Jianan Lin (林家南) 3 | * @param a, b, c, d - ax^3 + bx^2 + cx + d = 0 4 | * @returns - solution in 2-element array, including compelx solution 5 | * Theory comes from: https://jerkwin.github.io/2012/10/30/%E4%B8%80%E5%85%83%E4%B8%89%E6%AC%A1%E6%96%B9%E7%A8%8B%E6%B1%82%E6%A0%B9%E5%85%AC%E5%BC%8F%E5%8F%8A%E5%85%B6Fortran%E4%BB%A3%E7%A0%81/ 6 | * 7 | */ 8 | 9 | 10 | 11 | function cubic_equation(a, b = 0, c = 0, d = 0) { 12 | 13 | if (arguments.length === 0) { 14 | throw new Error('Exception occurred in cubic_equation - no argument given'); 15 | } 16 | if (arguments.length > 4) { 17 | throw new Error('Exception occurred in cubic_equation - wrong argument number'); 18 | } 19 | 20 | if (!(typeof a === 'number') || !(typeof b === 'number') || !(typeof c === 'number') || !(typeof d === 'number')) { 21 | throw new Error('Exception occurred in cubic_equation - a, b, c, d must be numbers'); 22 | } 23 | 24 | // we do not allowed a = 0, different from quadratic equation 25 | if (a === 0) { 26 | throw new Error('Exception occurred in cubic_equation - a cannot be 0'); 27 | } 28 | 29 | b = b / a / 3; 30 | c = c / a / 6; 31 | d = d / a / 2; 32 | a = 1; 33 | 34 | let alpha = -mathjs.pow(b, 3) + 3 * b * c - d; 35 | let beta = mathjs.pow(b, 2) - 2 * c; 36 | let delta = mathjs.pow(alpha, 2) - mathjs.pow(beta, 3); 37 | 38 | // one real root and two complex root 39 | if (delta > 0.0000000001) { 40 | let r1 = mathjs.cbrt(alpha + mathjs.sqrt(delta)); 41 | let r2 = beta / r1; 42 | let x1 = -b + r1 + r2; 43 | let temp_real = -b - (r1 + r2) / 2; 44 | let temp_imag = mathjs.sqrt(3) / 2 * (r1 - r2); 45 | temp_real = temp_real.toFixed(5); 46 | temp_imag = temp_imag.toFixed(5); 47 | x1 = x1.toFixed(5); 48 | return [mathjs.complex(x1, 0), mathjs.complex(temp_real, -temp_imag), mathjs.complex(temp_real, temp_imag)]; 49 | } 50 | 51 | // three real roots 52 | else if (delta < -0.0000000001) { 53 | let theta = mathj.acos(alpha / mathjs.cbrt(mathjs.pow(beta, 2))); 54 | let x1 = -b + 2 * mathjs.sqrt(beta) * mathjs.cos(theta / 3); 55 | let x2 = -b + 2 * mathjs.sqrt(beta) * mathjs.cos((theta + 2 * math.pi) / 3); 56 | let x3 = -b + 2 * mathjs.sqrt(beta) * mathjs.cos((theta - 2 * math.pi) / 3); 57 | x1 = x1.toFixed(5); 58 | x2 = x2.toFixed(5); 59 | x3 = x3.toFixed(5); 60 | return [mathjs.complex(x1, 0), mathjs.complex(x2, 0), mathjs.complex(x3, 0)]; 61 | } 62 | 63 | else { 64 | let r = mathjs.cbrt(alpha); 65 | r = r.toFixed(5); 66 | b = b.toFixed(5); 67 | // one real root, two double real root 68 | if (r > 0.0000000001 || r < -0.0000000001) { 69 | return [mathjs.complex(-b + 2 * r, 0), mathjs.complex(-b - r, 0), mathjs.complex(-b - r, 0)]; 70 | } 71 | // one triple real root 72 | else { 73 | return [mathjs.complex(-b, 0), mathjs.complex(-b, 0), mathjs.complex(-b, 0)]; 74 | } 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /cubic_equation_test: -------------------------------------------------------------------------------- 1 | /** 2 | * @author Jianan Lin 3 | * Unit test for quadratic_equation function 4 | */ 5 | 6 | 7 | function cubic_equation_test() { 8 | 9 | *import math: cubic_equation 10 | 11 | // x^3 = 0 12 | if (!(cubic_equation(1) === [0, 0, 0])) { 13 | throw new Error('Unit test for cubic_equation failed'); 14 | } 15 | 16 | print("cubic_equation test pass"); 17 | 18 | } 19 | -------------------------------------------------------------------------------- /deep_copy.hhs: -------------------------------------------------------------------------------- 1 | /** 2 | * @author 3 | * @param input - the input to make a deep copy of 4 | * @returns - a new copy of the original input with a different reference 5 | * 6 | * This recursive function is made for cloning arrays in a deep manner since javascript doesn't have a way to do it. All common ways to copy is shallow and that 7 | * causes a lot of issues for functions that modify the input or a reference to the input 8 | */ 9 | 10 | function deep_copy(items) { 11 | 12 | *import math:is_number 13 | 14 | // wrong argument number 15 | if (arguments.length === 0) { 16 | throw new Error('Exception occurred in deep_copy - no argument given'); 17 | } 18 | else if (arguments.length > 1) { 19 | throw new Error('Exception occurred in deep_copy - wrong argument number'); 20 | } 21 | 22 | // type check 23 | if (!(Array.isArray(items)) && !(items instanceof Mat) && !(items instanceof Tensor) && !(is_number(items))) { 24 | throw new Error('Exception occurred in deep_copy - input is not a Mat, Tensor or JS Array'); 25 | } 26 | 27 | // for loop is faster, will replace with it later 28 | //below is causing items.map is not a function issue again? 29 | //return items.map(item => Array.isArray(item) ? deep_copy(item) : item); 30 | 31 | //support for numbers in case we're deep copying numbers in general cases 32 | if (is_number(items)) { 33 | return items; 34 | } 35 | 36 | else { 37 | 38 | return items.map(item => Array.isArray(item) ? deep_copy(item) : item); 39 | 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /det.hhs: -------------------------------------------------------------------------------- 1 | /** 2 | * @author Jason Reynolds 3 | * @param input the square matrix to take in and find the determinant of 4 | * @returns the determinant of the matrix, as a number 5 | * 6 | * functions finds determinant of a matrix, wrapper functions from mathjs 7 | */ 8 | 9 | 10 | 11 | function det(input) { 12 | 13 | *import math: ndim 14 | 15 | // wrong argument number 16 | if (arguments.length === 0) { 17 | throw new Error('Exception occurred in det - no argument given'); 18 | } 19 | else if (arguments.length > 1) { 20 | throw new Error('Exception occurred in det - wrong argument number'); 21 | } 22 | 23 | // type check 24 | if (!(Array.isArray(input)) && !(input instanceof Mat) && !(input instanceof Tensor)) { 25 | throw new Error('Exception occurred in det - input is not a Mat, Tensor or JS Array'); 26 | } 27 | 28 | // dimension check 29 | if (ndim(input) !== 2) { 30 | throw new Error('Exception occurred in det - input is not 2-dimensional'); 31 | } 32 | 33 | //set raw_in to be either a clone of Mat or input itself if JS array 34 | let in_type = (input instanceof Mat) || (input instanceof Tensor); 35 | let raw_in = (in_type) ? input.clone() : input; 36 | 37 | //in the case that it's an input of Mat 38 | if (input instanceof Mat) { 39 | 40 | //check the dimensions: are rows, cols 0? is it not square? if so, throw error 41 | if (input.rows === 0 || input.cols === 0 || !(input.rows === input.cols)) { 42 | throw new Error('Exception occurred in det - wrong dimensions for input'); 43 | } 44 | //since it's Mat, degrade to JS array 45 | raw_in = raw_in.val; 46 | //plug it into mathjs.det and return 47 | return mathjs.det(raw_in); 48 | 49 | } 50 | 51 | //in the case that it's a JS array or Tensor 52 | else { 53 | //again check the input. is any dim 0? is it not square? if so, throw error. 54 | if (in_type) { 55 | input = raw_in.val; 56 | } 57 | if (input.length === 0 || input[0].length === 0 || !(input.length === input[0].length)) { 58 | throw new Error('Exception occurred in det - wrong dimensions for input'); 59 | } 60 | //plug in input clone into mathjs.det and return. Technically since det doesn't modify you could use parameter 'input' 61 | return mathjs.det(input); 62 | } 63 | } 64 | 65 | -------------------------------------------------------------------------------- /det_test.hhs: -------------------------------------------------------------------------------- 1 | /** 2 | * @author Jason Reynolds 3 | * @param 4 | * @returns 5 | * 6 | * Function for testing determinant of matrix 7 | */ 8 | 9 | function det_test() { 10 | 11 | *import math: det 12 | 13 | if (!(det([[2, 5, 3], [7, 13, -1], [2, 3, 3]]) === -46)) { 14 | throw new Error('Failed unit test for raw array for det.'); 15 | } 16 | let a = new Mat([[2, 1], [1, 2]]); 17 | if (!(det(a) === 3)) { 18 | throw new Error('Failed unit test for Mat array for det.'); 19 | } 20 | } -------------------------------------------------------------------------------- /dot_divide.hhs: -------------------------------------------------------------------------------- 1 | /** 2 | * Function - dot_divide : divides two matrices element-wise, as in divides A[0][0] / B[0][0] = result[0][0], and so on.. 3 | * 4 | * @author Jason Reynolds 5 | * @param A - matrix to be the numerator of the division operation. Must be same size as B. 6 | * @param B - matrix to be the denominator of the division operation. Must be same size as A. 7 | * @returns a matrix of the same dimension with the element wise divided results 8 | * 9 | * This function takes two matrix like structures and divides them element-wise. The left hand side argument is the numerator, the right, denominator. 10 | */ 11 | 12 | 13 | function dot_divide(A, B) { 14 | 15 | *import math: deep_copy 16 | *import math: ndim 17 | *import math: is_number 18 | 19 | // wrong argument number 20 | if (arguments.length !== 2) { 21 | throw new Error('Exception occurred in dot_divide - wrong argument number'); 22 | } 23 | 24 | // type check 25 | if (!(Array.isArray(A)) && !(A instanceof Mat) && !(A instanceof Tensor)) { 26 | throw new Error('Exception occurred in dot_divide - argument[0] is not a Mat, Tensor or JS Array'); 27 | } 28 | if (!(Array.isArray(B)) && !(B instanceof Mat) && !(B instanceof Tensor)) { 29 | throw new Error('Exception occurred in dot_divide - argument[1] is not a Mat, Tensor or JS Array'); 30 | } 31 | 32 | //declare raw_in_a and raw_in_b to be either clones of respective inputs or deep copy inputs themselves depending on if Mat or not 33 | let in_type_A = (A instanceof Mat) || (A instanceof Tensor); 34 | let in_type_B = (B instanceof Mat) || (B instanceof Tensor); 35 | let raw_in_a = (in_type_A) ? A.clone().val : deep_copy(A); 36 | let raw_in_b = (in_type_B) ? B.clone().val : deep_copy(B); 37 | 38 | // in case a = [[1, 2, 3]] 39 | while (Array.isArray(raw_in_a) && raw_in_a.length === 1) { 40 | raw_in_a = raw_in_a[0]; 41 | } 42 | while (Array.isArray(raw_in_b) && raw_in_b.length === 1) { 43 | raw_in_b = raw_in_b[0]; 44 | } 45 | 46 | // dimension check in case A and B are both number 47 | if (is_number(raw_in_a) && is_number(raw_in_b)) { 48 | return mat(raw_in_a / raw_in_b); 49 | } 50 | 51 | if (ndim(raw_in_a) !== ndim(raw_in_b) || ndim(raw_in_a) < 1) { 52 | throw new Error('Exception occurred in dot_divide - A and B must have the same dimensions'); 53 | } 54 | 55 | let result = dot_divide_helper(raw_in_a, raw_in_b); 56 | let dim = ndim(result); 57 | 58 | if (dim <= 2) { 59 | return mat(result); 60 | } 61 | else { 62 | return new Tensor(result); 63 | } 64 | 65 | function dot_divide_helper(A, B) { 66 | let dimA = ndim(A), dimB = ndim(B); 67 | if (dimA !== dimB) { 68 | throw new Error('Exception occurred in dot_divide - A and B must have the same dimensions'); 69 | } 70 | 71 | if (dimA === 1) { 72 | if (A.length !== B.length) { 73 | throw new Error('Exception occurred in dot_divide - A and B must have the same lengths'); 74 | } 75 | 76 | let result = deep_copy(A); 77 | for (let i = 0; i < A.length; i++) { 78 | result[i] = A[i] / B[i]; 79 | } 80 | return result; 81 | } 82 | 83 | else { 84 | if (A.length !== B.length) { 85 | throw new Error('Exception occurred in dot_divide - A and B must have the same lengths'); 86 | } 87 | 88 | let result = []; 89 | for (let i = 0; i < A.length; i++) { 90 | let temp = dot_divide_helper(A[i], B[i]); 91 | result.push(temp); 92 | } 93 | return result; 94 | } 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /dot_divide_test.hhs: -------------------------------------------------------------------------------- 1 | /** 2 | * @author Jason Reynolds 3 | * @param 4 | * @returns 5 | * 6 | * Function for testing dot_divide.hhs 7 | */ 8 | 9 | 10 | 11 | function dot_divide_test() { 12 | 13 | *import math: dot_divide 14 | 15 | let a = [4, 6, 8]; 16 | let b = [2, 3, 4]; 17 | let c = dot_divide(a, b).val[0]; 18 | for (let i = 0; i < 3; i++) { 19 | if (c[i] !== 2) { 20 | throw new Error('Unit test failed for array for dot_divide'); 21 | } 22 | } 23 | 24 | let d = mat([[4, 6, 8], [4, 6, 8]]); 25 | let e = mat([[2, 3, 4], [2, 3, 4]]); 26 | let f = dot_divide(d, e).val; 27 | for (let i = 0; i < 2; i++) { 28 | for (let j = 0; j < 3; j++) { 29 | if (f[i][j] !== 2) { 30 | throw new Error('Unit test failed for matrix for dot_divide'); 31 | } 32 | } 33 | } 34 | 35 | let g = new Tensor([[[4, 6, 8], [4, 6, 8]], [[4, 6, 8], [4, 6, 8]]]); 36 | let h = new Tensor([[[2, 3, 4], [2, 3, 4]], [[2, 3, 4], [2, 3, 4]]]); 37 | let m = dot_divide(g, h).val; 38 | for (let i = 0; i < 2; i++) { 39 | for (let j = 0; j < 2; j++) { 40 | for (let k = 0; k < 3; k++) { 41 | if (m[i][j][k] !== 2) { 42 | throw new Error('Unit test failed for tensor for dot_divide'); 43 | } 44 | } 45 | } 46 | } 47 | 48 | //print('dot_divide test pass'); 49 | } 50 | -------------------------------------------------------------------------------- /dot_multiply.hhs: -------------------------------------------------------------------------------- 1 | /** 2 | * Function - dot_multiply : multiplies two matrices element-wise, as in multiplies A[0][0] * B[0][0] = result[0][0], and so on.. 3 | * 4 | * @author Jason Reynolds 5 | * @param A - left hand multiplicand matrix 6 | * @param B - right hand multiplicand matrix (they are reversible) 7 | * @returns a matrix of the same dimension with the element wise multiplied results 8 | * 9 | * This function multiplies 2 matrices element-wise. Since multiplication in the reals is commutative, A*B = B*A in this case. 10 | * 11 | */ 12 | 13 | 14 | 15 | function dot_multiply(A, B) { 16 | 17 | *import math: deep_copy 18 | *import math: ndim 19 | *import math: is_number 20 | 21 | // wrong argument number 22 | if (arguments.length !== 2) { 23 | throw new Error('Exception occurred in dot_multiply - wrong argument number'); 24 | } 25 | 26 | // type check 27 | if (!(Array.isArray(A)) && !(A instanceof Mat) && !(A instanceof Tensor)) { 28 | throw new Error('Exception occurred in dot_multiply - argument[0] is not a Mat, Tensor or JS Array'); 29 | } 30 | if (!(Array.isArray(B)) && !(B instanceof Mat) && !(B instanceof Tensor)) { 31 | throw new Error('Exception occurred in dot_multiply - argument[1] is not a Mat, Tensor or JS Array'); 32 | } 33 | 34 | //declare raw_in_a and raw_in_b to be either clones of respective inputs or deep copy inputs themselves depending on if Mat or not 35 | let in_type_A = (A instanceof Mat) || (A instanceof Tensor); 36 | let in_type_B = (B instanceof Mat) || (B instanceof Tensor); 37 | let raw_in_a = (in_type_A) ? A.clone().val : deep_copy(A); 38 | let raw_in_b = (in_type_B) ? B.clone().val : deep_copy(B); 39 | 40 | // in case a = [[1, 2, 3]] 41 | while (Array.isArray(raw_in_a) && raw_in_a.length === 1) { 42 | raw_in_a = raw_in_a[0]; 43 | } 44 | while (Array.isArray(raw_in_b) && raw_in_b.length === 1) { 45 | raw_in_b = raw_in_b[0]; 46 | } 47 | 48 | // dimension check in case A and B are both number 49 | if (is_number(raw_in_a) && is_number(raw_in_b)) { 50 | return mat(raw_in_a * raw_in_b); 51 | } 52 | 53 | if (ndim(raw_in_a) !== ndim(raw_in_b) || ndim(raw_in_a) < 1) { 54 | throw new Error('Exception occurred in dot_multiply - A and B must have the same dimensions'); 55 | } 56 | 57 | let result = dot_multiply_helper(raw_in_a, raw_in_b); 58 | let dim = ndim(result); 59 | 60 | if (dim <= 2) { 61 | return mat(result); 62 | } 63 | else { 64 | return new Tensor(result); 65 | } 66 | 67 | function dot_multiply_helper(A, B) { 68 | let dimA = ndim(A), dimB = ndim(B); 69 | if (dimA !== dimB) { 70 | throw new Error('Exception occurred in dot_multiply - A and B must have the same dimensions'); 71 | } 72 | 73 | if (dimA === 1) { 74 | if (A.length !== B.length) { 75 | throw new Error('Exception occurred in dot_multiply - A and B must have the same lengths'); 76 | } 77 | 78 | let result = deep_copy(A); 79 | for (let i = 0; i < A.length; i++) { 80 | result[i] = A[i] * B[i]; 81 | } 82 | return result; 83 | } 84 | 85 | else { 86 | if (A.length !== B.length) { 87 | throw new Error('Exception occurred in dot_multiply - A and B must have the same lengths'); 88 | } 89 | 90 | let result = []; 91 | for (let i = 0; i < A.length; i++) { 92 | let temp = dot_multiply_helper(A[i], B[i]); 93 | result.push(temp); 94 | } 95 | return result; 96 | } 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /dot_multiply_test.hhs: -------------------------------------------------------------------------------- 1 | /** 2 | * @author Jason Reynolds 3 | * @param 4 | * @returns 5 | * 6 | * Function for testing dot_divide.hhs 7 | */ 8 | 9 | 10 | function dot_multiply_test() { 11 | 12 | *import math: dot_multiply 13 | 14 | let a = [1, 3, 9]; 15 | let b = [9, 3, 1]; 16 | if (!(dot_multiply(a, b) === mat([9, 9, 9]))) { 17 | throw new Error('Unit test failed for array for dot_multiply'); 18 | } 19 | 20 | let c = mat([[1, 3, 9], [9, 3, 1]]); 21 | let d = mat([[9, 3, 1], [1, 3, 9]]); 22 | if (!(dot_multiply(d, c) === mat([[9, 9, 9], [9, 9, 9]]))) { 23 | throw new Error('Unit test failed for matrix for dot_multiply'); 24 | } 25 | 26 | let e = new Tensor([[[1, 3, 9], [9, 3, 1]], [[1, 3, 9], [9, 3, 1]]]); 27 | let f = new Tensor([[[9, 3, 1], [1, 3, 9]], [[9, 3, 1], [1, 3, 9]]]); 28 | let g = dot_multiply(e, f).val; 29 | if (!(g[0] === [[9, 9, 9], [9, 9, 9]]) || !(g[1] === [[9, 9, 9], [9, 9, 9]])) { 30 | throw new Error('Unit test failed for tensor for dot_multiply'); 31 | } 32 | 33 | //print('dot_multiply test pass') 34 | } 35 | -------------------------------------------------------------------------------- /factorial.hhs: -------------------------------------------------------------------------------- 1 | /** 2 | * @author Jason Reynolds 3 | * @param input - The number, array or Mat to find the factorial of 4 | * @returns - The same type as the input but with factorial operated on it. If it wasn't a number, factorial was applied in an element-wise fashion 5 | * 6 | * This function takes a number, array or Mat and finds the factorial of it, where the factorial is the element-wise factorial for matrix like structures 7 | * and the normal factorial for numbers. Recall factorial for a number is num! = (num)*(num-1)*(num-2)*...*2*1 . Ex: 5! = 5*4*3*2*1 = 120. 8 | */ 9 | 10 | 11 | 12 | 13 | function factorial(input) { 14 | 15 | *import math: is_number 16 | *import math: ndim 17 | *import math: deep_copy 18 | 19 | if (arguments.length === 0) { 20 | throw new Error('Exception occurred in factorial - no argument given'); 21 | } 22 | else if (arguments.length !== 1) { 23 | throw new Error('Exception occurred in factorial - wrong argument number'); 24 | } 25 | 26 | if(!(is_number(input)) && !(Array.isArray(input)) && !(input instanceof Mat) && !(input instanceof Tensor)) { 27 | throw new Error('Exception occurred in factorial - input must be a number, array, matrix or tensor'); 28 | } 29 | 30 | if (is_number(input)) { 31 | if (input < 0 || parseInt(input) !== input) { 32 | throw new Error('Exception occurred in factorial - input number must be a non-negative integer'); 33 | } 34 | return mathjs.factorial(input); 35 | } 36 | 37 | //declare raw_in to be a clone of Mat if input is Mat, or input itself otherwise 38 | let in_type = (input instanceof Mat || input instanceof Tensor) 39 | let raw_in = (in_type) ? input.clone().val : deep_copy(input); 40 | 41 | let result = factorial_helper(raw_in); 42 | let dim = ndim(result); 43 | if (dim === 1) { 44 | return result; 45 | } 46 | else if (dim === 2) { 47 | return mat(result); 48 | } 49 | else { 50 | return new Tensor(result); 51 | } 52 | 53 | function factorial_helper(input) { 54 | let dim = ndim(input); 55 | 56 | if (dim === 1) { 57 | let result = deep_copy(input); 58 | for (let i = 0; i < input.length; i++) { 59 | if (input[i] < 0 || input[i] !== parseInt(input[i])) { 60 | throw new Error('Exception occurred in factorial - input number must be a non-negative integer'); 61 | } 62 | result[i] = mathjs.factorial(input[i]); 63 | } 64 | return result; 65 | } 66 | 67 | else { 68 | let result = []; 69 | for (let i = 0; i < input.length; i++) { 70 | let temp = factorial_helper(input[i]); 71 | result.push(temp); 72 | } 73 | return result; 74 | } 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /factorial_test.hhs: -------------------------------------------------------------------------------- 1 | /** 2 | * @author Jason Reynolds 3 | * @param 4 | * @returns 5 | * 6 | * Function for testing factorial.hhs 7 | */ 8 | 9 | 10 | function factorial_test() { 11 | 12 | *import math: factorial 13 | 14 | let a = 5; 15 | if (factorial(a) !== 120) { 16 | throw new Error('Unit test failed for number for factorial'); 17 | } 18 | 19 | let b = [1, 2, 3]; 20 | if (!(factorial(b) === [1, 2, 6])) { 21 | throw new Error('Unit test failed for array for factorial'); 22 | } 23 | 24 | let c = mat([[1, 2], [3, 4]]); 25 | if (!(factorial(c) === mat([[1, 2], [6, 24]]))) { 26 | throw new Error('Unit test failed for matrix for factorial'); 27 | } 28 | 29 | let d = new Tensor([[[1, 2], [3, 4]], [[5, 6], [7, 8]]]); 30 | let e = factorial(d).val; 31 | if (!(e[0] === [[1, 2], [6, 24]]) || !(e[1] === [[120, 720], [5040, 40320]])) { 32 | throw new Error('Unit test failed for tensor for factorial'); 33 | } 34 | 35 | //print('factorial test pass'); 36 | } 37 | -------------------------------------------------------------------------------- /fillmissing_test.hhs: -------------------------------------------------------------------------------- 1 | /** 2 | * @author Jianan Lin 3 | * Unit test for fillmissing 4 | * 5 | */ 6 | 7 | 8 | 9 | function fillmissing_test() { 10 | 11 | *import math: fillmissing 12 | 13 | let a = [1, 2, NaN]; 14 | let b = fillmissing(a); 15 | 16 | if (!(b === mat([1, 2, 0]))) { 17 | throw new Error('Unit test failed for fillmissing_test'); 18 | } 19 | 20 | let c = [[1], [2], [NaN]]; 21 | let d = fillmissing(c, 5); 22 | if (!(d === mat([[1], [2], [5]]))) { 23 | throw new Error('Unit test failed for fillmissing_test'); 24 | } 25 | 26 | let e = mat([[NaN, NaN, NaN], [1, 2, 3]]); 27 | let f = fillmissing(e, [1, 2, 3]); 28 | if (!(f === mat([[1, 2, 3], [1, 2, 3]]))) { 29 | throw new Error('Unit test failed for fillmissing_test'); 30 | } 31 | 32 | let g = mat([[NaN, NaN, NaN], [1, 2, 3]]); 33 | let h = fillmissing(g, 1, 'previous'); 34 | if (!(h === mat([[1, 1, 1], [1, 2, 3]]))) { 35 | throw new Error('Unit test failed for fillmissing_test'); 36 | } 37 | 38 | let i = mat([[NaN, 2, NaN], [1, 2, 3]]); 39 | let j = fillmissing(i, [1, 3], 'previous'); 40 | if (!(j === mat([[1, 2, 2], [1, 2, 3]]))) { 41 | throw new Error('Unit test failed for fillmissing_test'); 42 | } 43 | 44 | let k = [NaN, NaN, NaN, NaN, NaN]; 45 | let l = [1, 2, 3, 4]; 46 | let m = fillmissing(k, l); 47 | if (!(m === mat([1, 2, 3, 4, 4]))) { 48 | throw new Error('Unit test failed for fillmissing_test'); 49 | } 50 | 51 | //print('fillmissing test pass'); 52 | } 53 | -------------------------------------------------------------------------------- /flatten.hhs: -------------------------------------------------------------------------------- 1 | /** 2 | * @author Jason Reynolds 3 | * @param input - The multidimensional (or not) array to flatten 4 | * @returns A 1d array that represents the input 'flattened'. Not returning Mat because Mat forces 2d 5 | * 6 | * Function wrapper mathjs for flattening a multi dim matrix into a 1d matrix/array 7 | * 8 | * 9 | */ 10 | 11 | 12 | 13 | function flatten(input) { 14 | 15 | *import math: ndim 16 | *import math: deep_copy 17 | *import math: is_number 18 | 19 | if (arguments.length === 0) { 20 | throw new Error('Exception occurred in flatten - no argument given'); 21 | } 22 | else if (arguments.length !== 1) { 23 | throw new Error('Exception occurred in flatten - wrong argument number'); 24 | } 25 | 26 | if (is_number(input)) { 27 | return [input]; 28 | } 29 | 30 | if (!(Array.isArray(input)) && !(input instanceof Mat) && !(input instanceof Tensor)) { 31 | throw new Error('Exception occurred in flatten - input must be a number, array, matrix or tensor'); 32 | } 33 | 34 | //declare raw_input to be a clone of input when input is a Mat or Tensor, otherwise input itself 35 | let in_type = (input instanceof Mat || input instanceof Tensor); 36 | let raw_input = (in_type) ? input.clone() : deep_copy(input); 37 | 38 | //when it is a clone, degrade it to vanilla array to use as argument for JS function 39 | if (input instanceof Mat || input instanceof Tensor) { 40 | raw_input = raw_input.val; 41 | //return the result of mathjs.flatten 42 | return mathjs.flatten(raw_input); 43 | } 44 | //when its a number... create an empty array, push input into it and return that array of length 1 45 | //when its a 1d array, you can't flatten it so return itself 46 | else if (ndim(input) === 1) { 47 | return input; 48 | } 49 | //when ndim = 2 or higher and not instanceof Mat or Tensor, then it's JS array so return it through mathjs.flatten 50 | else { 51 | return mathjs.flatten(raw_input); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /floor.hhs: -------------------------------------------------------------------------------- 1 | /** 2 | * @author Blake Wang 3 | * @params input - The structure with elements to be rounded toward negative infinity. Can be a number, 1d array, 2d array, Mat, Tensor. 4 | * @returns a number rounded to negative infinity if input is a number, a Mat with all elements rounded to negative infinity if input is a 1d array, 2d array, Mat or Tensor. 5 | * 6 | * 7 | * 8 | */ 9 | 10 | 11 | 12 | function floor(input) { 13 | 14 | *import math: is_number 15 | *import math: ndim 16 | *import math: deep_copy 17 | 18 | if (arguments.length === 0) { 19 | throw new Error('Exception occurred in floor - no argument given'); 20 | } 21 | else if (arguments.length > 1) { 22 | throw new Error('Exception occurred in floor - wrong argument number'); 23 | } 24 | 25 | if (is_number(input)) { 26 | if (input >= 0) { 27 | return parseInt(input); 28 | } 29 | else { 30 | return (input % 1 === 0 ? parseInt(input) : parseInt(input) - 1); 31 | } 32 | } 33 | 34 | if (!(Array.isArray(input)) && !(input instanceof Mat) && !(input instanceof Tensor)) { 35 | throw new Error('Exception occurred in floor - input must be a number, array, matrix or tensor'); 36 | } 37 | 38 | // if input is a Mat, Tensor or 2d array 39 | let in_type = ((input instanceof Mat || input instanceof Tensor)); 40 | let raw_in = in_type ? input.clone().val : deep_copy(input); 41 | let result = floor_helper(raw_in); 42 | 43 | let dim = ndim(result); 44 | if (dim === 1) { 45 | return result; 46 | } 47 | else if (dim === 2) { 48 | return mat(result); 49 | } 50 | else { 51 | return new Tensor(result); 52 | } 53 | 54 | function floor_helper(input) { 55 | let dim = ndim(input); 56 | 57 | if (dim === 1) { 58 | let result = deep_copy(input); 59 | for (let i = 0; i < input.length; i++) { 60 | let temp = input[i]; 61 | if (temp >= 0) { 62 | result[i] = parseInt(temp); 63 | } 64 | else { 65 | result[i] = (temp % 1 === 0 ? parseInt(temp) : parseInt(temp) - 1); 66 | } 67 | } 68 | return result; 69 | } 70 | 71 | else { 72 | let result = []; 73 | for (let i = 0; i < input.length; i++) { 74 | let temp = floor_helper(input[i]); 75 | result.push(temp); 76 | } 77 | return result; 78 | } 79 | } 80 | } 81 | 82 | -------------------------------------------------------------------------------- /floor_test.hhs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | function floor_test() { 5 | 6 | *import math: floor 7 | 8 | // test scenario 1: integer test 9 | // testset setup 10 | let testSet1 = [1, -1, 0, -0, 1.1, -1.1, 1.0, -1.0]; 11 | let expectedVal1 = [1, -1, 0, 0, 1, -2, 1, -1]; 12 | // test process 13 | for (let i = 0; i < testSet1.length; i++) { 14 | if (floor(testSet1[i]) !== expectedVal1[i]) { 15 | throw new Error("floor integer test has failed on testcase: " + testSet1[i] + 16 | "\noutput " + floor(testSet1[i]) + " while " + expectedVal1[i] + " is expected."); 17 | } 18 | } 19 | 20 | // test scenario 2: 1d array test 21 | // testset setup 22 | let testSet2 = [1, -1, 0, -0, 1.1, -1.1, 1.0, -1.0]; 23 | let expectedVal2 = mat([[1, -1, 0, 0, 1, -2, 1, -1]]); 24 | // test process 25 | if (!(floor(testSet2) === expectedVal2)) { 26 | throw new Error("floor 1d array test has failed"); 27 | } 28 | 29 | // test scenario 3: 2d array test 30 | // testset setup 31 | let testSet3 = [[1, -1, 0, -0], [1.1, -1.1, 1.0, -1.0]]; 32 | let expectedVal3 = mat([[1, -1, 0, 0], [1, -2, 1, -1]]); 33 | // test process 34 | if (!(floor(testSet3) === expectedVal3)) { 35 | throw new Error("floor 2d array test has failed"); 36 | } 37 | 38 | // test scenario 4: Mat test 39 | // testset setup 40 | let testSet4 = mat([[1, -1, 0, -0], [1.1, -1.1, 1.0, -1.0]]); 41 | let expectedVal4 = mat([[1, -1, 0, 0], [1, -2, 1, -1]]); 42 | // test process 43 | if (!(floor(testSet4) === expectedVal4)) { 44 | throw new Error("floor Mat test has failed"); 45 | } 46 | 47 | if (floor(1.5) !== 1) { 48 | throw new Error('Unit test failed for number for floor'); 49 | } 50 | 51 | let a = [1.5, 2.5, -1.5]; 52 | if (!(floor(a) === [1, 2, -2])) { 53 | throw new Error('Unit test failed for array for floor'); 54 | } 55 | 56 | let b = mat([[1.5, 2.5], [-1.5, -2.5]]); 57 | if (!(floor(b) === mat([[1, 2], [-2, -3]]))) { 58 | throw new Error('Unit test failed for matrix for floor'); 59 | } 60 | 61 | let c = new Tensor([[[1.5, 2.5], [-1.5, -2.5]], [[1.5, 2.5], [-1.5, -2.5]]]); 62 | let d = floor(c).val; 63 | if (!(d[0] === [[1, 2], [-2, -3]]) || !(d[1] === [[1, 2], [-2, -3]])) { 64 | throw new Error('Unit test failed for tensor for floor'); 65 | } 66 | 67 | //print('floor test pass'); 68 | 69 | } 70 | -------------------------------------------------------------------------------- /gcd.hhs: -------------------------------------------------------------------------------- 1 | /** 2 | * @author Blake Wang 3 | * @params 2 integers, 2 js 2d-arrays/Mat objects of the same dimensions, 2 js 1d-arrays of the same dimensions 4 | * Or input an array to find the gcd of all the elements 5 | * @returns an integer representing greatest common divisor of 2 input integers 6 | * or a mat object holds greatest common divisors of the elements of A and B 7 | * 8 | * 9 | * 10 | */ 11 | 12 | 13 | 14 | 15 | function gcd(A, B = 1) { 16 | 17 | *import math: deep_copy 18 | *import math: ndim 19 | *import math: is_number 20 | *import math: flatten 21 | 22 | //helper function for 1 argument / list version. could probably combine with integer version for compactness 23 | 24 | function find_gcd(a, b) { 25 | 26 | a = a < 0 ? -a : a; 27 | b = b < 0 ? -b : b; 28 | 29 | if (parseInt(a) !== a || parseInt(b) !== b) { 30 | throw new Error('Exception occurred in gcd - number must be integer'); 31 | } 32 | 33 | if (a * b === 0) { 34 | return 0; 35 | } 36 | 37 | if (a === 1 || b === 1) { 38 | return 1; 39 | } 40 | 41 | if (a < b) { 42 | let temp = a; 43 | a = b; 44 | b = temp; 45 | } 46 | 47 | // looping 48 | while (b > 0) { 49 | let temp = a % b; 50 | a = b; 51 | b = temp; 52 | } 53 | return a; 54 | } 55 | 56 | if (arguments.length === 0) { 57 | throw new Error('Exception occurred in gcd - no argument given'); 58 | } 59 | 60 | if (arguments.length > 2) { 61 | throw new Error('Exception occurred in gcd - wrong argument number'); 62 | } 63 | 64 | // the case input is [1, 2, 3, 4, 5] 65 | if (arguments.length === 1) { 66 | if (is_number(A)) { 67 | return A; 68 | } 69 | if (!(Array.isArray(A)) && !(A instanceof Mat) && !(A instanceof Tensor)) { 70 | throw new Error('Exception occurred in gcd - input must be number, array, matrix or tensor'); 71 | } 72 | 73 | let in_type = A instanceof Mat || A instanceof Tensor; 74 | let raw_in = in_type ? A.clone().val : deep_copy(A); 75 | // in this way we guarantee A is 1-dimensional JS array 76 | raw_in = flatten(raw_in); 77 | if (raw_in.length === 1) { 78 | return raw_in[0]; 79 | } 80 | let result = find_gcd(raw_in[0], raw_in[1]); 81 | for (let i = 2; i < raw_in.length; i++) { 82 | result = find_gcd(result, raw_in[i]); 83 | } 84 | return result; 85 | } 86 | 87 | if (is_number(A) && is_number(B)) { 88 | return find_gcd(A, B); 89 | } 90 | 91 | if (!(Array.isArray(A)) && !(A instanceof Mat) && !(A instanceof Tensor)) { 92 | throw new Error('Exception occurred in gcd - input must be number, array, matrix or tensor'); 93 | } 94 | if (!(Array.isArray(B)) && !(B instanceof Mat) && !(B instanceof Tensor)) { 95 | throw new Error('Exception occurred in gcd - input must be number, array, matrix or tensor'); 96 | } 97 | 98 | let in_type_a = (A instanceof Mat) || (A instanceof Tensor); 99 | let in_type_b = (B instanceof Mat) || (B instanceof Tensor); 100 | let raw_in_a = in_type_a ? A.clone().val : deep_copy(A); 101 | let raw_in_b = in_type_b ? B.clone().val : deep_copy(B); 102 | 103 | // in case raw_in_a = [[[1, 2, 3]]] 104 | while (Array.isArray(raw_in_a) && raw_in_a.length === 1) { 105 | raw_in_a = raw_in_a[0]; 106 | } 107 | while (Array.isArray(raw_in_b) && raw_in_b.length === 1) { 108 | raw_in_b = raw_in_b[0]; 109 | } 110 | 111 | // in case A = [[1]], B = [[2]] 112 | if (is_number(raw_in_a) && is_number(raw_in_b)) { 113 | return find_gcd(A, B); 114 | } 115 | 116 | let result = gcd_helper(raw_in_a, raw_in_b); 117 | let dim = ndim(result); 118 | if (dim === 1) { 119 | return result; 120 | } 121 | else if (dim === 2) { 122 | return mat(result); 123 | } 124 | else { 125 | return new Tensor(result); 126 | } 127 | 128 | function gcd_helper(A, B) { 129 | 130 | let dimA = ndim(A); 131 | let dimB = ndim(B); 132 | if (dimA !== dimB || A.length !== B.length) { 133 | throw new Error('Exception occurred in gcd - A and B must have the same sizes'); 134 | } 135 | 136 | if (dimA === 1) { 137 | let result = deep_copy(A); 138 | for (let i = 0; i < A.length; i++) { 139 | result[i] = find_gcd(A[i], B[i]); 140 | } 141 | return result; 142 | } 143 | 144 | else { 145 | let result = []; 146 | for (let i = 0; i < A.length; i++) { 147 | let temp = gcd_helper(A[i], B[i]); 148 | result.push(temp); 149 | } 150 | return result; 151 | } 152 | } 153 | } 154 | -------------------------------------------------------------------------------- /gcd_test.hhs: -------------------------------------------------------------------------------- 1 | 2 | 3 | function gcd_test() { 4 | 5 | *import math: gcd 6 | 7 | if (gcd(42, 120) !== 6) { 8 | throw new Error('Unit test failed for number for gcd'); 9 | } 10 | 11 | let a = [3, 7, 9]; 12 | let b = [6, 9, 12]; 13 | if (!(gcd(a, b) === [3, 1, 3])) { 14 | throw new Error('Unit test failed for array for gcd'); 15 | } 16 | 17 | let c = mat([[3, 7, 9], [3, 7, 9]]); 18 | let d = mat([[6, 9, 12], [6, 9, 12]]); 19 | if (!(gcd(c, d) === mat([[3, 1, 3], [3, 1, 3]]))) { 20 | throw new Error('Unit test failed for matrix for gcd'); 21 | } 22 | 23 | let e = new Tensor([[[3, 7, 9], [3, 7, 9]], [[3, 7, 9], [3, 7, 9]]]); 24 | let f = new Tensor([[[6, 9, 12], [6, 9, 12]], [[6, 9, 12], [6, 9, 12]]]); 25 | let g = gcd(e, f).val; 26 | if (!(g[0] === [[3, 1, 3], [3, 1, 3]]) || !(g[1] === [[3, 1, 3], [3, 1, 3]])) { 27 | throw new Error('Unit test failed for tensor for gcd'); 28 | } 29 | 30 | let h = [15, 25, 35]; 31 | if (gcd(h) !== 5) { 32 | throw new Error('Unit test failed for array for gcd'); 33 | } 34 | 35 | //print('gcd test pass'); 36 | 37 | } 38 | -------------------------------------------------------------------------------- /hankel.hhs: -------------------------------------------------------------------------------- 1 | /** 2 | * @Author Jianan Lin (林家南) 3 | * @param input: an array or some numbers, must be odd length 4 | * @returns - a hankel matrix 5 | * definition: https://en.wikipedia.org/wiki/Hankel_matrix 6 | * 7 | */ 8 | 9 | 10 | 11 | function hankel(input) { 12 | 13 | *import math: ndim 14 | *import math: deep_copy 15 | *import math: flatten 16 | 17 | // argument check 18 | if (arguments.length === 0) { 19 | throw new Error('Exception occurred in hankel - no argument given'); 20 | } 21 | 22 | // hankel(1, 2, 3) 23 | if (typeof input === 'number') { 24 | if (arguments.length % 2 === 0) { 25 | throw new Error('Exception occurred in hankel - length of input must be odd'); 26 | } 27 | let raw_in = []; 28 | for (let i = 0; i < arguments.length; i++) { 29 | raw_in.push(arguments[i]); 30 | } 31 | let mid = parseInt(raw_in.length / 2); 32 | let result = new Array(); 33 | for (let i = 0; i <= mid; i++) { 34 | result[i] = new Array(); 35 | for (let j = 0; j <= mid; j++) { 36 | result[i][j] = 0; 37 | } 38 | } 39 | for (let i = 0; i <= mid; i++) { 40 | for (j = 0; j <= i; j++) { 41 | result[i - j][j] = raw_in[i]; 42 | result[mid - i + j][mid - j] = raw_in[raw_in.length - i - 1]; 43 | } 44 | } 45 | return mat(result); 46 | } 47 | 48 | // hankel([1, 2, 3]) 49 | else { 50 | let raw_in = flatten(input); 51 | if (raw_in.length % 2 === 0) { 52 | throw new Error('Exception occurred in hankel - length of input must be odd'); 53 | } 54 | let mid = parseInt(raw_in.length / 2); 55 | let result = new Array(); 56 | for (let i = 0; i <= mid; i++) { 57 | result[i] = new Array(); 58 | for (let j = 0; j <= mid; j++) { 59 | result[i][j] = 0; 60 | } 61 | } 62 | for (let i = 0; i <= mid; i++) { 63 | for (j = 0; j <= i; j++) { 64 | result[i - j][j] = raw_in[i]; 65 | result[mid - i + j][mid - j] = raw_in[raw_in.length - i - 1]; 66 | } 67 | } 68 | return mat(result); 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /hankel_test.hhs: -------------------------------------------------------------------------------- 1 | /** 2 | * @author Jianan Lin 3 | * Unit test for hankel 4 | * 5 | */ 6 | 7 | 8 | 9 | function hankel_test() { 10 | 11 | *import math: hankel 12 | 13 | let a = [1, 2, 3, 4, 5]; 14 | b = hankel(a); 15 | if (!(b === mat([[1, 2, 3], [2, 3, 4], [3, 4, 5]]))) { 16 | throw new Error('Unit test failed for hankel'); 17 | } 18 | 19 | let c = hankel(1, 2, 3, 4, 5); 20 | if (!(c === mat([[1, 2, 3], [2, 3, 4], [3, 4, 5]]))) { 21 | throw new Error('Unit test failed for hankel'); 22 | } 23 | 24 | print('hankel test pass'); 25 | } 26 | -------------------------------------------------------------------------------- /hedgehog-package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name":"math" 3 | } 4 | -------------------------------------------------------------------------------- /http-server/server.js: -------------------------------------------------------------------------------- 1 | const Koa = require('koa') 2 | const path = require('path') 3 | const fs = require('fs') 4 | 5 | const app = new Koa() 6 | 7 | app.use(async ctx => { 8 | ctx.set('content-type', 'text/plain; charset=utf-8'); 9 | ctx.set('access-control-allow-origin', '*'); 10 | ctx.set('accept-ranges', 'bytes'); 11 | 12 | const requestFile = ctx.request.path; 13 | const filePath = path.join(__dirname, "/..", requestFile); 14 | 15 | try{ 16 | console.log(filePath); 17 | const fileBuffer = fs.ReadStream(filePath); 18 | ctx.body = fileBuffer; 19 | } catch(error) { 20 | console.log(error); 21 | } 22 | }); 23 | 24 | app.listen(5000); 25 | 26 | // run with node server.js, default port is 4000 -------------------------------------------------------------------------------- /identity.hhs: -------------------------------------------------------------------------------- 1 | /** 2 | * @author Mudroad White 3 | * @param input - a positive integer denoting the size of the expected matrix. 4 | * @returns - a Mat object representing a 2D identity matrix. 5 | */ 6 | 7 | function identity(input) { 8 | 9 | *import math: is_number 10 | 11 | // Corner cases for input 12 | // Argument length should be only one 13 | if ( !(arguments.length === 1) ) { 14 | throw new Error('Identity expects only one argument'); 15 | } 16 | // Input should be a positive integer 17 | if (!Number.isInteger(input) || input < 0) { 18 | throw new Error('Excepted positive integer input'); 19 | } 20 | 21 | // Then we return special Mat value when input is 0 or 1 22 | if (input === 0) { 23 | return new Mat([]); 24 | } 25 | 26 | if (input === 1) { 27 | return new Mat([[1]]); 28 | } 29 | 30 | // Now we can start on creating the Id matrix 31 | let result = new Mat().zeros(input, input); 32 | for (let i = 0; i < input; i++){ 33 | result.val[i][i] = 1; 34 | } 35 | 36 | // return as a Mat object 37 | return result; 38 | } -------------------------------------------------------------------------------- /identity_test.hhs: -------------------------------------------------------------------------------- 1 | /** 2 | * @author Mudroad White 3 | * @param 4 | * @returns 5 | * 6 | * Functions for testing the identity function. 7 | */ 8 | 9 | function identity_test(){ 10 | *import math: identity 11 | 12 | // Corner cases 13 | if (!(identity(0) === new Mat([]))){ 14 | throw 'identity unit test failed on identity(0)'; 15 | } 16 | 17 | if (!(identity(1) === new Mat([[1]]) )){ 18 | throw 'identity unit test failed on identity(1)'; 19 | } 20 | 21 | // Ordinary case 22 | const test1 = new Mat([[1, 0], [0, 1]]); 23 | if (!(identity(2)) === test1){ 24 | throw 'identity unit test failed on identity(2)'; 25 | } 26 | } -------------------------------------------------------------------------------- /inv.hhs: -------------------------------------------------------------------------------- 1 | /** 2 | * @author Jason Reynolds 3 | * @param input - the matrix to be inverted, Mat or JS array 4 | * @returns - the inverted matrix 5 | * 6 | * Function that given a square invertible matrice, it will return the inverted matrix 7 | */ 8 | 9 | 10 | 11 | 12 | function inv(input) { 13 | 14 | *import math: ndim 15 | *import math: det 16 | *import math: deep_copy 17 | 18 | if (arguments.length === 0) { 19 | throw new Error('Exception occurred in inv - no argument given'); 20 | } 21 | else if (arguments.length > 1) { 22 | throw new Error('Exception occurred in inv - wrong argument number'); 23 | } 24 | 25 | if (!(Array.isArray(input)) && !(input instanceof Mat) && !(input instanceof Tensor)) { 26 | throw new Error('Exception occurred in inv - input must be a 2-dimensional JS array, matrix or tensor'); 27 | } 28 | 29 | //declare raw_in to be a clone of input if Mat otherwise input itself 30 | let in_type = (input instanceof Mat) || (input instanceof Tensor) 31 | let raw_in = (in_type) ? input.clone() : deep_copy(input); 32 | 33 | //when an instance of Mat degrade to JS array to generalize preceeding code 34 | if (in_type) { 35 | raw_in = raw_in.val; 36 | } 37 | //if the ndim is not 2 i.e. not a 2d matrix, throw an error 38 | if (!(ndim(input) === 2)) { 39 | throw new Error('Exception occurred in inv - not a 2d matrix, either wrong formatting or wrong dimensions') 40 | } 41 | //if row length =/= col length throw error 42 | if (!(raw_in.length === raw_in[0].length)) { 43 | throw new Error('Exception occurred in inv - not a square matrix'); 44 | } 45 | //if det === 0 it's not invertible, throw error 46 | if (det(raw_in) === 0) { 47 | throw new Error('Exception occurred in inv - det is zero, not invertible'); 48 | } 49 | //if it is ndim 2 or input is of instance Mat (not necessary?), finally return the inverse as a Mat object 50 | if (ndim(raw_in) === 2 || input instanceof Mat) { 51 | return new Mat(mathjs.inv(raw_in)); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /iqr.hhs: -------------------------------------------------------------------------------- 1 | /** 2 | * @Author Jianan Lin (林家南) 3 | * @param input - a series of number, or an array, matrix, tensor to get the interquartile range 4 | * calculate the quartile of the data and get three borderline points q1, q2, q3 5 | * Use this formula: https://www.statisticshowto.com/probability-and-statistics/interquartile-range/ 6 | * @returns - a number = q3 - q1 7 | * 8 | */ 9 | 10 | 11 | function iqr(input) { 12 | 13 | *import math: ndim 14 | *import math: deep_copy 15 | *import math: flatten 16 | 17 | let sort_array = []; 18 | 19 | if (arguments.length === 0) { 20 | throw new Error('Exception occurred in iqr - no argument given'); 21 | } 22 | 23 | // bounds(1, 2, 3, 4, 5, 6) 24 | if (typeof arguments[0] === 'number') { 25 | for (let i = 0; i < arguments.length; i++) { 26 | if (!(typeof arguments[i] === 'number')) { 27 | throw new Error('Exception occurred in iqr - argument must be number'); 28 | } 29 | else { 30 | sort_array.push(arguments[i]); 31 | } 32 | } 33 | sort_array = private_quick_sort(sort_array); 34 | } 35 | 36 | else { 37 | // iqr([1, 2, 3], 0) 38 | if (arguments.length > 1) { 39 | throw new Error('Exception occurred in iqr - wrong argument number'); 40 | } 41 | 42 | if (!(Array.isArray(input)) && !(input instanceof Mat) && !(input instanceof Tensor)) { 43 | throw new Error('Exception occurred in iqr - input must be an array, matrix or tensor'); 44 | } 45 | 46 | let in_type = (input instanceof Mat) || (input instanceof Tensor); 47 | let raw_in = in_type ? input.clone().val : deep_copy(input); 48 | if (ndim(raw_in) > 1) { 49 | raw_in = flatten(raw_in); 50 | } 51 | sort_array = private_quick_sort(raw_in); 52 | } 53 | 54 | let length = sort_array.length; 55 | if (length === 0) { 56 | throw new Error('Exception occurred in iqr - input cannot be empty'); 57 | } 58 | 59 | // even even 60 | else if (length % 2 === 0) { 61 | let borderline = length / 2; 62 | if (borderline % 2 === 1) { 63 | return sort_array[(3 * borderline - 1) / 2] - sort_array[(borderline - 1) / 2]; 64 | } 65 | else { 66 | return 0.5 * (sort_array[3 * borderline / 2] + sort_array[3 * borderline / 2 - 1] - sort_array[borderline / 2] - sort_array[borderline / 2 - 1]); 67 | } 68 | } 69 | 70 | // odd length 71 | else { 72 | let borderline = (length - 1) / 2; 73 | if (borderline % 2 === 1) { 74 | return sort_array[(3 * borderline + 1) / 2] - sort_array[(borderline - 1) / 2]; 75 | } 76 | else { 77 | return 0.5 * (sort_array[3 * borderline / 2] + sort_array[3 * borderline / 2 + 1] - sort_array[borderline / 2] - sort_array[borderline / 2 - 1]); 78 | } 79 | } 80 | 81 | 82 | // when we use this function, we have ensured that input is an array 83 | function private_quick_sort(input) { 84 | 85 | // if the length is less than 10, then use insert sort 86 | if (input.length < 10) { 87 | return private_insert_sort(input); 88 | } 89 | 90 | let borderline = (input[0] + input[input.length - 1]) / 2; 91 | let L = [], M = [], R = []; 92 | 93 | // divide the elements into left and right 94 | for (let i = 0; i < input.length; i++) { 95 | if (input[i] < borderline) { 96 | L.push(input[i]); 97 | } 98 | else if (input[i] == borderline) { 99 | M.push(input[i]); 100 | } 101 | else { 102 | R.push(input[i]); 103 | } 104 | } 105 | 106 | // recursion 107 | L = private_quick_sort(L); 108 | R = private_quick_sort(R); 109 | 110 | let result = L.concat(M).concat(R); 111 | return result; 112 | } 113 | 114 | // when we use this function, we have ensured that input is an array 115 | function private_insert_sort(input) { 116 | 117 | // if the length is 0 or 1 118 | if (input.length <= 1) { 119 | return input; 120 | } 121 | 122 | // insert part 123 | for (let i = 1; i < input.length; i++) { 124 | 125 | // find the position to insert 126 | let j = 0; 127 | while (input[j] <= input[i] && j < i) { 128 | j++; 129 | } 130 | 131 | // move the element 132 | let temp = input[i]; 133 | for (let k = i; k > j; k--) { 134 | input[k] = input[k - 1]; 135 | } 136 | input[j] = temp; 137 | } 138 | 139 | return input; 140 | } 141 | } 142 | -------------------------------------------------------------------------------- /iqr_test.hhs: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author Jianan Lin 3 | * test for iqr function 4 | */ 5 | 6 | 7 | function iqr_test() { 8 | 9 | *import math: iqr 10 | 11 | // length = 11, borderline = 5 12 | if (iqr(1, 2, 5, 6, 7, 9, 12, 15, 18, 19, 27) !== 13) { 13 | throw new Error('Unit test failed for iqr for number'); 14 | } 15 | 16 | // length = 9, borderline = 4 17 | if (iqr(2, 5, 11, 13, 19, 26, 32, 40, 50) !== 28) { 18 | throw new Error('Unit test failed for iqr for number'); 19 | } 20 | 21 | // length = 10, borderline = 5 22 | if (iqr(3, 5, 7, 8, 9, 11, 15, 16, 20, 21) !== 9) { 23 | throw new Error('Unit test failed for iqr for number'); 24 | } 25 | 26 | // length = 8, borderline = 4 27 | if (iqr(1, 2, 4, 7, 11, 18, 30, 55) !== 21) { 28 | throw new Error('Unit test failed for iqr for number'); 29 | } 30 | 31 | // length = 11, borderline = 5 32 | if (iqr([1, 2, 5, 6, 7, 9, 12, 15, 18, 19, 27]) !== 13) { 33 | throw new Error('Unit test failed for iqr for array'); 34 | } 35 | 36 | // length = 9, borderline = 4 37 | if (iqr([2, 5, 11, 13, 19, 26, 32, 40, 50]) !== 28) { 38 | throw new Error('Unit test failed for iqr for array'); 39 | } 40 | 41 | // length = 10, borderline = 5 42 | if (iqr([3, 5, 7, 8, 9, 11, 15, 16, 20, 21]) !== 9) { 43 | throw new Error('Unit test failed for iqr for array'); 44 | } 45 | 46 | // length = 8, borderline = 4 47 | if (iqr([1, 2, 4, 7, 11, 18, 30, 55]) !== 21) { 48 | throw new Error('Unit test failed for iqr for array'); 49 | } 50 | 51 | //print('iqr test pass'); 52 | } 53 | -------------------------------------------------------------------------------- /is_diag.hhs: -------------------------------------------------------------------------------- 1 | /** 2 | * @author Jing Stone 3 | * @param input - 1d list or 2d matrix 4 | * @returns - 5 | * Determine if matrix is diagonal, matrix could be rectangular. 6 | */ 7 | 8 | function is_diag(input) { 9 | *import math: ndim 10 | *import math: deep_copy 11 | 12 | // now we're dealing with non-numbers and matrix-like structures: 13 | // declare raw_in, set it to a input clone if its a Matrix / Tensor otherwise input itself 14 | let in_type = (input instanceof Mat) || (input instanceof Tensor); 15 | // here we always maintain raw_in to be an array 16 | let raw_in = (in_type) ? input.clone().val : deep_copy(input); 17 | // declare dimension of raw_in 18 | let dims = ndim(raw_in); 19 | 20 | // true for [] 21 | if (raw_in.length === 0) return true; 22 | 23 | // for 1d list, checkt if the element is 0 exclude raw_in[0] 24 | if (dims === 1) { 25 | for (let i = 1; i < raw_in.length; i++) { 26 | if (raw_in[i] !== 0) { 27 | return false; 28 | } 29 | } 30 | return true; 31 | } 32 | // check other elements except main diagonal are zero or not. 33 | else if(dims === 2) { 34 | let row = raw_in.length; 35 | let col = raw_in[0].length; 36 | for (let i = 0; i < row; i++) { 37 | for (let j = 0; j < col; j++) { 38 | if (i !== j && raw_in[i][j] !== 0) 39 | return false; 40 | } 41 | } 42 | return true; 43 | } 44 | else 45 | { 46 | throw new Error("Error in is_diag - input parameter dimension should be 1 or 2"); 47 | } 48 | } -------------------------------------------------------------------------------- /is_diag_test.hhs: -------------------------------------------------------------------------------- 1 | /** 2 | * @author Jing Stone 3 | * Unit test for is_diag function 4 | */ 5 | 6 | function is_diag_test() 7 | { 8 | *import math: is_diag 9 | 10 | let A1 = [1, 2, 3, 4]; 11 | let A2 = []; 12 | let A3 = [1, 0, 0, 0]; 13 | let A4 = [1, 0, 0, 1]; 14 | let A5 = [[1, 0, 0], [0, 2, 0], [0, 0, 3]]; 15 | let A6 = [[0, 0, 0], [0, 1, 0], [0, 0, 0]]; 16 | let A7 = [[1, 0, 0], [0, 1, 0], [0, 0, 0], [0, 0, 0]]; 17 | let A8 = [[1, 0, 0], [0, 1, 0], [0, 0, 3], [0, 0, 1]]; 18 | 19 | function test(input, output) 20 | { 21 | if(!(is_diag(input) === output)) 22 | { 23 | throw new Error('Unit test failed for is_diag.'); 24 | } 25 | } 26 | 27 | test(A1, false); 28 | test(A2, true); 29 | test(A3, true); 30 | test(A4, false); 31 | test(A5, true); 32 | test(A6, true); 33 | test(A7, true); 34 | test(A8, false); 35 | } -------------------------------------------------------------------------------- /is_local_max.hhs: -------------------------------------------------------------------------------- 1 | /** 2 | * @author Jianan Lin (林家南) 3 | * @param input - an array, matrix, tensor that you want to get local max 4 | * @param dim - 0 or 1: 0 is local max of each row, 1 is each column. 5 | * dim only supports 2-d matrix. For tensor or array, this para is ignored. 6 | * @returns - a matrix / tensor with elements 0 or 1 denoting whether it is local max. 7 | * 8 | */ 9 | 10 | 11 | function is_local_max(input, dim = 0) { 12 | 13 | *import math: deep_copy 14 | *import math: ndim 15 | 16 | // argument check 17 | if (arguments.length === 0) { 18 | throw new Error('Exception occurred in is_local_max - no argument given'); 19 | } 20 | 21 | if (arguments.length > 2) { 22 | throw new Error('Exception occurred in is_local_max - wrong argument number'); 23 | } 24 | 25 | if (!(Array.isArray(input)) && !(input instanceof Mat) && !(input instanceof Tensor)) { 26 | throw new Error('Exception occurred in is_local_max - input must be an array, matrix or tensor'); 27 | } 28 | 29 | if (dim !== 0 && dim !== 1) { 30 | throw new Error('Exception occurred in is_local_max - dim must be 0 or 1'); 31 | } 32 | 33 | let in_type = input instanceof Mat || input instanceof Tensor; 34 | let raw_in = in_type ? input.clone().val : deep_copy(input); 35 | let result = deep_copy(raw_in); 36 | 37 | // row local max 38 | if (dim === 0) { 39 | is_local_max_helper_row(raw_in, result); 40 | if (ndim(result) <= 2) { 41 | return mat(result); 42 | } 43 | else { 44 | return new Tensor(result); 45 | } 46 | } 47 | 48 | // column local max, require m * n matrix 49 | else { 50 | if (ndim(raw_in) !== 2) { 51 | throw new Error('Exception occurred in is_local_max - if dim = 1, then input must be a m * n matrix'); 52 | } 53 | else if (is_normal_matrix(raw_in) === false) { 54 | throw new Error('Exception occurred in is_local_max - if dim = 1, then input must be a m * n matrix'); 55 | } 56 | 57 | else { 58 | for (let i = 0; i < raw_in.length; i++) { 59 | for (let j = 0; j < raw_in[i].length; j++) { 60 | if (i === 0 || i === raw_in.length - 1) { 61 | result[i][j] = 0; 62 | } 63 | else if (raw_in[i][j] < raw_in[i - 1][j] || raw_in[i][j] < raw_in[i + 1][j]) { 64 | result[i][j] = 0; 65 | } 66 | else { 67 | result[i][j] = 1; 68 | } 69 | } 70 | } 71 | 72 | return mat(result); 73 | } 74 | } 75 | 76 | function is_normal_matrix(input) { 77 | let m = input.length; 78 | let n = input[0].length; 79 | for (let i = 0; i < m; i++) { 80 | if (input[i].length !== n) { 81 | return false; 82 | } 83 | } 84 | return true; 85 | } 86 | 87 | function judge_row(array, index) { 88 | if (index === 0 || index === array.length - 1) { 89 | return 0; 90 | } 91 | else if (array[index] < array[index + 1] || array[index] < array[index - 1]) { 92 | return 0; 93 | } 94 | else { 95 | return 1; 96 | } 97 | } 98 | 99 | function is_local_max_helper_row(array1, array2) { 100 | if (ndim(array1) === 1) { 101 | for (let i = 0; i < array1.length; i++) { 102 | array2[i] = judge_row(array1, i); 103 | } 104 | } 105 | else { 106 | for (let i = 0; i < array1.length; i++) { 107 | is_local_max_helper_row(array1[i], array2[i]); 108 | } 109 | } 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /is_local_max_test.hhs: -------------------------------------------------------------------------------- 1 | /** 2 | * @author Jianan Lin 3 | * Unit test for is_local_max 4 | * 5 | */ 6 | 7 | 8 | function is_local_max_test() { 9 | 10 | *import math: is_local_max 11 | 12 | let a = mat([[2, 3, 1], [2, 3, 1]]); 13 | let b = mat([[0, 1, 0], [0, 1, 0]]) 14 | 15 | if (!(is_local_max(a) === b)) { 16 | throw new Error('Unit test failed for is_local_max for numbers'); 17 | } 18 | 19 | let c = mat([[2, 2], [3, 3], [1, 1]]); 20 | let d = mat([[0, 0], [1, 1], [0, 0]]); 21 | if (!(is_local_max(c, 1) === d)) { 22 | throw new Error('Unit test failed for is_local_max for numbers'); 23 | } 24 | 25 | //print('is_local_max test pass'); 26 | } 27 | -------------------------------------------------------------------------------- /is_local_min.hhs: -------------------------------------------------------------------------------- 1 | /** 2 | * @author Jianan Lin (林家南) 3 | * @param input - an array, matrix, tensor that you want to get local min 4 | * @param dim - 0 or 1: 0 is local min of each row, 1 is each column. 5 | * dim only supports 2-d matrix. For tensor or array, this para is ignored. 6 | * @returns - a matrix / tensor with elements 0 or 1 denoting whether it is local min. 7 | * 8 | */ 9 | 10 | 11 | function is_local_min(input, dim = 0) { 12 | 13 | *import math: deep_copy 14 | *import math: ndim 15 | 16 | // argument check 17 | if (arguments.length === 0) { 18 | throw new Error('Exception occurred in is_local_min - no argument given'); 19 | } 20 | 21 | if (arguments.length > 2) { 22 | throw new Error('Exception occurred in is_local_min - wrong argument number'); 23 | } 24 | 25 | if (!(Array.isArray(input)) && !(input instanceof Mat) && !(input instanceof Tensor)) { 26 | throw new Error('Exception occurred in is_local_min - input must be an array, matrix or tensor'); 27 | } 28 | 29 | if (dim !== 0 && dim !== 1) { 30 | throw new Error('Exception occurred in is_local_min - dim must be 0 or 1'); 31 | } 32 | 33 | let in_type = input instanceof Mat || input instanceof Tensor; 34 | let raw_in = in_type ? input.clone().val : deep_copy(input); 35 | let result = deep_copy(raw_in); 36 | 37 | // row local min 38 | if (dim === 0) { 39 | is_local_min_helper_row(raw_in, result); 40 | if (ndim(result) <= 2) { 41 | return mat(result); 42 | } 43 | else { 44 | return new Tensor(result); 45 | } 46 | } 47 | 48 | // column local min, require m * n matrix 49 | else { 50 | if (ndim(raw_in) !== 2) { 51 | throw new Error('Exception occurred in is_local_min - if dim = 1, then input must be a m * n matrix'); 52 | } 53 | else if (is_normal_matrix(raw_in) === false) { 54 | throw new Error('Exception occurred in is_local_min - if dim = 1, then input must be a m * n matrix'); 55 | } 56 | 57 | else { 58 | for (let i = 0; i < raw_in.length; i++) { 59 | for (let j = 0; j < raw_in[i].length; j++) { 60 | if (i === 0 || i === raw_in.length - 1) { 61 | result[i][j] = 0; 62 | } 63 | else if (raw_in[i][j] > raw_in[i - 1][j] || raw_in[i][j] > raw_in[i + 1][j]) { 64 | result[i][j] = 0; 65 | } 66 | else { 67 | result[i][j] = 1; 68 | } 69 | } 70 | } 71 | 72 | return mat(result); 73 | } 74 | } 75 | 76 | function is_normal_matrix(input) { 77 | let m = input.length; 78 | let n = input[0].length; 79 | for (let i = 0; i < m; i++) { 80 | if (input[i].length !== n) { 81 | return false; 82 | } 83 | } 84 | return true; 85 | } 86 | 87 | function judge_row(array, index) { 88 | if (index === 0 || index === array.length - 1) { 89 | return 0; 90 | } 91 | else if (array[index] > array[index + 1] || array[index] > array[index - 1]) { 92 | return 0; 93 | } 94 | else { 95 | return 1; 96 | } 97 | } 98 | 99 | function is_local_min_helper_row(array1, array2) { 100 | if (ndim(array1) === 1) { 101 | for (let i = 0; i < array1.length; i++) { 102 | array2[i] = judge_row(array1, i); 103 | } 104 | } 105 | else { 106 | for (let i = 0; i < array1.length; i++) { 107 | is_local_min_helper_row(array1[i], array2[i]); 108 | } 109 | } 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /is_local_min_test.hhs: -------------------------------------------------------------------------------- 1 | /** 2 | * @author Jianan Lin 3 | * Unit test for is_local_min 4 | * 5 | */ 6 | 7 | 8 | function is_local_min_test() { 9 | 10 | *import math: is_local_min 11 | 12 | let a = mat([[2, 1, 3], [2, 1, 3]]); 13 | let b = mat([[0, 1, 0], [0, 1, 0]]) 14 | 15 | if (!(is_local_min(a) === b)) { 16 | throw new Error('Unit test failed for is_local_min for numbers'); 17 | } 18 | 19 | let c = mat([[2, 2], [1, 1], [3, 3]]); 20 | let d = mat([[0, 0], [1, 1], [0, 0]]); 21 | if (!(is_local_min(c, 1) === d)) { 22 | throw new Error('Unit test failed for is_local_min for numbers'); 23 | } 24 | 25 | //print('is_local_min test pass'); 26 | } 27 | -------------------------------------------------------------------------------- /is_missing.hhs: -------------------------------------------------------------------------------- 1 | /** 2 | * @author Jianan Lin (林家南) 3 | * @param input - an array, matrix, tensor that you want to find missing element 4 | * @returns - a matrix or tensor with same size, element is 1 or 0 depending on missing or not 5 | * A missing element is NaN, null, '', or undefined. Notice [] and {} are not missing. 6 | * 7 | */ 8 | 9 | 10 | 11 | function is_missing(input) { 12 | 13 | *import math: ndim 14 | *import math: deep_copy 15 | 16 | // argument check 17 | if (arguments.length === 0) { 18 | throw new Error('Exception occurred in is_missing - no argument given'); 19 | } 20 | 21 | if (arguments.length > 1) { 22 | throw new Error('Exception occurred in is_missing - wrong argument number'); 23 | } 24 | 25 | if (!(Array.isArray(input)) && !(input instanceof Mat) && !(input instanceof Tensor)) { 26 | throw new Error('Exception occurred in is_missing - input must be an array, matrix or tensor'); 27 | } 28 | 29 | let in_type = input instanceof Mat || input instanceof Tensor; 30 | let raw_in = in_type ? input.clone().val : deep_copy(input); 31 | let result = deep_copy(raw_in); 32 | ismissing_helper(raw_in, result); 33 | if (ndim(result) <= 2) { 34 | return mat(result); 35 | } 36 | else { 37 | return new Tensor(result); 38 | } 39 | 40 | function ismissing_helper(raw_in, result) { 41 | if (ndim(raw_in) === 1) { 42 | for (let i = 0; i < raw_in.length; i++) { 43 | if (raw_in[i] || raw_in[i] === 0) { 44 | result[i] = 0; 45 | } 46 | else { 47 | result[i] = 1 48 | } 49 | } 50 | return; 51 | } 52 | 53 | else { 54 | for (let i = 0; i < raw_in.length; i++) { 55 | ismissing_helper(raw_in[i], result[i]); 56 | } 57 | return; 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /is_missing_test.hhs: -------------------------------------------------------------------------------- 1 | /** 2 | * @author Jianan Lin 3 | * Unit test for ismissing 4 | * 5 | */ 6 | 7 | 8 | 9 | 10 | function is_missing_test() { 11 | 12 | *import math: is_missing 13 | 14 | let a = [NaN, 1, 0, 0, 1]; 15 | let b = mat([a, a]); 16 | let c = new Tensor([[a, a], [a, a]]); 17 | let d = [1, 0, 0, 0, 0]; 18 | 19 | if (!(is_missing(a) === mat(d))) { 20 | throw new Error('Unit test failed for is_missing for matrix'); 21 | } 22 | 23 | if (!(is_missing(b) === mat([d, d]))) { 24 | throw new Error('Unit test failed for is_missing for matrix'); 25 | } 26 | 27 | let e = is_missing(c).val; 28 | if (!(e[0] === [d, d]) || !(e[1] === [d, d])) { 29 | throw new Error('Unit test failed for is_missing for tensor'); 30 | } 31 | 32 | //print('is_missing test pass'); 33 | } 34 | -------------------------------------------------------------------------------- /is_number.hhs: -------------------------------------------------------------------------------- 1 | /** 2 | * Function for determining if a data type is of 'number', i.e. typical non finite number 3 | * 4 | * Original author: * is-number 5 | * 6 | * Copyright (c) 2014-present, Jon Schlinkert. 7 | * Released under the MIT License. 8 | * 9 | * @param input the value to check if its a number 10 | * @returns boolean true or false depending if its evaluated to be a number or not. Note that NaN, and infinity are not numbers 11 | */ 12 | 13 | function is_number(input) { 14 | 15 | "use strict"; 16 | 17 | //check if typeof input is a number first 18 | if (typeof input === 'number') { 19 | //if input-input === 0 return true; else return false; 20 | return input - input === 0; 21 | } 22 | //if we have a string for cases like '0xfff3d' and the trim is not blank 23 | if (typeof input === 'string' && !(input.trim() === '')) { 24 | //then return boolean Number.isFinite(+input) if Number.isFinite is true, if false return isFinite(+input) 25 | return Number.isFinite ? Number.isFinite(+input) : isFinite(+input) 26 | } 27 | //in all other cases return false 28 | return false; 29 | } 30 | -------------------------------------------------------------------------------- /is_symmetric.hhs: -------------------------------------------------------------------------------- 1 | /** 2 | * @author Jason Reynolds 3 | * @param input - the matrix to check if it's symmetric, needs to be square and 2d, can be Mat or JS 4 | * @returns - boolean value, true or false, depending on if if input is symmetric or not 5 | * 6 | * Function to check if the 2d matrix, raw array or Mat object is symmetric 7 | * 8 | */ 9 | 10 | 11 | 12 | //returns true if symmetric, false if not, throws exception if not correct dimensions or 0 dimensions 13 | function is_symmetric(input) { 14 | 15 | 16 | *import math: transpose 17 | *import math: ndim 18 | *import math: deep_copy 19 | 20 | if (arguments.length === 0) { 21 | throw new Error('Exception occurred in is_symmetric - no argument given'); 22 | } 23 | else if (arguments.length > 1) { 24 | throw new Error('Exception occurred in is_symmetric - wrong argument number'); 25 | } 26 | 27 | if (!(Array.isArray(input)) && !(input instanceof Mat) && !(input instanceof Tensor)) { 28 | throw new Error('Exception occurred in is_symmetric - input must be a 2-dimensional JS array, matrix or tensor'); 29 | } 30 | 31 | //declare raw_in to be input.clone() if input is Mat otherwise let it be input itself 32 | let in_type = (input instanceof Mat) || (input instanceof Tensor); 33 | let raw_in = (in_type) ? input.clone() : deep_copy(input); 34 | 35 | //in the case of input being Mat, degrade raw_in to JS array for generalized code 36 | if (in_type) { 37 | raw_in = raw_in.val; 38 | } 39 | //check: is matrix square? is any dimension 0? is ndim not 2? if so throw an error 40 | if ((!(ndim(raw_in) === 2)) || !(raw_in.length === raw_in[0].length) || raw_in.length === 0 || raw_in[0].length === 0) { 41 | throw new Error('Exception occurred in is_symmetric - either not square, not 2d, or 0 dimension row/cols'); 42 | } 43 | //if the Mat object doesn't equal it's transpose, return false 44 | if (!(raw_in === (transpose(raw_in)))) { 45 | return false; 46 | } 47 | //otherwise if all above statements failed, then its of valid size and it's transpose is itself; it passed all tests ==> it's symmetric 48 | return true; 49 | } 50 | -------------------------------------------------------------------------------- /is_symmetric_test.hhs: -------------------------------------------------------------------------------- 1 | /** 2 | * @author Jason Reynolds 3 | * @param 4 | * @returns 5 | * 6 | * Function that is a simple test for checking symmetricity of a matrix, raw or Mat object 7 | */ 8 | 9 | 10 | 11 | function is_symmetric_test() { 12 | 13 | *import math: is_symmetric 14 | 15 | if (is_symmetric([[1, 2, 3], [1, 2, 3], [1, 2, 3]])) { 16 | throw 'is_symmetric has failed the unit test' 17 | } 18 | 19 | if (!is_symmetric([[1, 2, 3], [2, 1, 4], [3, 4, 1]])) { 20 | throw 'is_symmetric has failed the unit test' 21 | } 22 | 23 | if (!is_symmetric(new Mat([[2, 1], [1, 2]]))) { 24 | throw 'is_symmetric has failed unit test for mat obj' 25 | } 26 | 27 | if (is_symmetric(new Mat([[1, 2, 3], [1, 2, 3], [1, 2, 3]]))) { 28 | throw 'is_symmetric has failed unit test for mat obj' 29 | } 30 | 31 | } -------------------------------------------------------------------------------- /is_tril.hhs: -------------------------------------------------------------------------------- 1 | /** 2 | * @author Jason Reynolds 3 | * 4 | * @param input - the matrix to be check if its lower triangular or not 5 | * @returns - boolean value true or false depending on if its determined to be lower triangular or not 6 | * 7 | * Function : is_tril - a boolean function to determine if a square matrix is lower triangular or not. Counter part to is_triu (upper triangular) 8 | * 9 | * 10 | */ 11 | 12 | 13 | 14 | function is_tril(input) { 15 | 16 | *import math: ndim 17 | *import math: deep_copy 18 | 19 | if (arguments.length === 0) { 20 | throw new Error('Exception occurred in is_tril - no argument given'); 21 | } 22 | else if (arguments.length > 1) { 23 | throw new Error('Exception occurred in is_tril - wrong argument number'); 24 | } 25 | 26 | if (!(Array.isArray(input)) && !(input instanceof Mat) && !(input instanceof Tensor)) { 27 | throw new Error('Exception occurred in is_tril - input must be a 2-dimensional JS array, matrix or tensor'); 28 | } 29 | 30 | //declare raw_in to be input.clone() if Mat otherwise input itself 31 | let in_type = (input instanceof Mat) || (input instanceof Tensor); 32 | let raw_in = (in_type) ? input.clone() : deep_copy(input); 33 | 34 | //degrade raw_in to JS array for generalized code 35 | if (in_type) { 36 | raw_in = raw_in.val; 37 | } 38 | //check: any dimension 0? square matrix? 2d? if so, throw error 39 | if (!(ndim(raw_in) === 2) || raw_in.length === 0 || raw_in[0].length === 0 || !(raw_in.length === raw_in[0].length)) { 40 | throw new Error('Exception occurred in is_tril - wrong dimensions, need a square 2d, non empty matrix as input'); 41 | } 42 | 43 | //iterate through array and if you spot a non-zero entry in a spot in the lower triangular region, return false 44 | //cut a sub matrix in the lower triangular region 45 | for (let k = 1; k < raw_in.length; k++) { 46 | for (let h = 0; h < raw_in.length - 1; h++) { 47 | //when in the tringaular zone exactly 48 | if (h < k) { 49 | if (!(raw_in[h][k] === 0)) { 50 | return false; 51 | } 52 | } 53 | } 54 | } 55 | //if it goes through the whole loop just fine then all the lower triangular entries are zero 56 | return true; 57 | } 58 | -------------------------------------------------------------------------------- /is_tril_test.hhs: -------------------------------------------------------------------------------- 1 | /** 2 | * @author Jason Reynolds 3 | * @param 4 | * @returns 5 | * 6 | * Function for testing the is_tril.hhs function 7 | */ 8 | 9 | 10 | 11 | 12 | 13 | function is_tril_test() { 14 | 15 | *import math: is_tril 16 | 17 | let a = new Mat([[3, 0, 0], [3, 2, 0], [1, 4, 1]]); 18 | if (!is_tril(a)) { 19 | throw new Error('Unit test failed for is_tril'); 20 | } 21 | } -------------------------------------------------------------------------------- /is_triu.hhs: -------------------------------------------------------------------------------- 1 | /** 2 | * @author Jason Reynolds 3 | * 4 | * @param input - matrix to determine if its upper triangular or not 5 | * @returns - a boolean value true or false depending if its upper triangular or not 6 | * 7 | * Function : is_triu - a boolean function to determine if a square matrix is upper triangular or not. Counter part to is_tril (lower triangular) 8 | * Done by cutting a square portion of the upper triangular matrix and then checking the upper triangular explicit entries 9 | * 10 | */ 11 | 12 | 13 | 14 | 15 | function is_triu(input) { 16 | 17 | *import math: ndim 18 | *import math: deep_copy 19 | 20 | if (arguments.length === 0) { 21 | throw new Error('Exception occurred in is_triu - no argument given'); 22 | } 23 | else if (arguments.length > 1) { 24 | throw new Error('Exception occurred in is_triu - wrong argument number'); 25 | } 26 | 27 | if (!(Array.isArray(input)) && !(input instanceof Mat) && !(input instanceof Tensor)) { 28 | throw new Error('Exception occurred in is_triu - input must be a 2-dimensional JS array, matrix or tensor'); 29 | } 30 | 31 | //declare raw_in to be input.clone() if Mat otherwise input itself 32 | let in_type = (input instanceof Mat) || (input instanceof Tensor); 33 | let raw_in = (in_type) ? input.clone() : deep_copy(input); 34 | 35 | //degrade raw_in to JS array for generalized code 36 | if (in_type) { 37 | raw_in = raw_in.val; 38 | } 39 | 40 | //check: any dimension 0? square matrix? 2d? if so, throw error 41 | if (!(ndim(raw_in) === 2) || raw_in.length === 0 || raw_in[0].length === 0 || !(raw_in.length === raw_in[0].length)) { 42 | throw new Error('Exception occurred in is_triu - wrong dimensions and need a square matrix as input'); 43 | } 44 | 45 | //iterate through array and if you spot a non-zero entry in a spot in the upper triangular region, return false 46 | //cut a sub matrix in the upper triangular region 47 | 48 | //reason being dont want to copy the whole array if its large, theres definitely more efficient ways to do it 49 | for (let k = 0; k < raw_in.length - 1; k++) { 50 | for (let h = 1; h < raw_in.length; h++) { 51 | //when in the tringaular zone exactly 52 | if (h > k) { 53 | if (!(raw_in[h][k] === 0)) { 54 | return false; 55 | } 56 | } 57 | } 58 | } 59 | //if it goes through the whole loop just fine then all the upper triangular entries are zero 60 | return true; 61 | } 62 | -------------------------------------------------------------------------------- /is_triu_test.hhs: -------------------------------------------------------------------------------- 1 | /** 2 | * @author Jason Reynolds 3 | * 4 | * @param 5 | * @returns 6 | * 7 | * Function for testing the is_tril.hhs function 8 | */ 9 | 10 | 11 | 12 | function is_triu_test() { 13 | 14 | *import math: is_triu 15 | 16 | let a = new Mat([[3, 2, 1], [0, 1, 3], [0, 0, 2]]); 17 | if (!is_triu(a)) { 18 | throw new Error('Unit test failed for is_tril'); 19 | } 20 | } -------------------------------------------------------------------------------- /lsolve.hhs: -------------------------------------------------------------------------------- 1 | /** 2 | * @author Jason Reynolds 3 | * 4 | * @param inputM - the matrix part of the equation Ax=b to be solved for x. Is strictly lower triangular in lsolve (see lusolve for other). Note lower triangular 5 | * implies it is also square 6 | * @param input_col - the column part, b, of the equation Ax = b, to be solved for x. Has to be of size inputM[0].length 7 | * @returns - Matrix that has dimensions A.length x 1 (column vector) with the answers to the linear equation systems ie it is unknown x in Ax=b 8 | * 9 | * Function similar to lusolve and usolve - it solves an nxn matrix exquation Ax = b for x. May work faster but only allows lower triangular matrices as input 10 | */ 11 | 12 | 13 | function lsolve(inputM, input_col) { 14 | 15 | *import math: is_tril 16 | *import math: ndim 17 | *import math: deep_copy 18 | 19 | if (arguments.length === 0) { 20 | throw new Error('Exception occurred in lsolve - no argument given'); 21 | } 22 | else if (arguments.length !== 2) { 23 | throw new Error('Exception occurred in lsolve - wrong argument number'); 24 | } 25 | 26 | if (!(Array.isArray(inputM)) && !(inputM instanceof Mat) && !(inputM instanceof Tensor)) { 27 | throw new Error('Exception occurred in lsolve - argument[0] must be a 2-dimensional JS array, matrix or tensor'); 28 | } 29 | 30 | if (!(Array.isArray(input_col)) && !(input_col instanceof Mat) && !(input_col instanceof Tensor)) { 31 | throw new Error('Exception occurred in lsolve - argument[1] must be a 2-dimensional JS array, matrix or tensor'); 32 | } 33 | 34 | //declare raw_in and raw_in_col : they are either clones of inputM and input_col respectively if Mat objects or inputM, input_col themselves otherwise 35 | let in_type_M = (inputM instanceof Mat) || (inputM instanceof Tensor); 36 | let raw_in = (in_type_M) ? inputM.clone() : deep_copy(inputM); 37 | let in_type_col = (input_col instanceof Mat) || (input_col instanceof Tensor); 38 | let raw_in_col = (in_type_col) ? input_col.clone() : deep_copy(input_col); 39 | 40 | //if any input is Mat, degrade to JS array for generalized code 41 | if (in_type_M) { 42 | raw_in = raw_in.val; 43 | } 44 | if (in_type_col instanceof Mat) { 45 | raw_in_col = raw_in_col.val; 46 | } 47 | 48 | if (ndim(raw_in) !== 2) { 49 | throw new Error('Exception occurred in lsolve - argument[0] must be 2-dimensional'); 50 | } 51 | if (ndim(raw_in_col) !== 2) { 52 | throw new Error('Exception occurred in lsolve - argument[1] must be 2-dimensional'); 53 | } 54 | 55 | //make sure that the input array is lower triangular (this checks: 2d, 0 length dims, square) 56 | if (!(is_tril(raw_in))) { 57 | throw new Error('Exception occurred in lsolve - non lower triangular matrix in argument[0]'); 58 | } 59 | //Double check. Not sure if this is good practice or not? Technically only need the last check for column vector check 60 | //check: 0 dims? square? column length === row/col length of matrix? if not throw error 61 | if (raw_in.length === 0 || raw_in[0].length === 0 || !(raw_in.length === raw_in[0].length) || !(raw_in_col.length === raw_in.length)) { 62 | throw new Error('Exception occurred in lsolve - wrong dimensions and need nxn matrix, may want to try lusolveAll or check column vector'); 63 | } 64 | 65 | //check: is column not 2d? (still is 2d when its nx1) is it not a -column- vector? that is, does row have more than 1 entry? if so, throw error 66 | if (!(ndim(raw_in_col) === 2) || !(raw_in_col[0].length === 1)) { 67 | throw new Error('Exception occurred in lsolve - column vector is not of correct input and make sure it is a column and not row vector, of the form, [[n1],[n2],...,[n]]'); 68 | } 69 | 70 | //declare the result to be the output of lsolve 71 | let result = mathjs.lsolve(raw_in, raw_in_col); 72 | //return it as a Mat object 73 | return new Mat(result); 74 | } 75 | -------------------------------------------------------------------------------- /lsolve_test.hhs: -------------------------------------------------------------------------------- 1 | /** 2 | * @author Jason Reynolds 3 | * 4 | * @param 5 | * @returns 6 | * 7 | * Function for unit testing lsolve (solve linear equation system with lower triangular matrix) 8 | */ 9 | 10 | 11 | 12 | 13 | 14 | function lsolve_test() { 15 | 16 | *import math: lsolve 17 | 18 | let a = new Mat([[3, 0, 0], [3, 2, 0], [1, 4, 1]]); 19 | let b = [[3], [4], [5]]; 20 | 21 | if (!(lsolve(a, b) === [[1], [0.5], [2]])) { 22 | throw new Error('Unit test failed for lusolve'); 23 | } 24 | } -------------------------------------------------------------------------------- /lu.hhs: -------------------------------------------------------------------------------- 1 | /** 2 | * @author Jason Reynolds 3 | * @param input - the matrix to be decomposed into L, U and p 4 | * @returns - 3 objects of type Mat - L, U and p. A lower triangular matrix, upper triangular and p 5 | */ 6 | 7 | 8 | 9 | 10 | //takes in raw array or Mat object and return L, U, p as decomposed objects 11 | function lu(input) { 12 | 13 | *import math: ndim 14 | *import math: deep_copy 15 | 16 | if (arguments.length === 0) { 17 | throw new Error('Exception occurred in lu - no argument given'); 18 | } 19 | else if (arguments.length > 1) { 20 | throw new Error('Exception occurred in lu - wrong argument number'); 21 | } 22 | 23 | if (!(Array.isArray(input)) && !(input instanceof Mat) && !(input instanceof Tensor)) { 24 | throw new Error('Exception occurred in lu - input must be a 2-dimensional JS array, matrix or tensor'); 25 | } 26 | 27 | //declare raw_in as input.clone() if Mat otherwise input 28 | let in_type = (input instanceof Mat) || (input instanceof Tensor); 29 | let raw_in = (in_type) ? input.clone() : deep_copy(input); 30 | 31 | //when a Mat, degrade to JS array 32 | if (in_type) { 33 | raw_in = raw_in.val; 34 | } 35 | 36 | //check: is it a vector? if so, throw error 37 | if (!(ndim(raw_in) === 2)) { 38 | throw new Error('Exception occurred in lu - not a 2d matrix'); 39 | } 40 | if (raw_in.length < 2 || raw_in[0].length < 2) { 41 | throw new Error('Exception occurred in lu - wrong dimensions, make sure it\'s a matrix'); 42 | } 43 | 44 | //mathjs lup on the raw, save as result 45 | let result = mathjs.lup(raw_in); 46 | //return the L, U and p objects as Mats 47 | return { 48 | L: mat(result.L), 49 | U: mat(result.U), 50 | p: mat(result.p) 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /lu_test.hhs: -------------------------------------------------------------------------------- 1 | /** 2 | * @author Jason Reynolds 3 | * @param 4 | * @returns 5 | * 6 | * Function thats a unit test for the lu function/wrapper 7 | */ 8 | 9 | 10 | 11 | function lu_test() { 12 | 13 | *import math: lu 14 | 15 | let a = [[1, 2], [4, 5]]; 16 | if (!(lu(a).L === [[1, 0], [0.25, 1]]) || 17 | !(lu(a).U === [[4, 5], [0, 0.75]]) || 18 | !(lu(a).p === [[1, 0]])) { 19 | throw new Error('LU unit test failed.Note there may be multiple answers to 1 solution by different factors.'); 20 | } 21 | 22 | } -------------------------------------------------------------------------------- /lusolve.hhs: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * @author Jason Reynolds 4 | * @param - inputM the matrix in the equation Ax=b, namely A 5 | * @param - input_col the column vector in the equation Ax = b, namely b 6 | * @returns - the matrix answer , x, to the equation Ax=b using lu decomposition 7 | * 8 | * Function that uniquely solves the equation Ax = b for a square matrix/Mat A and column vector b 9 | */ 10 | 11 | 12 | function lusolve(inputM, input_col) { 13 | 14 | *import math: deep_copy 15 | *import math: ndim 16 | 17 | if (arguments.length === 0) { 18 | throw new Error('Exception occurred in lusolve - no argument given'); 19 | } 20 | else if (arguments.length !== 2) { 21 | throw new Error('Exception occurred in lusolve - wrong argument number'); 22 | } 23 | 24 | if (!(Array.isArray(inputM)) && !(inputM instanceof Mat) && !(inputM instanceof Tensor)) { 25 | throw new Error('Exception occurred in lusolve - argument[0] must be a 2-dimensional JS array, matrix or tensor'); 26 | } 27 | 28 | if (!(Array.isArray(input_col)) && !(input_col instanceof Mat) && !(input_col instanceof Tensor)) { 29 | throw new Error('Exception occurred in lusolve - argument[1] must be a 2-dimensional JS array, matrix or tensor'); 30 | } 31 | 32 | 33 | //declare raw_in, raw_in_col as input.clone() if Mat otherwise, inputM, input_col themselves 34 | let in_type_M = (inputM instanceof Mat) || (inputM instanceof Tensor); 35 | let raw_in = (in_type_M) ? inputM.clone() : deep_copy(inputM); 36 | let in_type_col = (input_col instanceof Mat) || (input_col instanceof Tensor); 37 | let raw_in_col = (in_type_col) ? input_col.clone() : deep_copy(input_col); 38 | 39 | //if any input is Mat, degrade to JS array 40 | if (in_type_M) { 41 | raw_in = raw_in.val; 42 | } 43 | if (in_type_col) { 44 | raw_in_col = raw_in_col.val; 45 | } 46 | 47 | if (ndim(raw_in) !== 2) { 48 | throw new Error('Exception occurred in lusolve - argument[0] must be 2-dimensional'); 49 | } 50 | if (ndim(raw_in_col) !== 2) { 51 | throw new Error('Exception occurred in lusolve - argument[1] must be 2-dimensional'); 52 | } 53 | 54 | //check: 0 dims? not square? column vector not equal to length of matrix row length? (if input is m x n, b is m x 1, while solution is n x 1 55 | if (raw_in.length === 0 || raw_in[0].length === 0 || !(raw_in.length === raw_in[0].length) || !(raw_in_col.length === raw_in.length)) { 56 | throw new Error('Exception occurred in lusolve - wrong dimensions and need nxn matrix, may want to try lusolveAll or check column vector'); 57 | } 58 | //define the result to be the lusolve output ie the solution 59 | let result = mathjs.lusolve(raw_in, raw_in_col); 60 | //return it as a Mat object 61 | return new Mat(result); 62 | } 63 | -------------------------------------------------------------------------------- /lusolve_test.hhs: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * @author Jason Reynolds 4 | * @param 5 | * @returns 6 | * 7 | * Function that's a unit test for lusolve.hhs. Tests if it gives the correct answer and if it accepts both array and Mat object 8 | */ 9 | 10 | 11 | 12 | function lusolve_test() { 13 | 14 | *import math: lusolve 15 | 16 | let a = new Mat([[2, 1], [3, 4]]); 17 | let b = [[3], [3]]; 18 | 19 | if (!(lusolve(a, b) === [[1.8], [-0.6]])) { 20 | throw new Error('Unit test failed for lusolve'); 21 | } 22 | } -------------------------------------------------------------------------------- /magic_test.hhs: -------------------------------------------------------------------------------- 1 | 2 | function magic_test() 3 | { 4 | *import math:magic 5 | 6 | function check_magic(input) 7 | { 8 | let magic_matrix = magic(input).clone().val; 9 | 10 | let sumd1 = 0; 11 | let sumd2 = 0; 12 | for (let i = 0; i < input; i++) 13 | { 14 | // (i, i) is the diagonal from top-left -> bottom-right 15 | // (i, input - i - 1) is the diagonal from top-right -> bottom-left 16 | sumd1 = sumd1 + magic_matrix[i][i]; 17 | sumd2 = sumd2 + magic_matrix[i][input-1-i]; 18 | } 19 | 20 | // // if the two diagonal sums are unequal then it is not a magic square 21 | if(sumd1 !== sumd2) 22 | return false; 23 | 24 | // check if each row or column equal 25 | for (let i = 0; i < input; i++) { 26 | let colSum = 0; 27 | let rowSum = 0; 28 | for (let j = 0; j < input; j++) 29 | { 30 | rowSum += magic_matrix[i][j]; 31 | colSum += magic_matrix[j][i]; 32 | } 33 | 34 | if (rowSum != colSum || colSum != sumd1) 35 | return false; 36 | } 37 | // print(input); 38 | // print("magic passed."); 39 | return true; 40 | } 41 | 42 | for(let i = 3; i <= 20; i++) 43 | { 44 | if(!check_magic(i)) 45 | { 46 | throw new Error("Failed unit test for magic function."); 47 | } 48 | } 49 | // print("all test passed."); 50 | } -------------------------------------------------------------------------------- /max.hhs: -------------------------------------------------------------------------------- 1 | /** 2 | * @author Jason Reynolds 3 | * @param input - the structure to find the maximum value of. Can be a list, 1d array, 2d array, Mat, Tensor, number, etc 4 | * max(1, 2, 3) or max([1, 2, 3]) 5 | * @returns - a number that is the maximum value in the structure 6 | * 7 | * Function that finds the maximum value of a list, matrix or general matrix like structure 8 | * 9 | */ 10 | 11 | 12 | 13 | function max(input) { 14 | 15 | *import math: is_number 16 | *import math: flatten 17 | 18 | if (arguments.length === 0) { 19 | throw new Error('Exception occurred in max - no argument given'); 20 | } 21 | 22 | // max(1, 2, 3) 23 | if (arguments.length > 1) { 24 | let result = arguments[0]; 25 | for (let i = 0; i < arguments.length; i++) { 26 | if (!is_number(arguments[i])) { 27 | throw new Error('Exception occurred in max - only numbers can be compared'); 28 | } 29 | 30 | if (result < arguments[i]) { 31 | result = arguments[i]; 32 | } 33 | } 34 | return result; 35 | } 36 | 37 | if (is_number(input)) { 38 | return input; 39 | } 40 | 41 | if (!(Array.isArray(input)) && !(input instanceof Mat) && !(input instanceof Tensor)) { 42 | throw new Error('Exception occurred in max - input can only be number, JS array, matrix or tensor'); 43 | } 44 | 45 | //declare raw_in as the clone of input if a Mat/Tensor otherwise, input itself 46 | let in_type = (input instanceof Mat || input instanceof Tensor); 47 | let raw_in = (in_type) ? input.clone().val : input; 48 | // let raw_in be a 1-d array 49 | raw_in = flatten(raw_in); 50 | 51 | // in case raw_in === [] 52 | if (raw_in.length === 0) { 53 | return 0; 54 | } 55 | let result = raw_in[0]; 56 | for (let i = 0; i < raw_in.length; i++) { 57 | if (!is_number(raw_in[i])) { 58 | throw new Error('Exception occurred in max - only numbers can be compared'); 59 | } 60 | 61 | if (result < raw_in[i]) { 62 | result = raw_in[i]; 63 | } 64 | } 65 | return result; 66 | } 67 | -------------------------------------------------------------------------------- /mean.hhs: -------------------------------------------------------------------------------- 1 | /** 2 | * @author Jianan Lin (林家南) 3 | * @param input - the list, matrix, tensor or structure that you want to get the mean(average) of 4 | * @returns - a number representing the mean of all the elements of a list, matrix, tensor or other structure 5 | * 6 | */ 7 | 8 | 9 | function mean(input) { 10 | 11 | *import math: is_number 12 | *import math: flatten 13 | *import math: ndim 14 | *import math: deep_copy 15 | 16 | // if there are no arguments, there is an error 17 | if (arguments.length === 0) { 18 | throw new Error('No argument given'); 19 | } 20 | 21 | // if the first argument is a number, then we should get the mean of a list of numbers 22 | if (is_number(arguments[0])) { 23 | 24 | // determine whether there is invalid element 25 | for (let i = 0; i < arguments.length; i++) { 26 | if (!is_number(arguments[i])) { 27 | throw new Error('First argument suggests a list, but there is a non number element in arguments.'); 28 | } 29 | } 30 | 31 | // sum_array is the sum, n is the number, result is sum / n 32 | let sum_array = 0, n = 0; 33 | for (let i = 0; i < arguments.length; i++) { 34 | sum_array += arguments[i]; 35 | n++; 36 | } 37 | 38 | let result = sum_array / n; 39 | return result; 40 | } 41 | 42 | // at this time, we do not accept arguments = [matrix, axis] 43 | // we will add this function in the future 44 | if (!arguments.length === 1) { 45 | throw new Error('Wrong arguments'); 46 | } 47 | 48 | // now we're dealing with non-numbers and matrix-like structures: 49 | // declare raw_in, set it to a input clone if its a Matrix / Tensor otherwise input itself 50 | let in_type = (input instanceof Mat) || (input instanceof Tensor); 51 | // here we always maintain raw_in to be an array 52 | let raw_in = (in_type) ? input.clone().val : deep_copy(input); 53 | // declare dimension of raw_in 54 | let dims = ndim(raw_in); 55 | 56 | // if the input is 1 or 2 dimension 57 | if (dims < 3) { 58 | 59 | // in the case dimension = 1, e.g. [1, 2, 3, 4] 60 | if (dims === 1) { 61 | let sum_array = 0, n = 0; 62 | for (let i = 0; i < raw_in.length; i++) { 63 | sum_array += raw_in[i]; 64 | n++; 65 | } 66 | let result = sum_array / n; 67 | return result; 68 | } 69 | 70 | // in the case dimension = 2, e.g. [[1, 2], [3, 4]] 71 | // also consider this: [[1, 2], [3], [4, 5, 6]] 72 | else if (dims === 2) { 73 | let sum_array = 0, n = 0; 74 | for (let i = 0; i < raw_in.length; i++) { 75 | for (let j = 0; j < raw_in[i].length; j++) { 76 | sum_array += raw_in[i][j]; 77 | n++; 78 | } 79 | } 80 | let result = sum_array / n; 81 | return result; 82 | } 83 | } 84 | 85 | // in the case dimension > 2, e.g. [[[1, 2], [3, 4]], [[5, 6], [7, 8]]] 86 | else if (dims > 2 && is_number(dims)) { 87 | raw_in = flatten(raw_in); 88 | let sum_array = 0, n = 0; 89 | for (let i = 0; i < raw_in.length; i++) { 90 | sum_array += raw_in[i]; 91 | n++; 92 | } 93 | let result = sum_array / n; 94 | return result; 95 | } 96 | 97 | else { 98 | throw new Error('Invalid dimension of the first argument'); 99 | } 100 | 101 | } 102 | -------------------------------------------------------------------------------- /mean_test.hhs: -------------------------------------------------------------------------------- 1 | /** 2 | * @author Jianan Lin (林家南) 3 | * Function of test unit for mean 4 | */ 5 | 6 | 7 | 8 | function mean_test() { 9 | 10 | *import math: mean 11 | 12 | let b = [5, 34, 3]; 13 | let c = [[3, 2, 3], [3, 4, 3]]; 14 | let d = new Mat([3, 4, 2]); 15 | let e = new Mat([[3, 4], [4, 3], [4, 3]]); 16 | let f = [[[2, 2], [3, 3], [4, 4]], [[2, 2], [3, 3], [4, 4]], [[4, 4], [3, 3], [2, 2]]]; 17 | let g = [[1, 2], [3], [4, 5, 6]]; 18 | let h = new Tensor([[[1, 2], [3, 4]], [[5, 6], [7, 8]]]); 19 | 20 | if (!mean(1, 2) === 1.5) { 21 | throw new Error('Failed unit test for numbers for mean.'); 22 | } 23 | 24 | if (!mean(b) === 14) { 25 | throw new Error('Failed unit test for Array for mean.'); 26 | } 27 | 28 | if (!mean(c) === 3) { 29 | throw new Error('Failed unit test for Array for mean.'); 30 | } 31 | 32 | if (!mean(d) === 3) { 33 | throw new Error('Failed unit test for Matrix for mean.'); 34 | } 35 | 36 | if (!mean(e) === 3.5) { 37 | throw new Error('Failed unit test for Matrix for mean.'); 38 | } 39 | 40 | if (!mean(f) === 3) { 41 | throw new Error('Failed unit test for Array for mean.'); 42 | } 43 | 44 | if (!mean(g) === 3.5) { 45 | throw new Error('Failed unit test for Array for mean.'); 46 | } 47 | 48 | if (!mean(h) === 4.5) { 49 | throw new Error('Failed unit test for Tensor for mean.'); 50 | } 51 | 52 | //print("Mean test pass!"); 53 | } 54 | -------------------------------------------------------------------------------- /median_test.hhs: -------------------------------------------------------------------------------- 1 | /** 2 | * @author Jianan Lin (林家南) 3 | * Function for unit test for median 4 | */ 5 | 6 | 7 | 8 | function median_test() { 9 | 10 | *import math: median 11 | 12 | let b = [5, 34, 3]; 13 | let c = [[3, 2, 3], [3, 4, 3]]; 14 | let d = new Mat([3, 4, 2]); 15 | let e = new Mat([[3, 4], [4, 3], [4, 3]]); 16 | let f = [[[2, 2], [3, 3], [4, 4]], [[2, 2], [3, 3], [4, 4]], [[4, 4], [3, 3], [2, 2]]]; 17 | let g = [[1, 2], [3], [4, 5, 6]]; 18 | let h = new Tensor([[[1, 2], [3, 4]], [[5, 6], [7, 8]]]); 19 | 20 | if (!median(1, 2) === 1.5) { 21 | throw new Error('Failed unit test for numbers for median.'); 22 | } 23 | 24 | if (!median(b) === 5) { 25 | throw new Error('Failed unit test for Array for median.'); 26 | } 27 | 28 | if (!median(c) === 3) { 29 | throw new Error('Failed unit test for Array for median.'); 30 | } 31 | 32 | if (!median(d) === 3) { 33 | throw new Error('Failed unit test for Matrix for median.'); 34 | } 35 | 36 | if (!median(e) === 3.5) { 37 | throw new Error('Failed unit test for Matrix for median.'); 38 | } 39 | 40 | if (!median(f) === 3) { 41 | throw new Error('Failed unit test for Array for median.'); 42 | } 43 | 44 | if (!median(g) === 3.5) { 45 | throw new Error('Failed unit test for Array for median.'); 46 | } 47 | 48 | if (!median(h) === 4.5) { 49 | throw new Error('Failed unit test for Tensor for median.'); 50 | } 51 | 52 | //print("Median test pass!"); 53 | } 54 | -------------------------------------------------------------------------------- /min.hhs: -------------------------------------------------------------------------------- 1 | /** 2 | * @OriginalAuthor Jason Reynolds 3 | * @modifiedBy Blake Wang 4 | * @param input - the structure to find the minimum value of. Can be a list, 1d array, 2d array, Mat, Tensor, number, etc 5 | * min(1, 2, 3) or max([1, 2, 3]) 6 | * @returns - a number that is the minimum value in the structure 7 | * 8 | * Function that finds the minimum value of a list, matrix or general matrix like structure 9 | * 10 | */ 11 | 12 | 13 | 14 | function min(input) { 15 | 16 | *import math: is_number 17 | *import math: flatten 18 | 19 | if (arguments.length === 0) { 20 | throw new Error('Exception occurred in min - no argument given'); 21 | } 22 | 23 | // min(1, 2, 3) 24 | if (arguments.length > 1) { 25 | let result = arguments[0]; 26 | for (let i = 0; i < arguments.length; i++) { 27 | if (!is_number(arguments[i])) { 28 | throw new Error('Exception occurred in min - only numbers can be compared'); 29 | } 30 | 31 | if (result > arguments[i]) { 32 | result = arguments[i]; 33 | } 34 | } 35 | return result; 36 | } 37 | 38 | if (is_number(input)) { 39 | return input; 40 | } 41 | 42 | if (!(Array.isArray(input)) && !(input instanceof Mat) && !(input instanceof Tensor)) { 43 | throw new Error('Exception occurred in min - input can only be number, JS array, matrix or tensor'); 44 | } 45 | 46 | //declare raw_in as the clone of input if a Mat/Tensor otherwise, input itself 47 | let in_type = (input instanceof Mat || input instanceof Tensor); 48 | let raw_in = (in_type) ? input.clone().val : input; 49 | // let raw_in be a 1-d array 50 | raw_in = flatten(raw_in); 51 | 52 | // in case raw_in === [] 53 | if (raw_in.length === 0) { 54 | return 0; 55 | } 56 | let result = raw_in[0]; 57 | for (let i = 0; i < raw_in.length; i++) { 58 | if (!is_number(raw_in[i])) { 59 | throw new Error('Exception occurred in min - only numbers can be compared'); 60 | } 61 | 62 | if (result > raw_in[i]) { 63 | result = raw_in[i]; 64 | } 65 | } 66 | return result; 67 | } 68 | -------------------------------------------------------------------------------- /mink.hhs: -------------------------------------------------------------------------------- 1 | /** 2 | * @author Jing Stone 3 | * @param input - list or two dimensional matrix, the smallest k numbers that you want, the direction of matrix(column or row) 4 | * @returns - if A is a list, return a list contain the k smallest elements of a 5 | * if A is a matrix, return a matrix whose columns or rows contain the k smallest elements of each columns or rows of A. 6 | * return a Mat object 7 | * dim = 1, column; dim = 2, row 8 | * 9 | */ 10 | 11 | 12 | function mink(input, k = 1, dim = 1) 13 | { 14 | *import math: ndim 15 | *import math: deep_copy 16 | *import math: transpose 17 | 18 | if (arguments.length === 0) { 19 | throw new Error('Exception occurred in mink - no argument given'); 20 | } 21 | 22 | if (!(Array.isArray(input)) && !(input instanceof Mat) && !(input instanceof Tensor)) { 23 | throw new Error('Exception occurred in mink - argument[0] must be an array, matrix or tensor'); 24 | } 25 | 26 | if (arguments.length >= 2) { 27 | if (!(typeof k === 'number') || k <= 0 || parseInt(k) !== k) { 28 | throw new Error('Exception occurred in mink - argument[1] must be a positive integer'); 29 | } 30 | } 31 | 32 | if (arguments.length > 3) { 33 | throw new Error('Exception occurred in mink - wrong argument given'); 34 | } 35 | 36 | //declare raw_in to be input clone if Mat and input deep copy otherwise 37 | let in_type = (input instanceof Mat || input instanceof Tensor); 38 | //if input is Mat degrade to JS array to have the below code work for both JS arrays and Mat objects 39 | let raw_in = (in_type) ? input.clone().val : deep_copy(input); 40 | //only support 1dim and 2dims 41 | let dims = ndim(raw_in); 42 | 43 | // if is list, use built-in sort function to get the k smallest elements 44 | if(dims === 1) 45 | { 46 | raw_in.sort((a, b) => a - b); 47 | let result = []; 48 | 49 | k = k > raw_in.length ? raw_in.length : k; 50 | 51 | for(let i = 0; i < k; i++) 52 | { 53 | result.push(raw_in[i]); 54 | } 55 | return (new Mat(result)); 56 | } 57 | // for 2 dims, the direction determined by dim parameter 58 | else if(dims === 2) 59 | { 60 | //get k smallest elements of every row 61 | if(dim === 2) 62 | { 63 | let result = []; 64 | //in case k is larger than the column or row of the matrix 65 | k = k < raw_in[0].length ? k : raw_in[0].length; 66 | //sort every row and get the k smallest elements 67 | for(let i = 0; i < raw_in.length; i++) 68 | { 69 | let temp = []; 70 | raw_in[i].sort((a, b) => a - b); 71 | for(let j = 0; j < k; j++) 72 | { 73 | temp.push(raw_in[i][j]); 74 | } 75 | result[i] = temp; 76 | } 77 | return (new Mat(result)); 78 | } 79 | else if(dim === 1) 80 | { 81 | let result = []; 82 | k = k < raw_in.length ? k : raw_in.length; 83 | for(let i = 0; i < raw_in[0].length; i++) 84 | { 85 | // store every column of matrix 86 | let temp_1 = []; 87 | // store the k smallest elements 88 | let temp_2 = []; 89 | for(let j = 0; j < raw_in.length; j++) 90 | { 91 | temp_1.push(raw_in[j][i]); 92 | } 93 | temp_1.sort((a, b) => a-b); 94 | for(let p = 0; p < k; p++) 95 | { 96 | temp_2.push(temp_1[p]); 97 | } 98 | result.push(temp_2); 99 | } 100 | return transpose(result); 101 | } 102 | else 103 | { 104 | throw new Error('Exception occured in mink - dim can only be 1 or 2'); 105 | } 106 | } 107 | else 108 | { 109 | throw new Error('Exception occured in mink - only support 1d list or 2d matrix'); 110 | } 111 | return null; 112 | } 113 | -------------------------------------------------------------------------------- /mink_test.hhs: -------------------------------------------------------------------------------- 1 | /** 2 | * @author Jing Stone 3 | * Unit test for mink function 4 | */ 5 | 6 | function mink_test() 7 | { 8 | *import math: mink 9 | 10 | let A = [[3, 2, 1, 4, 7], [2, 7, 0, 5, 2], [1, 9, 4, 3, 0], [2, 3, 4, 5, 6]]; 11 | // A-num-num means the 3 parameters of mink 12 | const A_3_1 = [[1, 2, 0, 3, 0], 13 | [2, 3, 1, 4, 2], 14 | [2, 7, 4, 5, 6]]; 15 | const A_3_2 = [[1, 2, 3], 16 | [0, 2, 2], 17 | [0, 1, 3], 18 | [2, 3, 4]]; 19 | const A_4_1 = [[1, 2, 0, 3, 0], 20 | [2, 3, 1, 4, 2], 21 | [2, 7, 4, 5, 6], 22 | [3, 9, 4, 5, 7]]; 23 | const A_4_2 = [[1, 2, 3, 4], 24 | [0, 2, 2, 5], 25 | [0, 1, 3, 4], 26 | [2, 3, 4, 5]]; 27 | 28 | function test(input, k, dim, result) 29 | { 30 | let output = mink(input, k, dim); 31 | if(output instanceof Mat) 32 | { 33 | output = output.clone().val; 34 | } 35 | if(!(output.join("") === result.join(""))) 36 | { 37 | throw new Error('Unit test failed for mink.') 38 | } 39 | } 40 | 41 | test(A, 3, 1, A_3_1); 42 | test(A, 3, 2, A_3_2); 43 | test(A, 4, 1, A_4_1); 44 | test(A, 4, 2, A_4_2); 45 | //print("mink test passed!"); 46 | } -------------------------------------------------------------------------------- /missing.hhs: -------------------------------------------------------------------------------- 1 | /** 2 | * @Author Jianan Lin (林家南) 3 | * @param input: a 1-2d JS array / matrix / tensor 4 | * @param dim - 0 means row and 1 means column 5 | * @param times - how many rows / columns created 6 | * @returns - input with extra blank (missing) rows / columns 7 | * According to the previous value, number - NaN, string - "", 8 | * boolean - false, others - undefined (maybe null is also OK) 9 | * 10 | */ 11 | 12 | 13 | 14 | function missing(input, dim = 1, times = 1) { 15 | 16 | *import math: ndim 17 | *import math: deep_copy 18 | 19 | // argument check 20 | if (arguments.length === 0) { 21 | throw new Error('Exception occurred in missing - no argument given'); 22 | } 23 | if (arguments.length > 3) { 24 | throw new Error('Exception occurred in missing - wrong argument number'); 25 | } 26 | 27 | // type check 28 | if (!(Array.isArray(input)) && !(input instanceof Mat) && !(input instanceof Tensor)) { 29 | throw new Error('Exception occurred in missing - argument[0] must be JS array, matrix or tensor'); 30 | } 31 | if (!(typeof times === 'number') || times <= 0 || parseInt(times) !== times) { 32 | throw new Error('Exception occurred in missing - argument[2] must be a positive integer'); 33 | } 34 | if (dim !== 0 && dim !== 1) { 35 | throw new Error('Exception occurred in missing - argument[1] must be 0 or 1'); 36 | } 37 | 38 | let in_type = input instanceof Mat || input instanceof Tensor; 39 | let raw_in = in_type ? input.clone().val : deep_copy(input); 40 | let size = ndim(raw_in); 41 | if (size > 2) { 42 | throw new Error('Exception occurred in missing - argument[0] must be no more than 2-dimensional'); 43 | } 44 | 45 | if (size === 1) { 46 | if (dim === 1) { 47 | if (raw_in.length === 0) { 48 | for (let i = 0; i < times; i++) { 49 | raw_in.push(NaN); 50 | } 51 | } 52 | else { 53 | let temp = raw_in[raw_in.length - 1]; 54 | for (let i = 0; i < times; i++) { 55 | let result = getMiss(temp); 56 | raw_in.push(result); 57 | } 58 | } 59 | } 60 | 61 | else { 62 | if (raw_in.length === 0) { 63 | for (let i = 0; i < times; i++) { 64 | raw_in.push([NaN]); 65 | } 66 | } 67 | else { 68 | let temp = deep_copy(raw_in); 69 | for (let i = 0; i < raw_in.length; i++) { 70 | temp[i] = getMiss(raw_in[i]); 71 | } 72 | raw_in = [raw_in]; 73 | for (let i = 0; i < times; i++) { 74 | raw_in.push(temp); 75 | } 76 | } 77 | } 78 | } 79 | 80 | // size == 2 81 | else { 82 | if (dim === 1) { 83 | if (raw_in[0].length === 0) { 84 | for (let i = 0; i < raw_in.length; i++) { 85 | for (let j = 0; j < times; j++) { 86 | raw_in[i].push(NaN); 87 | } 88 | } 89 | } 90 | 91 | else { 92 | for (let i = 0; i < raw_in.length; i++) { 93 | let temp = raw_in[i][raw_in[i].length - 1]; 94 | let result = getMiss(temp); 95 | for (let j = 0; j < times; j++) { 96 | raw_in[i].push(result); 97 | } 98 | } 99 | } 100 | } 101 | 102 | // dim === 0 103 | else { 104 | if (raw_in[0].length === 0) { 105 | for (let j = 0; j < times; j++) { 106 | raw_in.push([]); 107 | } 108 | } 109 | 110 | else { 111 | let temp = []; 112 | let result = []; 113 | for (let j = 0; j < raw_in[raw_in.length - 1].length; j++) { 114 | temp.push(raw_in[raw_in.length - 1][j]); 115 | result.push(getMiss(temp[j])); 116 | } 117 | for (let i = 0; i < times; i++) { 118 | raw_in.push(result); 119 | } 120 | } 121 | } 122 | } 123 | return mat(raw_in); 124 | 125 | function getMiss(x) { 126 | if (x || x === 0) { 127 | if (typeof x === 'number') { 128 | return NaN; 129 | } 130 | else if (typeof x === 'string') { 131 | return ""; 132 | } 133 | else if (typeof x === 'boolean') { 134 | return false; 135 | } 136 | else { 137 | return undefined; 138 | } 139 | } 140 | // there is a bug, typeof NaN will case an error 141 | else { 142 | return x; 143 | } 144 | } 145 | 146 | } 147 | -------------------------------------------------------------------------------- /missing_test.hhs: -------------------------------------------------------------------------------- 1 | /** 2 | * @author Jianan Lin 3 | * Unit test for missing 4 | * 5 | */ 6 | 7 | 8 | 9 | function missing_test() { 10 | 11 | *import math: missing 12 | 13 | let a = [1, 2, 3, NaN]; 14 | let b = missing(a); 15 | // b = [1, 2, 3, NaN, NaN] 16 | if (b.val[0][4] === b.val[0][4]) { 17 | throw new Error('Unit test failed for missing'); 18 | } 19 | 20 | let c = ['1', '2', '3']; 21 | let d = missing(c, 0, 2); 22 | if (!(d === mat([['1', '2', '3'], ['', '', ''], ['', '', '']]))) { 23 | throw new Error('Unit test failed for missing'); 24 | } 25 | 26 | //print('missing test pass'); 27 | } 28 | -------------------------------------------------------------------------------- /multinomial.hhs: -------------------------------------------------------------------------------- 1 | /** 2 | * @author Jianan Lin (林家南) 3 | * @param input - a series of numbers a1, ... an, or an array / matrix / tensor 4 | * @returns - a number = (a1 + ... + an)! / (a1! * ... * an!) 5 | * 6 | */ 7 | 8 | 9 | function multinomial(input) { 10 | 11 | *import math: is_number 12 | *import math: flatten 13 | *import math: deep_copy 14 | 15 | let length = arguments.length; 16 | 17 | // if there are no arguments, there is an error 18 | if (length === 0) { 19 | throw new Error('Exception occurred in multinomial - no argument given'); 20 | } 21 | 22 | // if we input a series of numbers 23 | if (is_number(input)) { 24 | for (let i = 0; i < length; i++) { 25 | if (arguments[i] < 0 || parseInt(arguments[i]) !== arguments[i]) { 26 | throw new Error('Exception occurred in multinomial - arguments must be non-negative integers'); 27 | } 28 | } 29 | 30 | let sum = 0; 31 | for (let i = 0; i < length; i++) { 32 | sum += arguments[i]; 33 | } 34 | 35 | let result = private_factorial(sum); 36 | for (let i = 0; i < length; i++) { 37 | result /= private_factorial(arguments[i]); 38 | } 39 | 40 | return result; 41 | } 42 | 43 | else { 44 | if (length !== 1) { 45 | throw new Error('Exception occurred in multinomial - wrong argument number'); 46 | } 47 | 48 | if ( !(Array.isArray(arguments[0])) && !(arguments[0] instanceof Mat) && !(arguments[0] instanceof Tensor)) { 49 | throw new Error('Exception occurred in multinomial - argument is of length 1, not a number, but is not a Mat, Tensor or JS Array'); 50 | } 51 | 52 | let in_type = input instanceof Mat || input instanceof Tensor; 53 | let raw_in = in_type ? input.clone().val : deep_copy(input); 54 | raw_in = flatten(raw_in); 55 | 56 | let array_length = raw_in.length; 57 | for (let i = 0; i < array_length; i++) { 58 | if (raw_in[i] < 0 || parseInt(raw_in[i]) !== raw_in[i]) { 59 | throw new Error('Exception occurred in multinomial - elements must be non-negative integers'); 60 | } 61 | } 62 | 63 | let sum = 0; 64 | for (let i = 0; i < array_length; i++) { 65 | sum += raw_in[i]; 66 | } 67 | 68 | let result = private_factorial(sum); 69 | for (let i = 0; i < array_length; i++) { 70 | result /= private_factorial(raw_in[i]); 71 | } 72 | 73 | return result; 74 | } 75 | 76 | // when we use this function, we have ensured that input is valid 77 | function private_factorial(input) { 78 | let result = 1; 79 | for (let i = 2; i <= input; i++) { 80 | result *= i; 81 | } 82 | return result; 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /multinomial_test.hhs: -------------------------------------------------------------------------------- 1 | /** 2 | * @author Jianan Lin (林家南) 3 | * Function of test unit for multinomial 4 | */ 5 | 6 | function multinomial_test() { 7 | 8 | *import math: multinomial 9 | 10 | if (multinomial(1, 2) !== 3) { 11 | throw new Error('Failed unit test for numbers for multinomial.'); 12 | } 13 | 14 | let a = [1, 2, 3]; 15 | if (multinomial(a) !== 60) { 16 | throw new Error('Failed unit test for array for multinomial.'); 17 | } 18 | 19 | let b = [[1, 2], [3]]; 20 | if (multinomial(b) !== 60) { 21 | throw new Error('Failed unit test for array for multinomial.'); 22 | } 23 | 24 | let c = mat([1, 2, 1]); 25 | if (multinomial(c) !== 12) { 26 | throw new Error('Failed unit test for matrix for multinomial.'); 27 | } 28 | 29 | let d = mat([[1, 2], [1]]); 30 | if (multinomial(d) !== 12) { 31 | throw new Error('Failed unit test for matrix for multinomial.'); 32 | } 33 | 34 | let e = new Tensor([2, 4]) 35 | if (multinomial(e) !== 15) { 36 | throw new Error('Failed unit test for tensor for multinomial.'); 37 | } 38 | 39 | let f = new Tensor([[2, 1], [2]]); 40 | if (multinomial(f) !== 30) { 41 | throw new Error('Failed unit test for tensor for multinomial.'); 42 | } 43 | 44 | //print("multinomial test pass"); 45 | } 46 | -------------------------------------------------------------------------------- /ndim.hhs: -------------------------------------------------------------------------------- 1 | /** 2 | * @author Jason Reynolds 3 | * @param - input - the matrix or tensor or list to enter to verify the dimension of 4 | * @returns a number, that is the dimension of the structure. for example a 2d array is 2 dimensions. a list is 1 dim, a 3d array (tensor) is 3 dim, etc 5 | * 6 | * Function that takes in a structure namely a list, matrix, or tensor and returns the dimension of said structure 7 | */ 8 | 9 | 10 | 11 | 12 | function ndim(input) { 13 | 14 | *import math: is_number 15 | *import math: shape 16 | *import math: deep_copy 17 | 18 | if (arguments.length !== 1) { 19 | throw new Error('Exception occurred in ndim - wrong argument number'); 20 | } 21 | 22 | //declare raw_in to be a clone of input or input itself depending on whether its a Mat/Tensor or not, respectively 23 | let in_type = (input instanceof Mat || input instanceof Tensor) 24 | let raw_in = (in_type) ? input.clone() : deep_copy(input); 25 | 26 | //if the input is... an array OR an instance of Mat OR an instance of Tensor (covers all grounds here) then do the algorithm 27 | if (Array.isArray(input) || (input instanceof Mat) || (input instanceof Tensor)) { 28 | 29 | //degrade to JS array if Tensor or Mat object 30 | 31 | /***not even this is necessary since shape() handles it***/ 32 | 33 | if (input instanceof Mat || input instanceof Tensor) { 34 | raw_in = raw_in.val; 35 | } 36 | 37 | return (shape(raw_in).length); 38 | /********* I think this is all unnecessary and we can simply have shape() which gives us the length of each dimension, and then find the 39 | * length of shape aka THE #dimensions. 40 | 41 | //initalize dims empty 42 | let dims = []; 43 | let max_i = max(shape(raw_in)); 44 | if (input instanceof Tensor) { 45 | max_i = max(raw_in.shape) 46 | } 47 | //do this for the number of loops necessary 48 | for (let i = 0; i < max_i; i++) { 49 | //push an item into dims to increase the size, reflecting that its a higher dimension 50 | dims.push(raw_in.length); 51 | //decrease the dimension of the array/Mat/Tensor by 1 by assigning it to the first row of the array (ex; [[2,3],[4,5]] --> [2,3]) 52 | if (Array.isArray(raw_in[0])) { 53 | raw_in = raw_in[0]; 54 | } 55 | //if the loop has reached its limit and its no longer an array but a number, break 56 | else { 57 | break; 58 | } 59 | } */ 60 | 61 | //return the size of dims, i.e. the number of elements pushed or the number of times we decreased the dimension + 1 62 | //return dims.length; 63 | } 64 | 65 | 66 | //if its a number just return 0; 67 | else if (is_number(input)) { 68 | return 0; 69 | } 70 | 71 | //should it be null? nothing? throw error? NaN? 72 | else { 73 | throw new Error('Exception occurred in ndim - incorrect structure provided as input'); 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /ndim_test.hhs: -------------------------------------------------------------------------------- 1 | /**let a = 6; 2 | let b = [5,34,3]; 3 | let c = [[3,2,3],[3,4,2]]; 4 | let d = new Mat([3,4,2]); 5 | let e = new Mat([[3,4],[4,3],[4,3]]) 6 | let f = [[[2,2],[3,3],[4,4]],[[2,2],[3,3],[4,4]],[[4,4],[3,3],[2,2]]] 7 | let g = new Tensor([[[2,2],[3,3],[4,4]],[[2,2],[3,3],[4,4]],[[4,4],[3,3],[2,2]]]);**/ 8 | 9 | //its taking e,f,g and saying 2.... but if i enter 3 values in each sub array it says 3. something weird with the dimensions 10 | //will think about it tomorrow -------------------------------------------------------------------------------- /normalize.hhs: -------------------------------------------------------------------------------- 1 | /** 2 | * @author Jianan Lin (林家南) 3 | * @param input - a series of numbers, array, matrix, tensor that you want to normalize 4 | * @returns - an array / matrix / tensor with elements in [0, 1] 5 | * you can write normalize(1, 2, 3) or normalize([1, 2, 3]) 6 | * 7 | */ 8 | 9 | 10 | function normalize(input) { 11 | 12 | *import math: deep_copy 13 | *import math: ndim 14 | *import math: max 15 | *import math: min 16 | 17 | // argument check 18 | if (arguments.length === 0) { 19 | throw new Error('Exception occurred in normalize - no argument given'); 20 | } 21 | 22 | // normalize(1, 2, 3) 23 | if (typeof arguments[0] === 'number') { 24 | raw_in = [] 25 | for (let i = 0; i < arguments.length; i++) { 26 | if (typeof arguments[i] === 'number') { 27 | raw_in.push(arguments[i]) 28 | } 29 | else { 30 | throw new Error('Exception occurred in normalize - only numbers are allowed'); 31 | } 32 | } 33 | 34 | let max_ = max(raw_in); 35 | let min_ = min(raw_in); 36 | if (max_ === min_) { 37 | let result = deep_copy(raw_in); 38 | for (let i = 0; i < raw_in.length; i++) { 39 | result[i] = 1; 40 | } 41 | return result; 42 | } 43 | 44 | else { 45 | let result = deep_copy(raw_in); 46 | for (let i = 0; i < raw_in.length; i++) { 47 | result[i] = (raw_in[i] - min_) / (max_ - min_); 48 | } 49 | return result; 50 | } 51 | } 52 | 53 | // normalize([1, 2, 3]) 54 | if (arguments.length !== 1) { 55 | throw new Error('Exception occurred in normalize - wrong argument number'); 56 | } 57 | if (!(Array.isArray(input)) && !(input instanceof Mat) && !(input instanceof Tensor)) { 58 | throw new Error('Exception occurred in normalize - input must be an array, matrix or tensor'); 59 | } 60 | 61 | let in_type = input instanceof Mat || input instanceof Tensor; 62 | let raw_in = in_type ? input.clone().val : deep_copy(input); 63 | 64 | let max_ = max(raw_in); 65 | let min_ = min(raw_in); 66 | 67 | let result = normalize_helper(raw_in, max_, min_); 68 | if (ndim(result) <= 2) { 69 | return mat(result); 70 | } 71 | else { 72 | return new Tensor(result); 73 | } 74 | 75 | function output(x, max_, min_) { 76 | if (max_ === min_) { 77 | return 1; 78 | } 79 | else { 80 | return (x - min_) / (max_ - min_); 81 | } 82 | } 83 | 84 | function normalize_helper(array, max_, min_) { 85 | if (ndim(array) === 1) { 86 | let result = deep_copy(array); 87 | for (let i = 0; i < result.length; i++) { 88 | result[i] = output(array[i], max_, min_); 89 | } 90 | return result; 91 | } 92 | else { 93 | let result = []; 94 | for (let i = 0; i < array.length; i++) { 95 | temp = normalize_helper(array[i], max_, min_); 96 | result.push(temp); 97 | } 98 | return result; 99 | } 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /normalize_test.hhs: -------------------------------------------------------------------------------- 1 | /** 2 | * @author Jianan Lin 3 | * Unit test for normalize 4 | * 5 | */ 6 | 7 | 8 | function normalize_test() { 9 | 10 | *import math: normalize 11 | 12 | if (!(normalize(1, 2, 3) === [0, 0.5, 1])) { 13 | throw new Error('Unit test failed for normalize for numbers'); 14 | } 15 | 16 | let a = [[1, 2, 3]]; 17 | let b = mat(a); 18 | let c = new Tensor([a, a]); 19 | 20 | if (!(normalize(a) === mat([[0, 0.5, 1]]))) { 21 | throw new Error('Unit test failed for normalize for array'); 22 | } 23 | 24 | if (!(normalize(b) === mat([[0, 0.5, 1]]))) { 25 | throw new Error('Unit test failed for normalize for matrix'); 26 | } 27 | 28 | let d = normalize(c).val 29 | if (!(d[0] === [[0, 0.5, 1]]) || !(d[1] === [[0, 0.5, 1]])) { 30 | throw new Error('Unit test failed for normalize for tensor'); 31 | } 32 | 33 | //print('normalize test pass'); 34 | } 35 | -------------------------------------------------------------------------------- /pascal.hhs: -------------------------------------------------------------------------------- 1 | /** 2 | * @author Alan Liang 3 | * @param n - integer, size of final matrix 4 | * @returns - Mat object matrix 5 | * 6 | * a symmetric Pascal matrix contains the binomial coefficients as its elements. 7 | * 8 | */ 9 | 10 | function pascal(n) 11 | { 12 | // wrong argument number 13 | if (arguments.length === 0) { 14 | throw new Error('Exception occurred in pascal - no argument given'); 15 | } 16 | else if (arguments.length > 1) { 17 | throw new Error('Exception occurred in pascal - wrong argument number'); 18 | } 19 | // the input shoule be an integer 20 | if(!Number.isInteger(n)) { 21 | throw new Error("Exception occurred in pascal - the parameter must be an integer."); 22 | } 23 | 24 | let pascalSquare = Array(n).fill(0).map(x => Array(n).fill(0)); 25 | 26 | // Fill the first row with 1s 27 | pascalSquare[0] = Array(n).fill(1); 28 | 29 | // Fill the first column with 1s 30 | for (let row = 0; row < n; row++) { 31 | pascalSquare[row][0] = 1; 32 | } 33 | 34 | // Use the definition of pascal matrix 35 | for (let row = 1; row < n; row++) { 36 | for (let col = 1; col < n; col++) { 37 | pascalSquare[row][col] = pascalSquare[row-1][col] + pascalSquare[row][col-1]; 38 | } 39 | } 40 | return pascalSquare; 41 | } 42 | -------------------------------------------------------------------------------- /pascal_test.hhs: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author Alan Liang 3 | * test for pascal matrix generation 4 | */ 5 | 6 | 7 | function pascal_test() { 8 | 9 | *import math: pascal 10 | *import math: inv 11 | 12 | // Property of pascal(n) : entries of pascal(n)^-1 are integers 13 | for (let n = 1; n < 5; n++) { 14 | let A = pascal(n); 15 | if (inv(A).val.some(c => c.some(x => !isInteger(x)))) { 16 | throw new Error('Unit test failed for pascal'); 17 | } 18 | } 19 | 20 | function isInteger(x) { 21 | return Number.isInteger(Math.round(x * 100000) / 100000); 22 | } 23 | 24 | //print('pascal test pass'); 25 | } 26 | -------------------------------------------------------------------------------- /percentile.hhs: -------------------------------------------------------------------------------- 1 | /** 2 | * @Author Jianan Lin (林家南) 3 | * @param input - an array, matrix, tensor to get the percentile 4 | * @param x - percentage probability from interval [0, 100] 5 | * @returns - 1-d array from the input[0] to input[(0.01 * probability * length)] 6 | * 7 | */ 8 | 9 | 10 | function percentile(input, x = 100) { 11 | 12 | *import math: ndim 13 | *import math: deep_copy 14 | *import math: flatten 15 | 16 | let sort_array = []; 17 | if (arguments.length === 0) { 18 | throw new Error('Exception occurred in percentile - no argument given'); 19 | } 20 | 21 | // quantile([1, 2, 3], 0, ???) 22 | if (arguments.length > 2) { 23 | throw new Error('Exception occurred in percentile - wrong argument number'); 24 | } 25 | 26 | if (!(typeof x === 'number') || x < 0 || x > 100) { 27 | throw new Error('Exception occurred in percentile - argument[1] must belong to interval [0, 100]'); 28 | } 29 | 30 | if (!(Array.isArray(input)) && !(input instanceof Mat) && !(input instanceof Tensor)) { 31 | throw new Error('Exception occurred in percentile - argument[0] must be an array, matrix or tensor'); 32 | } 33 | 34 | let in_type = (input instanceof Mat) || (input instanceof Tensor); 35 | let raw_in = in_type ? input.clone().val : deep_copy(input); 36 | if (ndim(raw_in) > 1) { 37 | raw_in = flatten(raw_in); 38 | } 39 | sort_array = private_quick_sort(raw_in); 40 | x = x / 100; 41 | 42 | let length = sort_array.length; 43 | if (length === 0) { 44 | throw new Error('Exception occurred in percentile - argument[0] cannot be empty'); 45 | } 46 | 47 | let size = mathjs.round(length * x); 48 | if (size < 1) { 49 | return [sort_array[0]]; 50 | } 51 | else { 52 | let result = sort_array.slice(0, size); 53 | return result; 54 | } 55 | 56 | // when we use this function, we have ensured that input is an array 57 | function private_quick_sort(input) { 58 | 59 | // if the length is less than 10, then use insert sort 60 | if (input.length < 10) { 61 | return private_insert_sort(input); 62 | } 63 | 64 | let borderline = (input[0] + input[input.length - 1]) / 2; 65 | let L = [], M = [], R = []; 66 | 67 | // divide the elements into left and right 68 | for (let i = 0; i < input.length; i++) { 69 | if (input[i] < borderline) { 70 | L.push(input[i]); 71 | } 72 | else if (input[i] == borderline) { 73 | M.push(input[i]); 74 | } 75 | else { 76 | R.push(input[i]); 77 | } 78 | } 79 | 80 | // recursion 81 | L = private_quick_sort(L); 82 | R = private_quick_sort(R); 83 | 84 | let result = L.concat(M).concat(R); 85 | return result; 86 | } 87 | 88 | // when we use this function, we have ensured that input is an array 89 | function private_insert_sort(input) { 90 | 91 | // if the length is 0 or 1 92 | if (input.length <= 1) { 93 | return input; 94 | } 95 | 96 | // insert part 97 | for (let i = 1; i < input.length; i++) { 98 | 99 | // find the position to insert 100 | let j = 0; 101 | while (input[j] <= input[i] && j < i) { 102 | j++; 103 | } 104 | 105 | // move the element 106 | let temp = input[i]; 107 | for (let k = i; k > j; k--) { 108 | input[k] = input[k - 1]; 109 | } 110 | input[j] = temp; 111 | } 112 | 113 | return input; 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /percentile_test.hhs: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author Jianan Lin 3 | * test for percentile function 4 | */ 5 | 6 | 7 | function percentile_test() { 8 | 9 | *import math: percentile 10 | 11 | let a = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; 12 | 13 | if (!(percentile(a, 0) === [1])) { 14 | throw new Error('Unit test failed for quantile'); 15 | } 16 | 17 | if (!(percentile(a, 100) === a)) { 18 | throw new Error('Unit test failed for quantile'); 19 | } 20 | 21 | if (!(percentile(a, 50) === [1, 2, 3, 4, 5])) { 22 | throw new Error('Unit test failed for quantile'); 23 | } 24 | 25 | if (!(percentile(a, 30) === [1, 2, 3])) { 26 | throw new Error('Unit test failed for quantile'); 27 | } 28 | 29 | //print('percentile test pass'); 30 | } 31 | -------------------------------------------------------------------------------- /quadratic_equation.hhs: -------------------------------------------------------------------------------- 1 | /** 2 | * @Author Jianan Lin (林家南) 3 | * @param a, b, c - ax^2 + bx + c = 0 4 | * @param flag - true: provide complex solution; false: only real solution 5 | * @returns - solution in 2-element array, NaN allowed if flag = false 6 | * Tips: if b = c = 0, then there is only real solution 7 | * 8 | */ 9 | 10 | 11 | 12 | function quadratic_equation(a, b = 0, c = 0, flag = true) { 13 | 14 | if (arguments.length === 0) { 15 | throw new Error('Exception occurred in quadratic_equation - no argument given'); 16 | } 17 | 18 | if (arguments.length > 4) { 19 | throw new Error('Exception occurred in quadratic_equation - wrong argument number'); 20 | } 21 | 22 | if (!(typeof a === 'number') || !(typeof b === 'number') || !(typeof c === 'number') || !(typeof flag === 'boolean')) { 23 | throw new Error('Exception occurred in quadratic_equation - a, b, c, must be numbers and flag must be boolean'); 24 | } 25 | 26 | // bx + c = 0 27 | if (a === 0) { 28 | if (b === 0) { 29 | return [NaN, NaN]; 30 | } 31 | else { 32 | let result = b / c; 33 | result = result.toFixed(5); 34 | return [mathjs.complex(-result, 0), mathjs.complex(result, 0)]; 35 | } 36 | } 37 | 38 | let delta = b * b - 4 * a * c; 39 | 40 | // if |delta| < 1E-10, we can believe it is 0 41 | if (delta > 0.0000000001) { 42 | // remember to use mathjs.sqrt, otherwise sqrt returns matrix 43 | let diff = mathjs.sqrt(delta); 44 | let x1 = (-b - diff) / (2 * a); 45 | let x2 = (-b + diff) / (2 * a); 46 | x1 = x1.toFixed(5); 47 | x2 = x2.toFixed(5); 48 | return [mathjs.complex(x1, 0), mathjs.complex(x2, 0)]; 49 | } 50 | 51 | else if (delta < -0.0000000001) { 52 | let diff = mathjs.sqrt(-delta); 53 | if (flag === true) { 54 | let real_part = -b / (2 * a); 55 | real_part = real_part.toFixed(5); 56 | let imag_part = diff / (2 * a); 57 | imag_part = imag_part.toFixed(5); 58 | return [mathjs.complex(real_part, -imag_part), mathjs.complex(real_part, imag_part)]; 59 | } 60 | else { 61 | return [NaN, NaN]; 62 | } 63 | } 64 | 65 | else { 66 | let result = -b / (2 * a); 67 | result = result.toFixed(5); 68 | return [mathjs.complex(result, 0), mathjs.complex(result, 0)]; 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /quadratic_equation_test.hhs: -------------------------------------------------------------------------------- 1 | /** 2 | * @author Jianan Lin 3 | * Unit test for quadratic_equation function 4 | */ 5 | 6 | 7 | function quadratic_equation_test() { 8 | 9 | *import math: quadratic_equation 10 | 11 | // x - 1 = 0 12 | if (!(quadratic_equation(0, 1, -1) === [1, 1])) { 13 | throw new Error('Unit test for quadratic_equation failed'); 14 | } 15 | 16 | // x^2 - 2x + 1 = 0 17 | if (!(quadratic_equation(1, -2, 1) === [1, 1])) { 18 | throw new Error('Unit test for quadratic_equation failed'); 19 | } 20 | 21 | // x^2 - 3x + 2 = 0 22 | if (!(quadratic_equation(1, -3, 2) === [1, 2])) { 23 | throw new Error('Unit test for quadratic_equation failed'); 24 | } 25 | 26 | // x^2 + x + 1 = 0, false 27 | // we can not test this because NaN === NaN returns false 28 | // but the answer is right [NaN, NaN] 29 | // if (!(quadratic_equation(1, 1, 1, false)[0] === NaN)) { 30 | // throw new Error('Unit test for quadratic_equation failed'); 31 | // } 32 | 33 | // x^2 + 2x + 2 = 0, true 34 | // we can not test this because complex === complex returns false 35 | // but the answer is right [-1 - i, -1 + i] 36 | // if (!(quadratic_equation(1, 2, 2, true) === [mathjs.complex(-1, -1), mathjs.complex(-1, 1)])) { 37 | // throw new Error('Unit test for quadratic_equation failed'); 38 | // } 39 | 40 | //print("quadratic_equation test pass"); 41 | 42 | } 43 | -------------------------------------------------------------------------------- /quantile.hhs: -------------------------------------------------------------------------------- 1 | /** 2 | * @Author Jianan Lin (林家南) 3 | * @param input - an array, matrix, tensor to get the qunatile 4 | * @param x - probability from interval [0, 1] 5 | * @returns - 1-d array from the input[0] to input[(probability * length)] 6 | * 7 | */ 8 | 9 | 10 | function quantile(input, x = 1) { 11 | 12 | *import math: ndim 13 | *import math: deep_copy 14 | *import math: flatten 15 | 16 | let sort_array = []; 17 | if (arguments.length === 0) { 18 | throw new Error('Exception occurred in qunatile - no argument given'); 19 | } 20 | 21 | // quantile([1, 2, 3], 0, ???) 22 | if (arguments.length > 2) { 23 | throw new Error('Exception occurred in quantile - wrong argument number'); 24 | } 25 | 26 | if (!(typeof x === 'number') || x < 0 || x > 1) { 27 | throw new Error('Exception occurred in qunatile - argument[1] must belong to interval [0, 1]'); 28 | } 29 | 30 | if (!(Array.isArray(input)) && !(input instanceof Mat) && !(input instanceof Tensor)) { 31 | throw new Error('Exception occurred in qunatile - argument[0] must be an array, matrix or tensor'); 32 | } 33 | 34 | let in_type = (input instanceof Mat) || (input instanceof Tensor); 35 | let raw_in = in_type ? input.clone().val : deep_copy(input); 36 | if (ndim(raw_in) > 1) { 37 | raw_in = flatten(raw_in); 38 | } 39 | sort_array = private_quick_sort(raw_in); 40 | 41 | let length = sort_array.length; 42 | if (length === 0) { 43 | throw new Error('Exception occurred in quantile - argument[0] cannot be empty'); 44 | } 45 | 46 | let size = mathjs.round(length * x); 47 | if (size < 1) { 48 | return [sort_array[0]]; 49 | } 50 | else { 51 | let result = sort_array.slice(0, size); 52 | return result; 53 | } 54 | 55 | // when we use this function, we have ensured that input is an array 56 | function private_quick_sort(input) { 57 | 58 | // if the length is less than 10, then use insert sort 59 | if (input.length < 10) { 60 | return private_insert_sort(input); 61 | } 62 | 63 | let borderline = (input[0] + input[input.length - 1]) / 2; 64 | let L = [], M = [], R = []; 65 | 66 | // divide the elements into left and right 67 | for (let i = 0; i < input.length; i++) { 68 | if (input[i] < borderline) { 69 | L.push(input[i]); 70 | } 71 | else if (input[i] == borderline) { 72 | M.push(input[i]); 73 | } 74 | else { 75 | R.push(input[i]); 76 | } 77 | } 78 | 79 | // recursion 80 | L = private_quick_sort(L); 81 | R = private_quick_sort(R); 82 | 83 | let result = L.concat(M).concat(R); 84 | return result; 85 | } 86 | 87 | // when we use this function, we have ensured that input is an array 88 | function private_insert_sort(input) { 89 | 90 | // if the length is 0 or 1 91 | if (input.length <= 1) { 92 | return input; 93 | } 94 | 95 | // insert part 96 | for (let i = 1; i < input.length; i++) { 97 | 98 | // find the position to insert 99 | let j = 0; 100 | while (input[j] <= input[i] && j < i) { 101 | j++; 102 | } 103 | 104 | // move the element 105 | let temp = input[i]; 106 | for (let k = i; k > j; k--) { 107 | input[k] = input[k - 1]; 108 | } 109 | input[j] = temp; 110 | } 111 | 112 | return input; 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /quantile_test.hhs: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author Jianan Lin 3 | * test for quantile function 4 | */ 5 | 6 | 7 | function quantile_test() { 8 | 9 | *import math: quantile 10 | 11 | let a = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; 12 | 13 | if (!(quantile(a, 0) === [1])) { 14 | throw new Error('Unit test failed for quantile'); 15 | } 16 | 17 | if (!(quantile(a, 1) === a)) { 18 | throw new Error('Unit test failed for quantile'); 19 | } 20 | 21 | if (!(quantile(a, 0.5) === [1, 2, 3, 4, 5])) { 22 | throw new Error('Unit test failed for quantile'); 23 | } 24 | 25 | if (!(quantile(a, 0.3) === [1, 2, 3])) { 26 | throw new Error('Unit test failed for quantile'); 27 | } 28 | 29 | //print('quantile test pass'); 30 | } 31 | -------------------------------------------------------------------------------- /quartic_equation_test.hhs: -------------------------------------------------------------------------------- 1 | /** 2 | * @Author Jianan Lin (林家南) 3 | * test function for quartic_equation 4 | */ 5 | 6 | 7 | 8 | function quartic_equation_test() { 9 | 10 | *import math: quartic_equation 11 | 12 | if (!(quartic_equation(3, 6, -123, -126, 1080) === [-4, 3, -6, 5])) { 13 | throw new Error('quartic_equation test failed'); 14 | } 15 | 16 | //print('quartic_equation test pass'); 17 | } 18 | -------------------------------------------------------------------------------- /reshape.hhs: -------------------------------------------------------------------------------- 1 | /** 2 | * @author Jianan Lin (林家南) 3 | * @param input - an array / matrix / tensor 4 | * @param sizes - an array such as [2, 3, 2] or [2, -1] 5 | * @returns - an 1-d array, 2-d matrix or more-d tensor with specified size 6 | * 7 | */ 8 | 9 | 10 | function reshape(input, sizes = [-1]) { 11 | 12 | *import math: flatten 13 | *import math: ndim 14 | *import math: deep_copy 15 | 16 | // wrong argument number 17 | if (arguments.length === 0) { 18 | throw new Error('Exception occurred in reshape - no argument given'); 19 | } 20 | else if (arguments.length > 2) { 21 | throw new Error('Exception occurred in reshape - wrong argument number'); 22 | } 23 | 24 | // type check 25 | if (!(Array.isArray(input)) && !(input instanceof Mat) && !(input instanceof Tensor)) { 26 | throw new Error('Exception occurred in reshape - argument[0] is not a Mat, Tensor or JS Array'); 27 | } 28 | 29 | if (!(Array.isArray(sizes)) && !(sizes instanceof Mat) && !(sizes instanceof Tensor)) { 30 | throw new Error('Exception occurred in reshape - argument[1] is not a Mat, Tensor or JS Array'); 31 | } 32 | 33 | // dimension check 34 | if (ndim(sizes) !== 1) { 35 | throw new Error('Exception occurred in reshape - argument[1] is not 1-dimensional'); 36 | } 37 | 38 | // size check 39 | let dimension = sizes.length; 40 | 41 | // the last element can be -1, others must be positive integers 42 | for (let i = 0; i < dimension - 1; i++) { 43 | if (sizes[i] <= 0 || parseInt(sizes[i]) !== sizes[i]) { 44 | throw new Error('Exception occurred in reshape - elements in sizes must be positive integers'); 45 | } 46 | } 47 | 48 | if ((sizes[dimension - 1] <= 0 && sizes[dimension - 1] !== -1) || (parseInt(sizes[dimension - 1]) !== sizes[dimension - 1])) { 49 | throw new Error('Exception occurred in reshape - the last element in sizes must be positive integer or -1'); 50 | } 51 | 52 | // process the input 53 | let in_type = (input instanceof Mat) || (input instanceof Tensor); 54 | let raw_in = (in_type) ? input.clone().val : deep_copy(input); 55 | raw_in = flatten(raw_in); 56 | let length = raw_in.length; 57 | 58 | // determine whether we can reshape 59 | let product = 1; 60 | for (let i = 0; i < dimension - 1; i++) { 61 | product = product * sizes[i]; 62 | } 63 | if (length % product !== 0) { 64 | throw new Error('Exception occurred in reshape - the product of array sizes must be a divisor'); 65 | } 66 | 67 | if (sizes[dimension - 1] !== -1 && product * sizes[dimension - 1] !== length) { 68 | throw new Error('Exception occurred in reshape - the product of array sizes must be a divisor'); 69 | } 70 | 71 | sizes[dimension - 1] = length / product; 72 | 73 | // next we reshape the array 74 | if (dimension === 1) { 75 | return raw_in; 76 | } 77 | else { 78 | let result = []; 79 | for (let i = 0; i < sizes[0]; i++) { 80 | let temp = reshape_helper(raw_in, length / sizes[0] * i, length / sizes[0] * (i + 1), sizes, 1); 81 | result.push(temp); 82 | } 83 | if (ndim(result) == 2) { 84 | return mat(result); 85 | } 86 | else { 87 | return new Tensor(result); 88 | } 89 | } 90 | 91 | // use this recursive function 92 | function reshape_helper(input, start, end, sizes, index) { 93 | // the end case 94 | if (index === sizes.length - 1) { 95 | let result = []; 96 | for (let i = 0; i < end - start; i++) { 97 | result.push(input[i + start]); 98 | } 99 | return result 100 | } 101 | 102 | // the recursion 103 | else { 104 | let result = []; 105 | let distance = end - start; 106 | for (let i = 0; i < sizes[index]; i++) { 107 | let temp = reshape_helper(input, start + distance / sizes[index] * i, start + distance / sizes[index] * (i + 1), sizes, index + 1); 108 | result.push(temp) 109 | } 110 | return result; 111 | } 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /reshape_test.hhs: -------------------------------------------------------------------------------- 1 | /** 2 | * @author Jianan Lin (林家南) 3 | * Function of test unit for reshape 4 | */ 5 | 6 | function reshape_test() { 7 | 8 | *import math: reshape 9 | 10 | let a = [1, 2, 3, 4, 5, 6, 7, 8]; 11 | let b = reshape(a, [2, 2, 2]).val; 12 | for (let i = 0; i < 2; i++) { 13 | for (let j = 0; j < 2; j++) { 14 | for (let k = 0; k < 2; k++) { 15 | if (b[i][j][k] !== 4 * i + 2 * j + k + 1) { 16 | throw new Error('Failed unit test for reshape.'); 17 | } 18 | } 19 | } 20 | } 21 | 22 | let c = reshape(a, [2, -1]).val; 23 | for (let i = 0; i < 2; i++) { 24 | for (let j = 0; j < 4; j++) { 25 | if (c[i][j] !== 4 * i + j + 1) { 26 | throw new Error('Failed unit test for reshape.'); 27 | } 28 | } 29 | } 30 | 31 | //print('reshape test pass'); 32 | } 33 | -------------------------------------------------------------------------------- /resize_test.hhs: -------------------------------------------------------------------------------- 1 | /** 2 | * @author Jianan Lin (林家南) 3 | * Function of test unit for resize 4 | */ 5 | 6 | function resize_test() { 7 | 8 | *import math: resize 9 | 10 | let a = [1, 2, 3, 4, 5, 6, 7, 8]; 11 | let b = resize(a, [2, 2, -1]).val; 12 | for (let i = 0; i < 2; i++) { 13 | for (let j = 0; j < 2; j++) { 14 | for (let k = 0; k < 2; k++) { 15 | if (b[i][j][k] !== 4 * i + 2 * j + k + 1) { 16 | throw new Error('Failed unit test for resize.'); 17 | } 18 | } 19 | } 20 | } 21 | 22 | let c = resize(a, [2, -1]).val; 23 | for (let i = 0; i < 2; i++) { 24 | for (let j = 0; j < 4; j++) { 25 | if (c[i][j] !== 4 * i + j + 1) { 26 | throw new Error('Failed unit test for resize.'); 27 | } 28 | } 29 | } 30 | 31 | let d = resize(a, [3, 3], 9).val; 32 | for (let i = 0; i < 3; i++) { 33 | for (let j = 0; j < 3; j++) { 34 | if (d[i][j] !== 3 * i + j + 1) { 35 | throw new Error('Failed unit test for resize.'); 36 | } 37 | } 38 | } 39 | 40 | let e = resize(a, [2, 2], -1).val; 41 | for (let i = 0; i < 2; i++) { 42 | for (let j = 0; j < 2; j++) { 43 | if (e[i][j] !== 2 * i + j + 1) { 44 | throw new Error('Failed unit test for resize.'); 45 | } 46 | } 47 | } 48 | 49 | //print('resize test pass'); 50 | } 51 | -------------------------------------------------------------------------------- /rmmissing.hhs: -------------------------------------------------------------------------------- 1 | /** 2 | * @Author Jianan Lin (林家南) 3 | * @param input: a 1-2d JS array / matrix / tensor 4 | * @param dim: 0 means delete the whole column, 1 means delete row 5 | * @returns - input without missing value 6 | * 7 | */ 8 | 9 | 10 | 11 | function rmmissing(input, dim = 0) { 12 | 13 | *import math: ndim 14 | *import math: deep_copy 15 | 16 | if (arguments.length === 0) { 17 | throw new Error('Exception occurred in rmmissing - no argument given'); 18 | } 19 | if (arguments.length > 2) { 20 | throw new Error('Exception occurred in rmmissing - wrong argument number'); 21 | } 22 | 23 | if (dim !== 0 && dim !== 1) { 24 | throw new Error('Exception occurred in rmmissing - argument[1] must be 0 or 1'); 25 | } 26 | if (!(Array.isArray(input)) && !(input instanceof Mat) && !(input instanceof Tensor)) { 27 | throw new Error('Exception occurred in rmmissing - argument[0] must be JS array, matrix or tensor'); 28 | } 29 | 30 | let in_type = input instanceof Mat || input instanceof Tensor; 31 | let raw_in = in_type ? input.clone().val : deep_copy(input); 32 | if (ndim(raw_in) > 2) { 33 | throw new Error('Exception occurred in rmmissing - input can only be 1-2 dimensional'); 34 | } 35 | 36 | // [1, 2, 3, NaN] 37 | if (ndim(raw_in) === 1) { 38 | let result = []; 39 | for (let i = 0; i < raw_in.length; i++) { 40 | if (isMiss(raw_in[i]) === false) { 41 | result.push(raw_in[i]); 42 | } 43 | } 44 | return mat(result); 45 | } 46 | 47 | // [[1], [2], [3]] 48 | if (raw_in[0].length === 1) { 49 | let result = []; 50 | for (let i = 0; i < raw_in.length; i++) { 51 | if (isMiss(raw_in[i][0]) === false) { 52 | result.push([raw_in[i][0]]); 53 | } 54 | } 55 | return mat(result); 56 | } 57 | 58 | // ndim = 2 59 | if (dim === 0) { 60 | // myset includes all the index of column that should be deleted 61 | let myset = new Set(); 62 | for (let i = 0; i < raw_in.length; i++) { 63 | for (let j = 0; j < raw_in[i].length; j++) { 64 | if (isMiss(raw_in[i][j])) { 65 | myset.add(j); 66 | } 67 | } 68 | } 69 | // then we construct the result matrix 70 | let result = [] 71 | for (let i = 0; i < raw_in.length; i++) { 72 | let temp = [] 73 | for (let j = 0; j < raw_in[i].length; j++) { 74 | if (myset.has(j) === false) { 75 | temp.push(raw_in[i][j]); 76 | } 77 | } 78 | result.push(temp); 79 | } 80 | return mat(result); 81 | } 82 | 83 | // dim === 1 84 | else { 85 | let myset = new Set(); 86 | for (let i = 0; i < raw_in.length; i++) { 87 | for (let j = 0; j < raw_in[i].length; j++) { 88 | if (isMiss(raw_in[i][j])) { 89 | myset.add(i); 90 | } 91 | } 92 | } 93 | let result = []; 94 | for (let i = 0; i < raw_in.length; i++) { 95 | if (myset.has(i) === false) { 96 | let temp = deep_copy(raw_in[i]); 97 | result.push(temp); 98 | } 99 | } 100 | return mat(result); 101 | } 102 | 103 | // x is missing: NaN, "", null 104 | function isMiss(x) { 105 | if (x === 0 || x) { 106 | return false; 107 | } 108 | else { 109 | return true; 110 | } 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /rmmissing_test.hhs: -------------------------------------------------------------------------------- 1 | /** 2 | * @author Jianan Lin 3 | * Unit test for ismissing 4 | * 5 | */ 6 | 7 | 8 | 9 | function rmmissing_test() { 10 | 11 | *import math: rmmissing 12 | 13 | let a = [1, 2, NaN]; 14 | let b = rmmissing(a); 15 | 16 | if (!(b === mat([1, 2]))) { 17 | throw new Error('Unit test failed for rmmissing_test'); 18 | } 19 | 20 | let c = [[1], [2], [NaN]]; 21 | let d = rmmissing(c); 22 | if (!(d === mat([[1], [2]]))) { 23 | throw new Error('Unit test failed for rmmissing_test'); 24 | } 25 | 26 | let e = mat([[1, 2, 3], [NaN, null, 4]]); 27 | let f = rmmissing(e, 0); 28 | if (!(f === mat([[3], [4]]))) { 29 | throw new Error('Unit test failed for rmmissing_test'); 30 | } 31 | 32 | let g = mat([[1, NaN], [2, null], [3, 4]]); 33 | let h = rmmissing(g, 1); 34 | if (!(h === mat([[3, 4]]))) { 35 | throw new Error('Unit test failed for rmmissing_test'); 36 | } 37 | 38 | //print('rmmissing test pass'); 39 | } 40 | -------------------------------------------------------------------------------- /rms.hhs: -------------------------------------------------------------------------------- 1 | /** 2 | * @Author Jianan Lin (林家南) 3 | * @param input - some numbers, an array, matrix, tensor to get the root mean square 4 | * @returns - a number = sqrt((a1^2 + a2^2 + ...) / n) 5 | * 6 | */ 7 | 8 | 9 | function rms(input) { 10 | 11 | *import math: ndim 12 | *import math: deep_copy 13 | *import math: flatten 14 | 15 | let sort_array = []; 16 | if (arguments.length === 0) { 17 | throw new Error('Exception occurred in rms - no argument given'); 18 | } 19 | 20 | // rmf(1, 2, 3) 21 | if (typeof arguments[0] === 'number') { 22 | let result = 0; 23 | for (let i = 0; i < arguments.length; i++) { 24 | if (typeof arguments[i] === 'number') { 25 | result = result + arguments[i] * arguments[i]; 26 | } 27 | else { 28 | throw new Error('Exception occurred in rms - you should input some numbers'); 29 | } 30 | } 31 | // sqrt 32 | return mathjs.sqrt(result / arguments.length); 33 | } 34 | 35 | // rms([1, 2, 3], 0) 36 | if (arguments.length > 1) { 37 | throw new Error('Exception occurred in rms - wrong argument number'); 38 | } 39 | 40 | if (!(Array.isArray(input)) && !(input instanceof Mat) && !(input instanceof Tensor)) { 41 | throw new Error('Exception occurred in rms - input must be an array, matrix or tensor'); 42 | } 43 | 44 | let in_type = (input instanceof Mat) || (input instanceof Tensor); 45 | let raw_in = in_type ? input.clone().val : deep_copy(input); 46 | if (ndim(raw_in) > 1) { 47 | raw_in = flatten(raw_in); 48 | } 49 | 50 | if (raw_in.length === 0) { 51 | throw new Error('Exception occurred in rms - input cannot be empty'); 52 | // return[0] 53 | } 54 | 55 | let result = 0; 56 | for (let i = 0; i < raw_in.length; i++) { 57 | result = result + raw_in[i] * raw_in[i]; 58 | } 59 | return mathjs.sqrt(result / raw_in.length); 60 | } 61 | -------------------------------------------------------------------------------- /rms_test.hhs: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author Jianan Lin 3 | * test for rms function 4 | */ 5 | 6 | 7 | function rms_test() { 8 | 9 | *import math: rms 10 | 11 | let a = [1, 1, 1, 1, 1]; 12 | 13 | if (!(rms(1, 1, 1) === 1)) { 14 | throw new Error('Unit test failed for rms for number'); 15 | } 16 | 17 | if (!(rms(a) === 1)) { 18 | throw new Error('Unit test failed for rms for array'); 19 | } 20 | 21 | //print('rms test pass'); 22 | } 23 | -------------------------------------------------------------------------------- /shape.hhs: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * @param input - the matrix like structure to find the shape of. Can be a 1d, 2d, etc JS array, Mat object or Tensor 4 | * @returns - a row vector or array representing the 'shape', i.e. the length of each dimension. for example a 3x4 (2 dimensional) matrix will have shape [3,4]. 5 | * 6 | * This function is similar to Tensor.shape, where it gives a shape vector representing each dimension's length 7 | */ 8 | 9 | 10 | 11 | function shape(input) { 12 | 13 | 14 | *import math: is_number 15 | *import math: deep_copy 16 | 17 | if (arguments.length !== 1) { 18 | throw new Error('Exception occurred in shape - wrong argument given'); 19 | } 20 | 21 | if (!(Array.isArray(input)) && !(input instanceof Mat) && !(input instanceof Tensor)) { 22 | throw new Error('Exception occurred in shape - input must be a JS array, matrix or tensor'); 23 | } 24 | 25 | let result = []; 26 | let current_dim = 0; 27 | 28 | if (is_number(input)) { 29 | throw new Error('Exception occurred in shape - numbers dont have dimensions'); 30 | } 31 | 32 | let in_type = (input instanceof Mat || input instanceof Tensor); 33 | let raw_in = (in_type) ? input.clone() : deep_copy(input); 34 | 35 | if (input instanceof Mat || input instanceof Tensor) { 36 | raw_in = raw_in.val; 37 | } 38 | 39 | while (raw_in instanceof Array) { 40 | current_dim += 1; 41 | result.push(raw_in.length); 42 | raw_in = raw_in[0]; 43 | } 44 | 45 | return result; 46 | } 47 | -------------------------------------------------------------------------------- /smooth.hhs: -------------------------------------------------------------------------------- 1 | /** 2 | * @author Jianan Lin (林家南) 3 | * @param input - an array, matrix, tensor that you want to smooth 4 | * @param dim - 0 or 1 with row or column, only support 2-d matrix 5 | * @returns - an array / matrix / tensor with elements smoothed 6 | * smooth method: a[i] -> (a[i - 2] + a[i - 1] + a[i] + a[i + 1] + a[i + 2]) / 5 7 | * 8 | */ 9 | 10 | 11 | function smooth(input, dim = 0) { 12 | 13 | *import math: deep_copy 14 | *import math: ndim 15 | 16 | // argument check 17 | if (arguments.length === 0) { 18 | throw new Error('Exception occurred in smooth - no argument given'); 19 | } 20 | 21 | if (arguments.length > 2) { 22 | throw new Error('Exception occurred in smooth - wrong argument number'); 23 | } 24 | 25 | if (!(Array.isArray(input)) && !(input instanceof Mat) && !(input instanceof Tensor)) { 26 | throw new Error('Exception occurred in smooth - input must be an array, matrix or tensor'); 27 | } 28 | 29 | if (dim !== 0 && dim !== 1) { 30 | throw new Error('Exception occurred in smooth - dim must be 0 or 1'); 31 | } 32 | 33 | let in_type = input instanceof Mat || input instanceof Tensor; 34 | let raw_in = in_type ? input.clone().val : deep_copy(input); 35 | let result = deep_copy(raw_in); 36 | 37 | // row local min 38 | if (dim === 0) { 39 | smooth_helper_row(raw_in, result); 40 | if (ndim(result) <= 2) { 41 | return mat(result); 42 | } 43 | else { 44 | return new Tensor(result); 45 | } 46 | } 47 | 48 | // column local min, require m * n matrix 49 | else { 50 | if (ndim(raw_in) !== 2) { 51 | throw new Error('Exception occurred in smooth - if dim = 1, then input must be a m * n matrix'); 52 | } 53 | else if (is_normal_matrix(raw_in) === false) { 54 | throw new Error('Exception occurred in smooth - if dim = 1, then input must be a m * n matrix'); 55 | } 56 | 57 | else { 58 | for (let i = 0; i < raw_in.length; i++) { 59 | for (let j = 0; j < raw_in[i].length; j++) { 60 | if (i === 0 || i === raw_in.length - 1) { 61 | result[i][j] = raw_in[i][j]; 62 | } 63 | else if (i === 1 || i === raw_in.length - 2) { 64 | result[i][j] = (raw_in[i - 1][j] + raw_in[i][j] + raw_in[i + 1][j]) / 3; 65 | } 66 | else { 67 | result[i][j] = (raw_in[i - 2][j] + raw_in[i - 1][j] + raw_in[i][j] + raw_in[i + 1][j] + raw_in[i + 2][j]) / 5; 68 | } 69 | } 70 | } 71 | 72 | return mat(result); 73 | } 74 | } 75 | 76 | function is_normal_matrix(input) { 77 | let m = input.length; 78 | let n = input[0].length; 79 | for (let i = 0; i < m; i++) { 80 | if (input[i].length !== n) { 81 | return false; 82 | } 83 | } 84 | return true; 85 | } 86 | 87 | function judge_row(array, index) { 88 | if (index === 0 || index === array.length - 1) { 89 | return array[index]; 90 | } 91 | else if (index === 1 || index === array.length - 2) { 92 | return (array[index - 1] + array[index] + array[index + 1]) / 3; 93 | } 94 | else { 95 | return (array[index - 2] + array[index - 1] + array[index] + array[index + 1] + array[index + 2]) / 5; 96 | } 97 | } 98 | 99 | function smooth_helper_row(array1, array2) { 100 | if (ndim(array1) === 1) { 101 | for (let i = 0; i < array1.length; i++) { 102 | array2[i] = judge_row(array1, i); 103 | } 104 | } 105 | else { 106 | for (let i = 0; i < array1.length; i++) { 107 | smooth_helper_row(array1[i], array2[i]); 108 | } 109 | } 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /smooth_test.hhs: -------------------------------------------------------------------------------- 1 | /** 2 | * @author Jianan Lin 3 | * Unit test for smooth 4 | * 5 | */ 6 | 7 | 8 | function smooth_test() { 9 | 10 | *import math: smooth 11 | 12 | let a = mat([[0, 2, 1, 2, 0], [0, 2, 1, 2, 0]]); 13 | let b = mat([[0, 1, 1, 1, 0], [0, 1, 1, 1, 0]]); 14 | 15 | if (!(smooth(a) === b)) { 16 | throw new Error('Unit test failed for smooth for numbers'); 17 | } 18 | 19 | let c = mat([[0, 0], [2, 2], [1, 1], [2, 2], [0, 0]]); 20 | let d = mat([[0, 0], [1, 1], [1, 1], [1, 1], [0, 0]]); 21 | if (!(smooth(c, 1) === d)) { 22 | throw new Error('Unit test failed for smooth for numbers'); 23 | } 24 | 25 | //print('smooth test pass'); 26 | } 27 | -------------------------------------------------------------------------------- /standard_missing.hhs: -------------------------------------------------------------------------------- 1 | /** 2 | * @Author Jianan Lin (林家南) 3 | * @param input: an array, matrix or tensor 4 | * @param missing: an element, array, matrix or tensor 5 | * @returns - a matrix / tensor with all elements in missing transformmed to missing values 6 | * 7 | */ 8 | 9 | 10 | 11 | function standard_missing(input, missing = 0) { 12 | 13 | *import math: ndim 14 | *import math: deep_copy 15 | *import math: flatten 16 | 17 | // argument check 18 | if (arguments.length === 0) { 19 | throw new Error('Exception occurred in standard_missing - no argument given'); 20 | } 21 | 22 | if (arguments.length > 2) { 23 | throw new Error('Exception occurred in standard_missing - wrong argument number'); 24 | } 25 | 26 | // type check 27 | if (!(Array.isArray(input)) && !(input instanceof Mat) && !(input instanceof Tensor)) { 28 | throw new Error('Exception occurred in standard_missing - argument[0] must be a JS array, matrix or tensor'); 29 | } 30 | 31 | let in_type = input instanceof Mat || input instanceof Tensor; 32 | let raw_in = in_type ? input.clone().val : deep_copy(input); 33 | 34 | let flag = (Array.isArray(missing)) || (missing instanceof Mat) || (missing instanceof Tensor); 35 | let pool = flag ? flatten(missing) : missing; 36 | 37 | if (flag) { 38 | let result = standard_array(raw_in); 39 | if (ndim(result) <= 2) { 40 | return mat(result); 41 | } 42 | else { 43 | return new Tensor(result); 44 | } 45 | } 46 | 47 | else { 48 | let result = standard_element(raw_in); 49 | if (ndim(result) <= 2) { 50 | return mat(result); 51 | } 52 | else { 53 | return new Tensor(result); 54 | } 55 | } 56 | 57 | function standard_array(data) { 58 | if (ndim(data) === 1) { 59 | let result = deep_copy(data); 60 | for (let i = 0; i < data.length; i++) { 61 | if (pool.includes(data[i])) { 62 | result[i] = getMiss(data[i]); 63 | } 64 | } 65 | return result; 66 | } 67 | 68 | else { 69 | let result = []; 70 | for (let i = 0; i < data.length; i++) { 71 | let temp = standard_array(data[i], pool); 72 | result.push(temp); 73 | } 74 | return result; 75 | } 76 | } 77 | 78 | function standard_element(data) { 79 | if (ndim(data) === 1) { 80 | let result = deep_copy(data); 81 | for (let i = 0; i < data.length; i++) { 82 | if (data[i] === pool) { 83 | result[i] = getMiss(data[i]); 84 | } 85 | } 86 | return result; 87 | } 88 | 89 | else { 90 | let result = []; 91 | for (let i = 0; i < data.length; i++) { 92 | let temp = standard_element(data[i], pool); 93 | result.push(temp); 94 | } 95 | return result; 96 | } 97 | } 98 | 99 | 100 | function getMiss(x) { 101 | if (x === 0 || x) { 102 | if (typeof x === 'number') { 103 | return NaN; 104 | } 105 | else if (typeof x === 'boolean') { 106 | return false; 107 | } 108 | else if (typeof x === 'string') { 109 | return ""; 110 | } 111 | else { 112 | return undefined; 113 | } 114 | } 115 | else { 116 | return x; 117 | } 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /standard_missing_test.hhs: -------------------------------------------------------------------------------- 1 | /** 2 | * @author Jianan Lin 3 | * Unit test for standard_missing 4 | * 5 | */ 6 | 7 | 8 | 9 | function standard_missing_test() { 10 | 11 | *import math: standard_missing 12 | 13 | let a = [1, 2, 3, 4, 5]; 14 | let b = standard_missing(a, [1, 2]); 15 | if (b.val[0][0] === b.val[0][0]) { 16 | throw new Error('Unit test failed for standard_missing'); 17 | } 18 | 19 | let c = mat([[1, 2, 3], [4, 5, 6]]); 20 | let d = standard_missing(c, 1); 21 | if (d.val[0][0] === d.val[0][0]) { 22 | throw new Error('Unit test failed for standard_missing'); 23 | } 24 | 25 | let e = standard_missing(c, [1, 2]); 26 | if (e.val[0][0] === e.val[0][0]) { 27 | throw new Error('Unit test failed for standard_missing'); 28 | } 29 | print('standard_missing test pass'); 30 | } 31 | -------------------------------------------------------------------------------- /sum.hhs: -------------------------------------------------------------------------------- 1 | /** 2 | * @author Jason Reynolds 3 | * @param input - the list, matrix, tensor or structure that you want to sum up the values of 4 | * @returns - a number representing all the elements added in the structure element wise. 5 | * 6 | * Function that takes in a list or matrix/submatrix or tensor/subtensor and returns the sum of all entries, using mathjs.sum 7 | * 8 | * 9 | * !!!consider pulling functions from here for basic use since javascript doesnt use scalars as 1d arrays!!!! 10 | * 11 | * 12 | */ 13 | 14 | 15 | 16 | function sum(input) { 17 | 18 | *import math: is_number 19 | *import math: flatten 20 | *import math: ndim 21 | *import math: deep_copy 22 | 23 | //if there are no arguments, there is an error 24 | if (arguments.length === 0) { 25 | throw new Error('Exception occurred in sum - no argument given'); 26 | } 27 | 28 | //if the first argument is a number, that means the user is inputting a list of numbers to add 29 | if (is_number(arguments[0])) { 30 | //loop through the arguments and for each one ... 31 | for (let i = 0; i < arguments.length; i++) { 32 | //first make sure it's a number! otherwise throw an error 33 | if (!(is_number(arguments[i]))) { 34 | throw new Error('Exception occurred in sum - first argument suggests a list, but there is a non number element in arguments.'); 35 | } 36 | } 37 | //initialize 0 and add up all the arguments .... !! could probably combine with above !! 38 | let result = 0; 39 | for (let j = 0; j < arguments.length; j++) { 40 | result += arguments[j]; 41 | } 42 | //return the sum 43 | return result; 44 | } 45 | //now we're dealing with non-numbers and matrix-like structures: 46 | if (!(Array.isArray(input)) && !(input instanceof Mat) && !(input instanceof Tensor)) { 47 | throw new Error('Exception occurred in sum - input must be a JS array, matrix or tensor'); 48 | } 49 | 50 | //declare raw_in, set it to a input clone if its a Mat otherwise input itself 51 | let in_type = (input instanceof Mat); 52 | let raw_in = (in_type) ? input.clone() : deep_copy(input); 53 | 54 | //check case: make sure there's only 1 arguments (1 matrix like structure) and the argument is an array or Mat 55 | if (arguments.length === 1 && (Array.isArray(arguments[0]) || arguments[0] instanceof Mat)) { 56 | //if its a Mat, as usual, degrade to JS array for generalized code 57 | if (arguments[0] instanceof Mat) { 58 | raw_in = raw_in.val; 59 | } 60 | 61 | //useful for later possibly, number of wrapped arrays in array 62 | //let num_wrap = arguments[0].filter(Array.isArray).length 63 | 64 | /*let num_wrap = (arguments[0].map(e=>e[0])) 65 | num_wrap = num_wrap.length; 66 | print(num_wrap)*/ 67 | 68 | //declare dims to be ndim(raw_in) 69 | let dims = ndim(raw_in) 70 | 71 | //in the case dims === 1 i.e. the Mat or JS array is a LIST, loop through ONE time and sum up the arguments 72 | if (dims === 1) { 73 | let result2 = 0; 74 | for (let k = 0; k < arguments[0].length; k++) { 75 | //sum up the arguments in arguments 0, add to result2 and return it (can name it result but whatever) 76 | result2 = result2 + arguments[0][k]; 77 | } 78 | 79 | return result2; 80 | } 81 | //in the case dims === 2 i.e. the Mat or JS is a 2d matrix, loop through TWICE and sum up arguments as usual. 82 | else if (dims === 2) { 83 | let result3 = 0; 84 | for (let h = 0; h < arguments[0].length; h++) { 85 | for (let g = 0; g < arguments[0][0].length; g++) { 86 | result3 += arguments[0][h][g]; 87 | } 88 | } 89 | return result3; 90 | } 91 | 92 | //last but not least if we have a Tensor (and dims > 2 and is finite): 93 | else if (input instanceof Tensor || (dims > 2 && is_number(dims))) { 94 | //use the raw_in (Mat object) and put it into flatten(raw_in) to get a LIST 95 | raw_in = flatten(raw_in); 96 | //is now in a 1d raw format, sum up the list's elements 97 | let result4 = 0; 98 | for (let n = 0; n < raw_in.length; n++) { 99 | result4 += raw_in[n]; 100 | } 101 | return result4; 102 | } 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /test.hhs: -------------------------------------------------------------------------------- 1 | /** 2 | * @author Jason Reynolds, ... 3 | * 4 | * Function for testing all unit tests 5 | */ 6 | 7 | 8 | function test() { 9 | 10 | 11 | *import math:cholesky_test 12 | *import math:det_test 13 | *import math:dot_divide_test 14 | *import math:dot_multiply_test 15 | *import math:factorial_test 16 | *import math:is_symmetric_test 17 | *import math:is_tril_test 18 | *import math:is_triu_test 19 | *import math:lsolve_test 20 | *import math:lu_test 21 | *import math:lusolve_test 22 | *import math:QR_test 23 | *import math:trace_test 24 | *import math:transpose_test 25 | *import math:usolve_test 26 | *import math:gcd_test 27 | //import math:floor_test --> UNCOMMENT WHEN REMOVED FROM BASE 28 | //import math:ceil_test --> UNCOMMENT WHEN REMOVED FROM BASE 29 | *import math:mean_test 30 | *import math:median_test 31 | *import math:multinomial_test 32 | *import math:reshape_test 33 | *import math:mink_test 34 | *import math:check_input_test 35 | *import math:is_diag_test 36 | *import math:magic_test 37 | *import math:bounds_test 38 | *import math:iqr_test 39 | *import math:quantile_test 40 | *import math:percentile_test 41 | *import math:rms_test 42 | *import math:correcoef_test 43 | *import math:cov_test 44 | *import math:quadratic_equation_test 45 | *import math:cubic_equation_test 46 | *import math:quartic_equation_test 47 | *import math:vander_test 48 | *import math:resize_test 49 | *import math:normalize_test 50 | *import math:is_local_min_test 51 | *import math:is_local_max_test 52 | *import math:smooth_test 53 | *import math:any_missing_test 54 | *import math:is_missing_test 55 | *import math:rmmissing_test 56 | *import math:fillmissing_test 57 | *import math:missing_test 58 | *import math:hankel_test 59 | *import math:standard_missing_test 60 | *import math:pascal_test 61 | 62 | 63 | 64 | cholesky_test(); 65 | det_test(); 66 | dot_divide_test(); 67 | dot_multiply_test(); 68 | factorial_test(); 69 | is_symmetric_test(); 70 | is_tril_test(); 71 | is_triu_test(); 72 | lsolve_test(); 73 | lu_test(); 74 | lusolve_test(); 75 | QR_test(); 76 | trace_test(); 77 | transpose_test(); 78 | usolve_test(); 79 | gcd_test(); 80 | //floor_test(); 81 | //ceil_test(); 82 | mean_test(); 83 | median_test(); 84 | multinomial_test(); 85 | reshape_test(); 86 | mink_test(); 87 | check_input_test(); 88 | is_diag_test(); 89 | magic_test(); 90 | bounds_test(); 91 | iqr_test(); 92 | quantile_test(); 93 | percentile_test(); 94 | rms_test(); 95 | correcoef_test(); 96 | cov_test(); 97 | quadratic_equation_test(); 98 | cubic_equation_test(); 99 | quartic_equation_test(); 100 | resize_test(); 101 | vander_test(); 102 | normalize_test(); 103 | is_local_min_test(); 104 | is_local_max_test(); 105 | any_missing_test(); 106 | smooth_test(); 107 | is_missing_test(); 108 | rmmissing_test(); 109 | fillmissing_test(); 110 | missing_test(); 111 | hankel_test(); 112 | standard_missing_test(); 113 | pascal_test(); 114 | 115 | print("All test cases passed!") 116 | } 117 | -------------------------------------------------------------------------------- /toeplitz.hhs: -------------------------------------------------------------------------------- 1 | /** 2 | * @author Alan Liang 3 | * @param c - Array | 1*n (optional) matrix, first column of final matrix 4 | * @param r - Array | 1*n matrix, first row of final matrix 5 | * @returns - Mat object matrix 6 | * 7 | * if both c and r is given, use them to calculate the toeplitz matrix. 8 | * in this case, if c[0] and r[0] are different, use c[0]. 9 | * 10 | * if only r is given, use it to calculate the toeplitz matrix. 11 | * in this case, the final matrix would be symmetric. 12 | * 13 | * Toeplitz matrices are matrices with identical elements on diagonals. 14 | * 15 | * For example, 16 | * 1 2 3 4 5 17 | * 6 1 2 3 4 18 | * 7 6 1 2 3 19 | * is a Toeplitz matrix. 20 | * 21 | */ 22 | 23 | function toeplitz() 24 | { 25 | // wrong argument number 26 | if (arguments.length === 0) { 27 | throw new Error('Exception occurred in pascal - no argument given'); 28 | } 29 | else if (arguments.length > 2) { 30 | throw new Error('Exception occurred in pascal - wrong argument number'); 31 | } 32 | 33 | if (arguments.length === 1) { 34 | return toeplitz_r(arguments[0]); 35 | } else { 36 | return toeplitz_cr(arguments[0], arguments[1]); 37 | } 38 | 39 | function toeplitz_r(r) { 40 | // Input must be Array, Mat or Tensor 41 | if (!(Array.isArray(r)) && !(r instanceof Mat) && !(r instanceof Tensor)) { 42 | throw new Error('Exception occurred in toeplitz - input must be an array, matrix or tensor'); 43 | } 44 | 45 | // Get the first row of input matrix 46 | if ((r instanceof Mat) || (r instanceof Tensor)) { 47 | r = r.val[0]; 48 | } 49 | 50 | let n = r.length; 51 | 52 | let toeplitzSquare = Array(n).fill(0).map(x => Array(n).fill(0)); 53 | 54 | // Fill the first row 55 | for (let j = 0; j < n; j++) { 56 | toeplitzSquare[0][j] = r[j]; 57 | } 58 | 59 | // Fill other rows 60 | for (let i = 1; i < n; i++) { 61 | for (let j = 0; j < n; j++) { 62 | toeplitzSquare[i][j] = r[Math.abs(i - j)] 63 | } 64 | } 65 | 66 | return new toeplitzSquare; 67 | } 68 | 69 | function toeplitz_cr(c, r) { 70 | // Input must be Array, Mat or Tensor 71 | if (!(Array.isArray(c)) && !(c instanceof Mat) && !(c instanceof Tensor)) { 72 | throw new Error('Exception occurred in toeplitz - input must be an array, matrix or tensor'); 73 | } 74 | 75 | if (!(Array.isArray(r)) && !(r instanceof Mat) && !(r instanceof Tensor)) { 76 | throw new Error('Exception occurred in toeplitz - input must be an array, matrix or tensor'); 77 | } 78 | 79 | // Get the first row of input matrices 80 | if ((c instanceof Mat) || (c instanceof Tensor)) { 81 | c = c.val[0]; 82 | } 83 | 84 | if ((r instanceof Mat) || (r instanceof Tensor)) { 85 | r = r.val[0]; 86 | } 87 | 88 | let m = c.length 89 | let n = r.length; 90 | 91 | let toeplitzMatrix = Array(m).fill(0).map(x => Array(n).fill(0)); 92 | 93 | // Fill the first row 94 | for (let j = 0; j < n; j++) { 95 | toeplitzMatrix[0][j] = r[j]; 96 | } 97 | 98 | // Fill the first column 99 | for (let i = 0; i < m; i++) { 100 | toeplitzMatrix[i][0] = c[i]; 101 | } 102 | 103 | // Fill other rows 104 | for (let i = 1; i < m; i++) { 105 | for (let j = 1; j < n; j++) { 106 | toeplitzMatrix[i][j] = toeplitzMatrix[i-1][j-1] 107 | } 108 | } 109 | 110 | return toeplitzMatrix; 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /trace.hhs: -------------------------------------------------------------------------------- 1 | /** 2 | * @author Jason Reynolds 3 | * @param input - the matrix to evaluate the trace on (sum of diagonal values), has to be square 4 | * @returns - a number which is the sum of the diagonal entries on the matrix, the trace 5 | * 6 | * Simple function for determining the trace of a matrix (recall trace is the sum of diagonal entries on a square 2d matrix) 7 | */ 8 | 9 | 10 | 11 | 12 | function trace(input) { 13 | 14 | *import math: ndim 15 | 16 | if (arguments.length !== 1) { 17 | throw new Error('Exception occurred in trace - wrong argument number'); 18 | } 19 | 20 | if (!(Array.isArray(input)) && !(input instanceof Mat) && !(input instanceof Tensor)) { 21 | throw new Error('Exception occurred in trace - input must be a JS array, matrix or tensor'); 22 | } 23 | 24 | //declare raw_in to be an input clone if Mat or input itself otherwise 25 | let in_type = (input instanceof Mat) || (input instanceof Tensor) 26 | let raw_in = (in_type) ? input.clone() : input; 27 | 28 | //when input is Mat, degrade to JS array 29 | if (in_type) { 30 | raw_in = raw_in.val; 31 | } 32 | 33 | //check: is it square? 0 dims? ndim not equal to 2? 34 | if ((!(ndim(raw_in) === 2)) || !(raw_in.length === raw_in[0].length) || raw_in.length === 0 || raw_in[0].length === 0) { 35 | throw 'Exception occurred in trace - not a square, 2d, non-empty matrix, so trace is undefined.'; 36 | } 37 | //declare trace_val the value we will sum up and return 38 | let trace_val = 0; 39 | 40 | //loop through twice, since its a 2d matrix 41 | for (let i = 0; i < input.length; i++) { 42 | for (let j = 0; j < input[0].length; j++) { 43 | //when i===j i.e. it's a trace value, sum up that value 44 | if (i === j) { 45 | trace_val += raw_in[i][j]; 46 | } 47 | } 48 | } 49 | return trace_val; 50 | } 51 | -------------------------------------------------------------------------------- /trace_test.hhs: -------------------------------------------------------------------------------- 1 | /** 2 | * @author Jason Reynolds 3 | * @param 4 | * @returns 5 | * 6 | * Function unit test for trace, determines if it's adding up the diagonal entries correctly 7 | */ 8 | 9 | 10 | 11 | 12 | function trace_test() { 13 | 14 | *import math: trace 15 | 16 | if (!(trace([[2.1, 3], [3, 6.11]]) === 8.21)) { 17 | throw "Trace unit test has failed." 18 | } 19 | 20 | //check for boundaries? no done in trace 21 | } 22 | 23 | -------------------------------------------------------------------------------- /transpose.hhs: -------------------------------------------------------------------------------- 1 | /** 2 | * @author Jason Reynolds 3 | * @modifiedBy Jing Stone 4 | * @param input - the matrix to transpose, aka make columns->rows and rows->columns. Support list and 2d matrix. 5 | * @returns - the transposed matrix, 2d matrix 6 | * 7 | * Simple function that transposes a matrix and returns it. This does NOT modify the input 8 | */ 9 | 10 | 11 | function transpose(input) { 12 | 13 | *import math: ndim 14 | *import math: deep_copy 15 | 16 | if (arguments.length !== 1) { 17 | throw new Error('Exception occurred in transpose - wrong argument number'); 18 | } 19 | 20 | if (!(Array.isArray(input)) && !(input instanceof Mat) && !(input instanceof Tensor)) { 21 | throw new Error('Exception occurred in transpose - input must be a JS array, matrix or tensor'); 22 | } 23 | 24 | //declare raw_in to be input clone if Mat and input deep copy otherwise 25 | let in_type = (input instanceof Mat) || (input instanceof Tensor); 26 | //if input is Mat degrade to JS array to have the below code work for both JS arrays and Mat objects 27 | /** 28 | * the speed the mat.clone() is a little slow, need some optimization. 29 | */ 30 | let raw_in = (in_type) ? input.clone().val : deep_copy(input); 31 | 32 | 33 | //checks: 0 dims, not square, ndim not === 2? 34 | if (raw_in.length === 0 || raw_in[0].length === 0 || (ndim(raw_in) > 2)) { 35 | throw new Error('Exception occured in transpose - parameter must be non-empty and 1d list or 2d matrix'); 36 | } 37 | 38 | //loop through twice as it's a 2d array 39 | //support for arbitrary two-dimensional arrays 40 | let result = []; 41 | // for one dimensional matrix: [1, 2, 3] --> [[1], [2], [3]] 42 | if(ndim(raw_in) === 1) 43 | { 44 | if(raw_in.length === 1) 45 | { 46 | return (new Mat(raw_in)); 47 | } 48 | for(let i = 0; i < raw_in.length; i++) 49 | { 50 | let temp = []; 51 | temp.push(raw_in[i]); 52 | result.push(temp); 53 | } 54 | } 55 | // for two dimensional matrix: [[1, 2, 3], [4, 5, 6]] --> [[1, 4], [2, 5], [3, 6]] 56 | if(ndim(raw_in) === 2) 57 | { 58 | for (let i = 0; i < raw_in[0].length; i++) { 59 | let temp = []; 60 | for (let j = 0; j < raw_in.length; j++) { 61 | temp.push(raw_in[j][i]); 62 | } 63 | result.push(temp); 64 | } 65 | } 66 | return (new Mat(result)); 67 | } 68 | -------------------------------------------------------------------------------- /transpose_test.hhs: -------------------------------------------------------------------------------- 1 | /** 2 | * @author Jason Reynolds 3 | * @modifiedBy Jing Stone 4 | * @param 5 | * @returns 6 | * 7 | * Function for checking transpose.hhs and making sure it doesnt modify original input 8 | */ 9 | 10 | function transpose_test() { 11 | 12 | *import math: transpose 13 | 14 | //check basic transpose 15 | if (!((transpose([[1, 1, 4], [4, 3, 12], [5, 6, 7]]) === ([[1, 4, 5], [1, 3, 6], [4, 12, 7]])))) { 16 | throw new Error('Unit test failed for transpose.') 17 | } 18 | 19 | let test_1 = [[1, 2], [3, 4]]; 20 | let test_1_result = [[1, 3], [2, 4]]; 21 | 22 | let test_2 = [1, 2]; 23 | let test_2_result = [[1], [2]]; 24 | 25 | let test_3 = [[1, 2, 3, 4], [2, 3, 4, 5], [3, 4, 5, 6]]; 26 | let test_3_result = [[1, 2, 3], [2, 3, 4], [3, 4, 5], [4, 5, 6]]; 27 | 28 | let test_4 = [[1, 2, 3], [4, 5, 6], [7, 8, 9], [9, 10, 11], [11, 12, 13]]; 29 | let test_4_result = [[1, 4, 7, 9, 11], [2, 5, 8, 10, 12], [3, 6, 9, 11, 13]]; 30 | 31 | let test_5 = [1]; 32 | let test_5_result = [1]; 33 | function test(input, result) 34 | { 35 | if(!(transpose(input) === result)) 36 | { 37 | throw new Error('Unit test failed for transpose.') 38 | } 39 | } 40 | 41 | test(test_1, test_1_result); 42 | test(test_2, test_2_result); 43 | test(test_3, test_3_result); 44 | test(test_4, test_4_result); 45 | test(test_5, test_5_result); 46 | // print("transpose test passed!"); 47 | 48 | //check input matrix is not modified after the execution of the transpose 49 | 50 | let a = [[2, 3], [1, 2]]; 51 | 52 | //if (!(inputM === null)) {. //// not working plus it asks for user input which is dumb of me 53 | //make a copy before the transpose 54 | let copy_input = zeros(a.length, a.length).val; 55 | for (i = 0; i < a.length; i++) { 56 | for (j = 0; j < a[0].length; j++) { 57 | copy_input[i][j] = a[i][j]; 58 | } 59 | } 60 | //tranpose the matrix in question 61 | transpose(a); 62 | 63 | //verify that the copy still matches inputM entry by entry 64 | for (h = 0; h < a.length; h++) { 65 | for (k = 0; k < a[0].length; k++) { 66 | if (!(copy_input[h][k] === a[h][k])) { 67 | throw new Error('Unit test for transpose has failed, input was modified'); 68 | } 69 | } 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /usolve.hhs: -------------------------------------------------------------------------------- 1 | /** 2 | * @author Jason Reynolds 3 | * 4 | * @param inputM - the matrix in the Ax=b equation, namely A, to solve for x 5 | * @param input_col - the column vector in the Ax=b equation, namely b, to solve for x 6 | * @returns - the value of 'x', in Ax=b, the solution to the equation. 7 | * 8 | * Function similar to lsolve - it solves an nxn matrix exquation Ax = b for x. May work faster but only allows upper triangular matrices as input 9 | */ 10 | 11 | 12 | 13 | function usolve(inputM, input_col) { 14 | 15 | *import math: is_triu 16 | *import math: ndim 17 | *import math: deep_copy 18 | 19 | if (arguments.length === 0) { 20 | throw new Error('Exception occurred in usolve - no argument given'); 21 | } 22 | else if (arguments.length !== 2) { 23 | throw new Error('Exception occurred in usolve - wrong argument number'); 24 | } 25 | 26 | //declare raw_in and raw_in_col : they are either clones of inputM and input_col respectively if Mat objects or inputM, input_col themselves otherwise 27 | let in_type_M = (inputM instanceof Mat) || (inputM instanceof Tensor); 28 | let raw_in = (in_type_M) ? inputM.clone() : deep_copy(inputM); 29 | let in_type_col = (input_col instanceof Mat) || (input_col instanceof Tensor); 30 | let raw_in_col = (in_type_col) ? input_col.clone() : deep_copy(input_col); 31 | 32 | 33 | //if any input is Mat, degrade to JS array for generalized code 34 | if (in_type_M) { 35 | raw_in = raw_in.val; 36 | } 37 | if (in_type_col) { 38 | raw_in_col = raw_in_col.val; 39 | } 40 | 41 | //make sure that the input array is upper triangular (this checks: 2d, 0 length dims, square) 42 | if (!(is_triu(raw_in))) { 43 | throw new Error('Exception occurred in usolve - non upper triangular matrix in input'); 44 | } 45 | //Double check, good practice 46 | //check: 0 dims? square? column length === row/col length of matrix? if not throw error 47 | if (raw_in.length === 0 || raw_in[0].length === 0 || !(raw_in.length === raw_in[0].length) || !(raw_in_col.length === raw_in.length)) { 48 | throw new Error('Exception occurred in usolve - wrong dimensions, and need nxn matrix, may want to try lusolveAll or check column vector'); 49 | } 50 | //check: is column not 2d? is it not a -column- vector? that is, does row have more than 1 entry? if so, throw error 51 | if (!(ndim(raw_in_col) === 2) || !(raw_in_col[0].length === 1)) { 52 | throw new Error('Exception occurred in usolve - column vector is not of correct input. make sure it is a column and not row vector, of the form [[n1], [n2], ... , [n]]'); 53 | } 54 | //declare the result to be the output of lsolve 55 | let result = mathjs.usolve(raw_in, raw_in_col); 56 | //return result as a Mat 57 | return new Mat(result); 58 | } 59 | -------------------------------------------------------------------------------- /usolve_test.hhs: -------------------------------------------------------------------------------- 1 | /** 2 | * @author Jason Reynolds 3 | * @param 4 | * @returns 5 | * 6 | * Function unit test for usolve - solves upper triangular matrix Ax=b cases 7 | * 8 | */ 9 | 10 | 11 | 12 | function usolve_test() { 13 | 14 | *import math: usolve 15 | 16 | let a = new Mat([[3, 2, 1], [0, 1, 3], [0, 0, 2]]); 17 | let b = new Mat([[3], [4], [5]]); 18 | if (!(usolve(a, b) === mat([[2.5], [-3.5], [2.5]]))) { 19 | throw new Error('Unit test failed for usolve_test') 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /vander.hhs: -------------------------------------------------------------------------------- 1 | /** 2 | * @author Jianan Lin (林家南) 3 | * @param input - an array with numbers, can be [1, 2, 3], [[1, 2, 3]] or [[1], [2], [3]] 4 | * @param dim - a number denoting the largest exponential, i.e. x^dim 5 | * @returns - a matrix with size = input.length * (dim + 1) in the form of 6 | * [[1, x1, x1^2, ..., x1^dim], ..., [1, xm, xm^2, ..., xm^dim]] 7 | * 8 | */ 9 | 10 | 11 | function vander(input, dim = 0) { 12 | 13 | *import math: flatten 14 | *import math: ndim 15 | *import math: deep_copy 16 | 17 | // wrong argument number 18 | if (arguments.length === 0) { 19 | throw new Error('Exception occurred in vander - no argument given'); 20 | } 21 | else if (arguments.length > 2) { 22 | throw new Error('Exception occurred in vander - wrong argument number'); 23 | } 24 | 25 | // type check 26 | if (!(Array.isArray(input)) && !(input instanceof Mat) && !(input instanceof Tensor)) { 27 | throw new Error('Exception occurred in vander - argument[0] is not a Mat, Tensor or JS Array'); 28 | } 29 | 30 | if (!(typeof dim === 'number') || dim < 0 || parseInt(dim) !== dim) { 31 | throw new Error('Exception occurred in vander - argument[1] must be a non-negative integer'); 32 | } 33 | 34 | // process the input 35 | let in_type = (input instanceof Mat) || (input instanceof Tensor); 36 | let raw_in = (in_type) ? input.clone().val : deep_copy(input); 37 | raw_in = flatten(raw_in); 38 | let length = raw_in.length; 39 | 40 | let result = []; 41 | for (let i = 0; i < length; i++) { 42 | let temp = [1]; 43 | for (let j = 1; j <= dim; j++) { 44 | temp.push(temp[j - 1] * raw_in[i]); 45 | } 46 | result.push(temp) 47 | } 48 | 49 | return mat(result); 50 | } 51 | -------------------------------------------------------------------------------- /vander_test.hhs: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author Jianan Lin 3 | * test for iqr function 4 | */ 5 | 6 | function vander_test() { 7 | 8 | *import math: vander 9 | 10 | if (!(vander([1, 2], 2) === mat([[1, 1, 1], [1, 2, 4]]))) { 11 | throw new Error('Unit test failed for vander'); 12 | } 13 | 14 | print('vander test pass'); 15 | } 16 | --------------------------------------------------------------------------------