diff --git a/README.md b/README.md index 27520fe..bd85cb2 100644 --- a/README.md +++ b/README.md @@ -98,6 +98,47 @@ Convert between Celsius (°C), Fahrenheit (°F), and Kelvin (K). - **PI** Math.PI constant (≈3.14159). +### 📊 Interpoolation and regression + +- **interpolation(input: { function: (number) => number, value: number, points: number[] })** + Interpolates a set of data points from a function and value through Newton Interpolation. + +```js +interpolation({ + function: Math.log, + value: 2, + points: [1, 4, 6] +}) + +➔ 0.5658443469009827 +``` + +- **regression(input: number[])** + Computes the linear and polynomial regression from a set of data points. + +```js +regression([ + [-2, -1], + [1, 2], + [4, 59], + [-1, 4], + [3, 24], + [-4, -53] +]); + +➔ { + linear: { + m: 10.97864768683274, + b: 4.00355871886121 + }, + polynomial: { + a: 6.689189189189188, + b: 11.060810810810809, + c: -0.34459459459459435 + } +} +``` + ### 📊 Population Statistics - **populationDensity(population, area)** diff --git a/src/area.ts b/src/area.ts index ec3d52d..5523bc6 100644 --- a/src/area.ts +++ b/src/area.ts @@ -5,46 +5,50 @@ import { PI, squared } from './numbers.js'; */ export const area = { /** - * @param base Size of the base of the rectangle - * @param height The height of the rectangle + * @param {number} base Size of the base of the rectangle + * @param {number} height The height of the rectangle * - * @returns base * height + * @returns {number} base * height */ rect: (base: number, height: number): number => { return Math.floor(base * height); }, + /** - * @param base Size of the base of the triangle - * @param height The height of the triangle + * @param {number} base Size of the base of the triangle + * @param {number} height The height of the triangle * - * @returns (base * height) / 2 + * @returns {number} (base * height) / 2 */ triangle: (base: number, height: number): number => { return Math.floor((base * height) / 2); }, + /** - * @param D larger diagonal - * @param d smaller diagonal + * @param {number} D larger diagonal + * @param {number} d smaller diagonal * - * @returns (D * d) / 2 + * @returns {number} (D * d) / 2 */ rhombus: (D: number, d: number): number => { return Math.floor((D * d) / 2); }, + /** - * @param B Larger base - * @param b Smaller base - * @param height Trapezoid height + * @param {number} B Larger base + * @param {number} b Smaller base + * @param {number} height Trapezoid height * - * @returns ((B + b) * height) / 2 + * @returns {number} ((B + b) * height) / 2 */ trapezoid: (B: number, b: number, height: number): number => { return Math.floor(((B + b) * height) / 2); }, + /** - * @param radius Circle radius + * @param {number} radius Circle radius * - * @returns π * (radius * radius) + * @returns {number} π * (radius * radius) */ circle: (radius: number): number => { return Math.floor(PI * squared(radius)); diff --git a/src/index.ts b/src/index.ts index 7a826ea..58a6ca7 100644 --- a/src/index.ts +++ b/src/index.ts @@ -2,3 +2,5 @@ export * from './temperatures.js'; export * from './numbers.js'; export * from './area.js'; export * from './population.js'; +export * from './interpolation.js'; +export * from './regression.js'; diff --git a/src/interpolation.ts b/src/interpolation.ts new file mode 100644 index 0000000..d9eff8c --- /dev/null +++ b/src/interpolation.ts @@ -0,0 +1,49 @@ +export interface InterpolationInput { + function: (input: number) => number; + value: number; + points: number[]; +} + +function getNewtonInterpolationB( + input: InterpolationInput, + end: number, + start: number, +): number { + if (end == start) { + return input.function(input.points[start]!); + } + + return ( + (getNewtonInterpolationB(input, end, start + 1) - + getNewtonInterpolationB(input, end - 1, start)) / + (input.points[end]! - input.points[start]!) + ); +} + +/** + * @param {InterpolationInput} input Interpolation input + * + * @returns {number} Interpolation result through Newton Interpolation + */ +export function interpolation(input: InterpolationInput): number { + if (input.points.length < 1) { + throw new Error('Points array must not be empty.'); + } + + let multiplier, + output = input.function(input.points[0]!); + + for (let multiplierIndex, index = 1; index < input.points.length; index++) { + for ( + multiplier = 1, multiplierIndex = 0; + multiplierIndex < index; + multiplierIndex++ + ) { + multiplier *= input.value - input.points[multiplierIndex]!; + } + + output += multiplier * getNewtonInterpolationB(input, multiplierIndex, 0); + } + + return output; +} diff --git a/src/numbers.ts b/src/numbers.ts index 33522c5..29ab6a0 100644 --- a/src/numbers.ts +++ b/src/numbers.ts @@ -1,49 +1,58 @@ export const PI = Math.PI; /** - * @param number The value of the number - * @returns boolean - * If it is even + * @param {number} number The value of the number + * + * @returns {boolean} If it is even */ export function isEven(number: number): boolean { return number % 2 == 0; } + /** - * @param number The value of the number - * @returns boolean - * If it is odd + * @param {number} number The value of the number + * + * @returns {boolean} If it is odd */ export function isOdd(number: number): boolean { return !isEven(number); } + /** - * @param x First number - * @param y Second number - * @returns The difference value between X and Y + * @param {number} x First number + * @param {number} y Second number + * + * @returns {number} The difference value between X and Y */ export function difference(x: number, y: number): number { return Math.max(x - y); } + /** - * @param x Target value - * @param times Times that the value can be multiplied by itself - * @returns The squared value + * @param {number} x Target value + * @param {number} [times] Times that the value can be multiplied by itself + * + * @returns {number} The squared value */ export function squared(x: number, times?: number): number { return Math.pow(x, times ?? 2); } + /** - * @param C Adjacent Cathetus - * @param c Opposite Cathetus - * @returns Value of the Hypotenuse + * @param {number} C Adjacent Cathetus + * @param {number} c Opposite Cathetus + * + * @returns {number} Value of the Hypotenuse */ export function hypotenuse(C: number, c: number): number { var h = squared(C) + squared(c); return Math.sqrt(h); } + export function cathetus(H: number, C: number): number { if (H <= C) throw new Error('Cathetus cannot be greater or equal than Hypotenuse.'); + return Math.sqrt(squared(H) - squared(C)); } diff --git a/src/regression.ts b/src/regression.ts new file mode 100644 index 0000000..ff4df3b --- /dev/null +++ b/src/regression.ts @@ -0,0 +1,112 @@ +export interface LinearRegressionOutput { + m: number; + b: number; +} + +export interface PolynomialRegressionOutput { + a: number; + b: number; + c: number; +} + +export interface RegressionOutput { + linear: LinearRegressionOutput; + polynomial: PolynomialRegressionOutput; +} + +function computePolynomialRegressionMatrix(matrix: number[]) { + let pivot, eliminationPivot; + + for (let i = 0, j, k; i < 3; i++) { + const pivotStartIndex = i * 4; + pivot = matrix[pivotStartIndex + i]!; + + for (j = 0; j < 4; j++) { + matrix[pivotStartIndex + j]! /= pivot; + } + + for (j = 0; j < 3; j++) { + if (j == i) { + continue; + } + + const nonPivotStartIndex = j * 4; + eliminationPivot = matrix[nonPivotStartIndex + i]!; + + for (k = i; k < 4; k++) { + matrix[nonPivotStartIndex + k]! -= + eliminationPivot * matrix[pivotStartIndex + k]!; + } + } + } +} + +/** + * @param {number[][]} input Regression input, must be an array of [x, y] + * + * @returns {RegressionOutput} Regression result for both linear and polynomial regression + */ +export function regression(input: number[][]): RegressionOutput { + if (input.length === 0) { + throw new Error('Input array must not be empty.'); + } + + let x, + y, + mDivisor, + sumX = 0, + sumY = 0, + sumXY = 0, + sumXSquared = 0, + sumXCubed = 0, + sumXPower4 = 0, + sumXSquaredY = 0; + + for (let index = 0; index < input.length; index++) { + x = input[index]![0]!; + y = input[index]![1]!; + + sumX += x; + sumY += y; + sumXY += x * y; + sumXSquared += x * x; + sumXCubed += x * x * x; + sumXPower4 += x * x * x * x; + sumXSquaredY += x * x * y; + } + + const m = + (mDivisor = input.length * sumXSquared - sumX * sumX) === 0 + ? 0 + : (input.length * sumXY - sumX * sumY) / mDivisor; + const b = (sumY - m * sumX) / input.length; + + const polMatrix = [ + input.length, + sumX, + sumXSquared, + sumY, + sumX, + sumXSquared, + sumXCubed, + sumXY, + sumXSquared, + sumXCubed, + sumXPower4, + sumXSquaredY, + ]; + + computePolynomialRegressionMatrix(polMatrix); + + return { + linear: { + m, + b, + }, + polynomial: { + a: polMatrix[3]!, + b: polMatrix[7]!, + c: polMatrix[11]!, + }, + }; +} diff --git a/src/temperatures.ts b/src/temperatures.ts index e141457..b65ceb9 100644 --- a/src/temperatures.ts +++ b/src/temperatures.ts @@ -3,32 +3,41 @@ */ /** - * @param temperature The temperature you want to convert to Celsius - * @param unit The temperature unit you want to convert + * @param {number} temperature The temperature you want to convert to Celsius + * @param {TemperatureUnits} [unit] The temperature unit you want to convert + * + * @returns {number} */ function toCelsius(temperature: number, unit?: TemperatureUnits): number { if (unit === 'C') return temperature; if (unit === 'K') return Math.floor(temperature - 273.15); + return Math.max(((temperature - 32) * 5) / 9); } /** - * @param temperature The temperature you want to convert to Celsius - * @param unit The temperature unit you want to convert + * @param {number} temperature The temperature you want to convert to Celsius + * @param {TemperatureUnits} [unit] The temperature unit you want to convert + * + * @returns {number} */ function toFahrenheit(temperature: number, unit?: TemperatureUnits): number { if (unit === 'F') return temperature; if (unit === 'K') return Math.floor((temperature * 5) / 9 + 459.67); + return Math.max((temperature * 9) / 5 + 32); } /** - * @param temperature The temperature you want to convert to Celsius - * @param unit The temperature unit you want to convert + * @param {number} temperature The temperature you want to convert to Celsius + * @param {TemperatureUnits} [unit] The temperature unit you want to convert + * + * @returns {number} */ function toKelvin(temperature: number, unit?: TemperatureUnits): number { if (unit === 'K') return temperature; if (unit === 'F') return Math.floor(((temperature + 459.67) * 5) / 9); + return Math.max(temperature + 273.15); }