commit
message
[CONVERT, etc] formula added and tested. formulajs no longer needed
author
Ben Vogt <[email protected]>
date
2017-04-29 22:59:57
stats
5 file(s) changed,
321 insertions(+),
11 deletions(-)
files
package.json
src/RawFormulas/Financial.ts
src/RawFormulas/RawFormulas.ts
src/RawFormulas/Text.ts
tests/TextTest.ts
1diff --git a/package.json b/package.json
2index 5fdc4a4..2e1e34c 100644
3--- a/package.json
4+++ b/package.json
5@@ -9,7 +9,6 @@
6 "author": "vogtb <bvogt at gmail.com>",
7 "license": "MIT",
8 "dependencies": {
9- "formulajs": "^1.0.8",
10 "moment": "^2.17.1"
11 }
12 }
13diff --git a/src/RawFormulas/Financial.ts b/src/RawFormulas/Financial.ts
14index 2823234..4e1f5c2 100644
15--- a/src/RawFormulas/Financial.ts
16+++ b/src/RawFormulas/Financial.ts
17@@ -374,6 +374,8 @@ var CUMIPMT = function (...values) : number {
18 * * https://support.office.com/en-us/article/ACCRINT-function-fe45d089-6722-4fb3-9379-e1f911d8dc74
19 *
20 * * https://quant.stackexchange.com/questions/7040/whats-the-algorithm-behind-excels-accrint
21+ *
22+ * * https://support.google.com/docs/answer/3093200
23 * @param values[0] issue - The date the security was initially issued.
24 * @param values[1] first_payment - The first date interest will be paid.
25 * @param values[2] settlement - The settlement date of the security, the date after issuance when the security is
26diff --git a/src/RawFormulas/RawFormulas.ts b/src/RawFormulas/RawFormulas.ts
27index 935efce..5cc850d 100644
28--- a/src/RawFormulas/RawFormulas.ts
29+++ b/src/RawFormulas/RawFormulas.ts
30@@ -1,4 +1,3 @@
31-import * as Formula from "formulajs"
32 import {
33 ABS,
34 ACOS,
35@@ -107,7 +106,8 @@ import {
36 CHAR,
37 CODE,
38 SPLIT,
39- CONCATENATE
40+ CONCATENATE,
41+ CONVERT
42 } from "./Text"
43 import {
44 DATE,
45@@ -136,12 +136,11 @@ import {
46 WORKDAY$INTL
47 } from "./Date"
48
49-var CONVERT = Formula["CONVERT"];
50-
51-
52 // Using alias to bind dot-notation function names.
53 var __COMPLEX = {
54- "F.DIST": FDIST$LEFTTAILED
55+ "F.DIST": FDIST$LEFTTAILED,
56+ "NETWORKDAYS.INTL": NETWORKDAYS$INTL,
57+ "WORKDAY.INTL": WORKDAY$INTL
58 };
59
60 export {
61diff --git a/src/RawFormulas/Text.ts b/src/RawFormulas/Text.ts
62index 999ffe4..dec14b8 100644
63--- a/src/RawFormulas/Text.ts
64+++ b/src/RawFormulas/Text.ts
65@@ -3,7 +3,10 @@ import {
66 TypeCaster
67 } from "./Utils";
68 import {
69- ValueError, NumError, RefError
70+ ValueError,
71+ NumError,
72+ RefError,
73+ NAError
74 } from "../Errors";
75
76 /**
77@@ -124,10 +127,310 @@ var CONCATENATE = function (...values) : string {
78 return string;
79 };
80
81+/**
82+ * Converts a numeric value to a different unit of measure.
83+ * @param values[0] value - the numeric value in start_unit to convert to end_unit.
84+ * @param values[1] start_unit - The starting unit, the unit currently assigned to value.
85+ * @param values[2] end_unit - The unit of measure into which to convert value.
86+ * @returns {number}
87+ * @constructor
88+ * TODO: Looking up units is not efficient at all. We should use an object instead of iterating through an array.
89+ */
90+var CONVERT = function (...values) {
91+ ArgsChecker.checkLength(values, 3);
92+ var n = TypeCaster.firstValueAsNumber(values[0]);
93+ var fromUnit = TypeCaster.firstValueAsString(values[1]);
94+ var toUnit = TypeCaster.firstValueAsString(values[2]);
95+
96+ // NOTE: A lot of the code for this method is from https://github.com/sutoiku/formula.js. I'm relying on them to have
97+ // gotten it right, but I'm spot checking some of their work against GS, MSE, LibreOffice, OpenOffice.
98+
99+ // List of units supported by CONVERT and units defined by the International System of Units
100+ // [Name, Symbol, Alternate symbols, Quantity, ISU, CONVERT, Conversion ratio]
101+ var units = [
102+ ["a.u. of action", "?", null, "action", false, false, 1.05457168181818e-34],
103+ ["a.u. of charge", "e", null, "electric_charge", false, false, 1.60217653141414e-19],
104+ ["a.u. of energy", "Eh", null, "energy", false, false, 4.35974417757576e-18],
105+ ["a.u. of length", "a?", null, "length", false, false, 5.29177210818182e-11],
106+ ["a.u. of mass", "m?", null, "mass", false, false, 9.10938261616162e-31],
107+ ["a.u. of time", "?/Eh", null, "time", false, false, 2.41888432650516e-17],
108+ ["admiralty knot", "admkn", null, "speed", false, true, 0.514773333],
109+ ["ampere", "A", null, "electric_current", true, false, 1],
110+ ["ampere per meter", "A/m", null, "magnetic_field_intensity", true, false, 1],
111+ ["ångström", "Å", ["ang"], "length", false, true, 1e-10],
112+ ["are", "ar", null, "area", false, true, 100],
113+ ["astronomical unit", "ua", null, "length", false, false, 1.49597870691667e-11],
114+ ["bar", "bar", null, "pressure", false, false, 100000],
115+ ["barn", "b", null, "area", false, false, 1e-28],
116+ ["becquerel", "Bq", null, "radioactivity", true, false, 1],
117+ ["bit", "bit", ["b"], "information", false, true, 1],
118+ ["btu", "BTU", ["btu"], "energy", false, true, 1055.05585262],
119+ ["byte", "byte", null, "information", false, true, 8],
120+ ["candela", "cd", null, "luminous_intensity", true, false, 1],
121+ ["candela per square metre", "cd/m?", null, "luminance", true, false, 1],
122+ ["coulomb", "C", null, "electric_charge", true, false, 1],
123+ ["cubic ångström", "ang3", ["ang^3"], "volume", false, true, 1e-30],
124+ ["cubic foot", "ft3", ["ft^3"], "volume", false, true, 0.028316846592],
125+ ["cubic inch", "in3", ["in^3"], "volume", false, true, 0.000016387064],
126+ ["cubic light-year", "ly3", ["ly^3"], "volume", false, true, 8.46786664623715e-47],
127+ ["cubic metre", "m?", null, "volume", true, true, 1],
128+ ["cubic mile", "mi3", ["mi^3"], "volume", false, true, 4168181825.44058],
129+ ["cubic nautical mile", "Nmi3", ["Nmi^3"], "volume", false, true, 6352182208],
130+ ["cubic Pica", "Pica3", ["Picapt3", "Pica^3", "Picapt^3"], "volume", false, true, 7.58660370370369e-8],
131+ ["cubic yard", "yd3", ["yd^3"], "volume", false, true, 0.764554857984],
132+ ["cup", "cup", null, "volume", false, true, 0.0002365882365],
133+ ["dalton", "Da", ["u"], "mass", false, false, 1.66053886282828e-27],
134+ ["day", "d", ["day"], "time", false, true, 86400],
135+ ["degree", "°", null, "angle", false, false, 0.0174532925199433],
136+ ["degrees Rankine", "Rank", null, "temperature", false, true, 0.555555555555556],
137+ ["dyne", "dyn", ["dy"], "force", false, true, 0.00001],
138+ ["electronvolt", "eV", ["ev"], "energy", false, true, 1.60217656514141],
139+ ["ell", "ell", null, "length", false, true, 1.143],
140+ ["erg", "erg", ["e"], "energy", false, true, 1e-7],
141+ ["farad", "F", null, "electric_capacitance", true, false, 1],
142+ ["fluid ounce", "oz", null, "volume", false, true, 0.0000295735295625],
143+ ["foot", "ft", null, "length", false, true, 0.3048],
144+ ["foot-pound", "flb", null, "energy", false, true, 1.3558179483314],
145+ ["gal", "Gal", null, "acceleration", false, false, 0.01],
146+ ["gallon", "gal", null, "volume", false, true, 0.003785411784],
147+ ["gauss", "G", ["ga"], "magnetic_flux_density", false, true, 1],
148+ ["grain", "grain", null, "mass", false, true, 0.0000647989],
149+ ["gram", "g", null, "mass", false, true, 0.001],
150+ ["gray", "Gy", null, "absorbed_dose", true, false, 1],
151+ ["gross registered ton", "GRT", ["regton"], "volume", false, true, 2.8316846592],
152+ ["hectare", "ha", null, "area", false, true, 10000],
153+ ["henry", "H", null, "inductance", true, false, 1],
154+ ["hertz", "Hz", null, "frequency", true, false, 1],
155+ ["horsepower", "HP", ["h"], "power", false, true, 745.69987158227],
156+ ["horsepower-hour", "HPh", ["hh", "hph"], "energy", false, true, 2684519.538],
157+ ["hour", "h", ["hr"], "time", false, true, 3600],
158+ ["imperial gallon (U.K.)", "uk_gal", null, "volume", false, true, 0.00454609],
159+ ["imperial hundredweight", "lcwt", ["uk_cwt", "hweight"], "mass", false, true, 50.802345],
160+ ["imperial quart (U.K)", "uk_qt", null, "volume", false, true, 0.0011365225],
161+ ["imperial ton", "brton", ["uk_ton", "LTON"], "mass", false, true, 1016.046909],
162+ ["inch", "in", null, "length", false, true, 0.0254],
163+ ["international acre", "uk_acre", null, "area", false, true, 4046.8564224],
164+ ["IT calorie", "cal", null, "energy", false, true, 4.1868],
165+ ["joule", "J", null, "energy", true, true, 1],
166+ ["katal", "kat", null, "catalytic_activity", true, false, 1],
167+ ["kelvin", "K", ["kel"], "temperature", true, true, 1],
168+ ["kilogram", "kg", null, "mass", true, true, 1],
169+ ["knot", "kn", null, "speed", false, true, 0.514444444444444],
170+ ["light-year", "ly", null, "length", false, true, 9460730472580800],
171+ ["litre", "L", ["l", "lt"], "volume", false, true, 0.001],
172+ ["lumen", "lm", null, "luminous_flux", true, false, 1],
173+ ["lux", "lx", null, "illuminance", true, false, 1],
174+ ["maxwell", "Mx", null, "magnetic_flux", false, false, 1e-18],
175+ ["measurement ton", "MTON", null, "volume", false, true, 1.13267386368],
176+ ["meter per hour", "m/h", ["m/hr"], "speed", false, true, 0.00027777777777778],
177+ ["meter per second", "m/s", ["m/sec"], "speed", true, true, 1],
178+ ["meter per second squared", "m?s??", null, "acceleration", true, false, 1],
179+ ["parsec", "pc", ["parsec"], "length", false, true, 30856775814671900],
180+ ["meter squared per second", "m?/s", null, "kinematic_viscosity", true, false, 1],
181+ ["metre", "m", null, "length", true, true, 1],
182+ ["miles per hour", "mph", null, "speed", false, true, 0.44704],
183+ ["millimetre of mercury", "mmHg", null, "pressure", false, false, 133.322],
184+ ["minute", "?", null, "angle", false, false, 0.000290888208665722],
185+ ["minute", "min", ["mn"], "time", false, true, 60],
186+ ["modern teaspoon", "tspm", null, "volume", false, true, 0.000005],
187+ ["mole", "mol", null, "amount_of_substance", true, false, 1],
188+ ["morgen", "Morgen", null, "area", false, true, 2500],
189+ ["n.u. of action", "?", null, "action", false, false, 1.05457168181818e-34],
190+ ["n.u. of mass", "m?", null, "mass", false, false, 9.10938261616162e-31],
191+ ["n.u. of speed", "c?", null, "speed", false, false, 299792458],
192+ ["n.u. of time", "?/(me?c??)", null, "time", false, false, 1.28808866778687e-21],
193+ ["nautical mile", "M", ["Nmi"], "length", false, true, 1852],
194+ ["newton", "N", null, "force", true, true, 1],
195+ ["œrsted", "Oe ", null, "magnetic_field_intensity", false, false, 79.5774715459477],
196+ ["ohm", "Ω", null, "electric_resistance", true, false, 1],
197+ ["ounce mass", "ozm", null, "mass", false, true, 0.028349523125],
198+ ["pascal", "Pa", null, "pressure", true, false, 1],
199+ ["pascal second", "Pa?s", null, "dynamic_viscosity", true, false, 1],
200+ ["pferdestärke", "PS", null, "power", false, true, 735.49875],
201+ ["phot", "ph", null, "illuminance", false, false, 0.0001],
202+ ["pica (1/6 inch)", "pica", null, "length", false, true, 0.00035277777777778],
203+ ["pica (1/72 inch)", "Pica", ["Picapt"], "length", false, true, 0.00423333333333333],
204+ ["poise", "P", null, "dynamic_viscosity", false, false, 0.1],
205+ ["pond", "pond", null, "force", false, true, 0.00980665],
206+ ["pound force", "lbf", null, "force", false, true, 4.4482216152605],
207+ ["pound mass", "lbm", null, "mass", false, true, 0.45359237],
208+ ["quart", "qt", null, "volume", false, true, 0.000946352946],
209+ ["radian", "rad", null, "angle", true, false, 1],
210+ ["second", "?", null, "angle", false, false, 0.00000484813681109536],
211+ ["second", "s", ["sec"], "time", true, true, 1],
212+ ["short hundredweight", "cwt", ["shweight"], "mass", false, true, 45.359237],
213+ ["siemens", "S", null, "electrical_conductance", true, false, 1],
214+ ["sievert", "Sv", null, "equivalent_dose", true, false, 1],
215+ ["slug", "sg", null, "mass", false, true, 14.59390294],
216+ ["square ångström", "ang2", ["ang^2"], "area", false, true, 1e-20],
217+ ["square foot", "ft2", ["ft^2"], "area", false, true, 0.09290304],
218+ ["square inch", "in2", ["in^2"], "area", false, true, 0.00064516],
219+ ["square light-year", "ly2", ["ly^2"], "area", false, true, 8.95054210748189e+31],
220+ ["square meter", "m?", null, "area", true, true, 1],
221+ ["square meter", "m^2", null, "area", true, true, 1], // Added by @vogtb.
222+ ["square mile", "mi2", ["mi^2"], "area", false, true, 2589988.110336],
223+ ["square nautical mile", "Nmi2", ["Nmi^2"], "area", false, true, 3429904],
224+ ["square Pica", "Pica2", ["Picapt2", "Pica^2", "Picapt^2"], "area", false, true, 0.00001792111111111],
225+ ["square yard", "yd2", ["yd^2"], "area", false, true, 0.83612736],
226+ ["statute mile", "mi", null, "length", false, true, 1609.344],
227+ ["steradian", "sr", null, "solid_angle", true, false, 1],
228+ ["stilb", "sb", null, "luminance", false, false, 0.0001],
229+ ["stokes", "St", null, "kinematic_viscosity", false, false, 0.0001],
230+ ["stone", "stone", null, "mass", false, true, 6.35029318],
231+ ["tablespoon", "tbs", null, "volume", false, true, 0.0000147868],
232+ ["teaspoon", "tsp", null, "volume", false, true, 0.00000492892],
233+ ["tesla", "T", null, "magnetic_flux_density", true, true, 1],
234+ ["thermodynamic calorie", "c", null, "energy", false, true, 4.184],
235+ ["ton", "ton", null, "mass", false, true, 907.18474],
236+ ["tonne", "t", null, "mass", false, false, 1000],
237+ ["U.K. pint", "uk_pt", null, "volume", false, true, 0.00056826125],
238+ ["U.S. bushel", "bushel", null, "volume", false, true, 0.03523907],
239+ ["U.S. oil barrel", "barrel", null, "volume", false, true, 0.158987295],
240+ ["U.S. pint", "pt", ["us_pt"], "volume", false, true, 0.000473176473],
241+ ["U.S. survey mile", "survey_mi", null, "length", false, true, 1609.347219],
242+ ["U.S. survey/statute acre", "us_acre", null, "area", false, true, 4046.87261],
243+ ["volt", "V", null, "voltage", true, false, 1],
244+ ["watt", "W", null, "power", true, true, 1],
245+ ["watt-hour", "Wh", ["wh"], "energy", false, true, 3600],
246+ ["weber", "Wb", null, "magnetic_flux", true, false, 1],
247+ ["yard", "yd", null, "length", false, true, 0.9144],
248+ ["year", "yr", null, "time", false, true, 31557600]
249+ ];
250+
251+ // Binary prefixes
252+ // [Name, Prefix power of 2 value, Previx value, Abbreviation, Derived from]
253+ var binary_prefixes = {
254+ Yi: ["yobi", 80, 1208925819614629174706176, "Yi", "yotta"],
255+ Zi: ["zebi", 70, 1180591620717411303424, "Zi", "zetta"],
256+ Ei: ["exbi", 60, 1152921504606846976, "Ei", "exa"],
257+ Pi: ["pebi", 50, 1125899906842624, "Pi", "peta"],
258+ Ti: ["tebi", 40, 1099511627776, "Ti", "tera"],
259+ Gi: ["gibi", 30, 1073741824, "Gi", "giga"],
260+ Mi: ["mebi", 20, 1048576, "Mi", "mega"],
261+ ki: ["kibi", 10, 1024, "ki", "kilo"]
262+ };
263+
264+ // Unit prefixes
265+ // [Name, Multiplier, Abbreviation]
266+ var unit_prefixes = {
267+ Y: ["yotta", 1e+24, "Y"],
268+ Z: ["zetta", 1e+21, "Z"],
269+ E: ["exa", 1e+18, "E"],
270+ P: ["peta", 1e+15, "P"],
271+ T: ["tera", 1e+12, "T"],
272+ G: ["giga", 1e+09, "G"],
273+ M: ["mega", 1e+06, "M"],
274+ k: ["kilo", 1e+03, "k"],
275+ h: ["hecto", 1e+02, "h"],
276+ e: ["dekao", 1e+01, "e"],
277+ d: ["deci", 1e-01, "d"],
278+ c: ["centi", 1e-02, "c"],
279+ m: ["milli", 1e-03, "m"],
280+ u: ["micro", 1e-06, "u"],
281+ n: ["nano", 1e-09, "n"],
282+ p: ["pico", 1e-12, "p"],
283+ f: ["femto", 1e-15, "f"],
284+ a: ["atto", 1e-18, "a"],
285+ z: ["zepto", 1e-21, "z"],
286+ y: ["yocto", 1e-24, "y"]
287+ };
288+
289+ // Initialize units and multipliers
290+ var from = null;
291+ var to = null;
292+ var base_from_unit = fromUnit;
293+ var base_to_unit = toUnit;
294+ var from_multiplier = 1;
295+ var to_multiplier = 1;
296+ var alt;
297+
298+ // Lookup from and to units
299+ for (var i = 0; i < units.length; i++) {
300+ alt = (units[i][2] === null) ? [] : units[i][2];
301+ if (units[i][1] === base_from_unit || alt.indexOf(base_from_unit) >= 0) {
302+ from = units[i];
303+ }
304+ if (units[i][1] === base_to_unit || alt.indexOf(base_to_unit) >= 0) {
305+ to = units[i];
306+ }
307+ }
308+
309+ // Lookup from prefix
310+ if (from === null) {
311+ var from_binary_prefix = binary_prefixes[fromUnit.substring(0, 2)];
312+ var from_unit_prefix = unit_prefixes[fromUnit.substring(0, 1)];
313+
314+ // Handle dekao unit prefix (only unit prefix with two characters)
315+ if (fromUnit.substring(0, 2) === 'da') {
316+ from_unit_prefix = ["dekao", 1e+01, "da"];
317+ }
318+
319+ // Handle binary prefixes first (so that 'Yi' is processed before 'Y')
320+ if (from_binary_prefix) {
321+ from_multiplier = from_binary_prefix[2];
322+ base_from_unit = fromUnit.substring(2);
323+ } else if (from_unit_prefix) {
324+ from_multiplier = from_unit_prefix[1];
325+ base_from_unit = fromUnit.substring(from_unit_prefix[2].length);
326+ }
327+
328+ // Lookup from unit
329+ for (var j = 0; j < units.length; j++) {
330+ alt = (units[j][2] === null) ? [] : units[j][2];
331+ if (units[j][1] === base_from_unit || alt.indexOf(base_from_unit) >= 0) {
332+ from = units[j];
333+ }
334+ }
335+ }
336+
337+ // Lookup to prefix
338+ if (to === null) {
339+ var to_binary_prefix = binary_prefixes[toUnit.substring(0, 2)];
340+ var to_unit_prefix = unit_prefixes[toUnit.substring(0, 1)];
341+
342+ // Handle dekao unit prefix (only unit prefix with two characters)
343+ if (toUnit.substring(0, 2) === 'da') {
344+ to_unit_prefix = ["dekao", 1e+01, "da"];
345+ }
346+
347+ // Handle binary prefixes first (so that 'Yi' is processed before 'Y')
348+ if (to_binary_prefix) {
349+ to_multiplier = to_binary_prefix[2];
350+ base_to_unit = toUnit.substring(2);
351+ } else if (to_unit_prefix) {
352+ to_multiplier = to_unit_prefix[1];
353+ base_to_unit = toUnit.substring(to_unit_prefix[2].length);
354+ }
355+
356+ // Lookup to unit
357+ for (var k = 0; k < units.length; k++) {
358+ alt = (units[k][2] === null) ? [] : units[k][2];
359+ if (units[k][1] === base_to_unit || alt.indexOf(base_to_unit) >= 0) {
360+ to = units[k];
361+ }
362+ }
363+ }
364+
365+ // Return error if a unit does not exist
366+ if (from === null || to === null) {
367+ console.log(from, to);
368+ throw new NAError("Invalid units for conversion.");
369+ }
370+
371+ // Return error if units represent different quantities
372+ if (from[3] !== to[3]) {
373+ throw new NAError("Invalid units for conversion.");
374+ }
375+
376+ // Return converted number
377+ return n * from[6] * from_multiplier / (to[6] * to_multiplier);
378+};
379+
380 export {
381 ARABIC,
382 CHAR,
383 CODE,
384 SPLIT,
385- CONCATENATE
386+ CONCATENATE,
387+ CONVERT
388 }
389\ No newline at end of file
390diff --git a/tests/TextTest.ts b/tests/TextTest.ts
391index 23ff056..4e2c469 100644
392--- a/tests/TextTest.ts
393+++ b/tests/TextTest.ts
394@@ -94,6 +94,9 @@ catchAndAssertEquals(function() {
395
396 // Test CONVERT
397 assertEquals(CONVERT(5.1, "mm", "m"), 0.0050999999999999995);
398+assertEquals(CONVERT(5.1, "mm", "km"), 0.0000050999999999999995);
399+assertEquals(CONVERT(5.1, "g", "kg"), 0.0050999999999999995);
400+assertEquals(CONVERT(35.7, "in^2", "m^2"), 0.023032212);
401
402
403 // Test ARABIC