create.js 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198
  1. import _extends from "@babel/runtime/helpers/extends";
  2. import typedFunction from 'typed-function';
  3. import { deepFlatten, isLegacyFactory, values } from '../utils/object.js';
  4. import * as emitter from './../utils/emitter.js';
  5. import { importFactory } from './function/import.js';
  6. import { configFactory } from './function/config.js';
  7. import { factory, isFactory } from '../utils/factory.js';
  8. import { isAccessorNode, isArray, isArrayNode, isAssignmentNode, isBigNumber, isBlockNode, isBoolean, isChain, isCollection, isComplex, isConditionalNode, isConstantNode, isDate, isDenseMatrix, isFraction, isFunction, isFunctionAssignmentNode, isFunctionNode, isHelp, isIndex, isIndexNode, isMatrix, isNode, isNull, isNumber, isObject, isObjectNode, isOperatorNode, isParenthesisNode, isRange, isRangeNode, isRelationalNode, isRegExp, isResultSet, isSparseMatrix, isString, isSymbolNode, isUndefined, isUnit } from '../utils/is.js';
  9. import { ArgumentsError } from '../error/ArgumentsError.js';
  10. import { DimensionError } from '../error/DimensionError.js';
  11. import { IndexError } from '../error/IndexError.js';
  12. import { DEFAULT_CONFIG } from './config.js';
  13. /**
  14. * Create a mathjs instance from given factory functions and optionally config
  15. *
  16. * Usage:
  17. *
  18. * const mathjs1 = create({ createAdd, createMultiply, ...})
  19. * const config = { number: 'BigNumber' }
  20. * const mathjs2 = create(all, config)
  21. *
  22. * @param {Object} [factories] An object with factory functions
  23. * The object can contain nested objects,
  24. * all nested objects will be flattened.
  25. * @param {Object} [config] Available options:
  26. * {number} epsilon
  27. * Minimum relative difference between two
  28. * compared values, used by all comparison functions.
  29. * {string} matrix
  30. * A string 'Matrix' (default) or 'Array'.
  31. * {string} number
  32. * A string 'number' (default), 'BigNumber', or 'Fraction'
  33. * {number} precision
  34. * The number of significant digits for BigNumbers.
  35. * Not applicable for Numbers.
  36. * {boolean} predictable
  37. * Predictable output type of functions. When true,
  38. * output type depends only on the input types. When
  39. * false (default), output type can vary depending
  40. * on input values. For example `math.sqrt(-4)`
  41. * returns `complex('2i')` when predictable is false, and
  42. * returns `NaN` when true.
  43. * {string} randomSeed
  44. * Random seed for seeded pseudo random number generator.
  45. * Set to null to randomly seed.
  46. * @returns {Object} Returns a bare-bone math.js instance containing
  47. * functions:
  48. * - `import` to add new functions
  49. * - `config` to change configuration
  50. * - `on`, `off`, `once`, `emit` for events
  51. */
  52. export function create(factories, config) {
  53. var configInternal = _extends({}, DEFAULT_CONFIG, config);
  54. // simple test for ES5 support
  55. if (typeof Object.create !== 'function') {
  56. throw new Error('ES5 not supported by this JavaScript engine. ' + 'Please load the es5-shim and es5-sham library for compatibility.');
  57. }
  58. // create the mathjs instance
  59. var math = emitter.mixin({
  60. // only here for backward compatibility for legacy factory functions
  61. isNumber,
  62. isComplex,
  63. isBigNumber,
  64. isFraction,
  65. isUnit,
  66. isString,
  67. isArray,
  68. isMatrix,
  69. isCollection,
  70. isDenseMatrix,
  71. isSparseMatrix,
  72. isRange,
  73. isIndex,
  74. isBoolean,
  75. isResultSet,
  76. isHelp,
  77. isFunction,
  78. isDate,
  79. isRegExp,
  80. isObject,
  81. isNull,
  82. isUndefined,
  83. isAccessorNode,
  84. isArrayNode,
  85. isAssignmentNode,
  86. isBlockNode,
  87. isConditionalNode,
  88. isConstantNode,
  89. isFunctionAssignmentNode,
  90. isFunctionNode,
  91. isIndexNode,
  92. isNode,
  93. isObjectNode,
  94. isOperatorNode,
  95. isParenthesisNode,
  96. isRangeNode,
  97. isRelationalNode,
  98. isSymbolNode,
  99. isChain
  100. });
  101. // load config function and apply provided config
  102. math.config = configFactory(configInternal, math.emit);
  103. math.expression = {
  104. transform: {},
  105. mathWithTransform: {
  106. config: math.config
  107. }
  108. };
  109. // cached factories and instances used by function load
  110. var legacyFactories = [];
  111. var legacyInstances = [];
  112. /**
  113. * Load a function or data type from a factory.
  114. * If the function or data type already exists, the existing instance is
  115. * returned.
  116. * @param {Function} factory
  117. * @returns {*}
  118. */
  119. function load(factory) {
  120. if (isFactory(factory)) {
  121. return factory(math);
  122. }
  123. var firstProperty = factory[Object.keys(factory)[0]];
  124. if (isFactory(firstProperty)) {
  125. return firstProperty(math);
  126. }
  127. if (!isLegacyFactory(factory)) {
  128. console.warn('Factory object with properties `type`, `name`, and `factory` expected', factory);
  129. throw new Error('Factory object with properties `type`, `name`, and `factory` expected');
  130. }
  131. var index = legacyFactories.indexOf(factory);
  132. var instance;
  133. if (index === -1) {
  134. // doesn't yet exist
  135. if (factory.math === true) {
  136. // pass with math namespace
  137. instance = factory.factory(math.type, configInternal, load, math.typed, math);
  138. } else {
  139. instance = factory.factory(math.type, configInternal, load, math.typed);
  140. }
  141. // append to the cache
  142. legacyFactories.push(factory);
  143. legacyInstances.push(instance);
  144. } else {
  145. // already existing function, return the cached instance
  146. instance = legacyInstances[index];
  147. }
  148. return instance;
  149. }
  150. var importedFactories = {};
  151. // load the import function
  152. function lazyTyped() {
  153. for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
  154. args[_key] = arguments[_key];
  155. }
  156. return math.typed.apply(math.typed, args);
  157. }
  158. lazyTyped.isTypedFunction = typedFunction.isTypedFunction;
  159. var internalImport = importFactory(lazyTyped, load, math, importedFactories);
  160. math.import = internalImport;
  161. // listen for changes in config, import all functions again when changed
  162. // TODO: move this listener into the import function?
  163. math.on('config', () => {
  164. values(importedFactories).forEach(factory => {
  165. if (factory && factory.meta && factory.meta.recreateOnConfigChange) {
  166. // FIXME: only re-create when the current instance is the same as was initially created
  167. // FIXME: delete the functions/constants before importing them again?
  168. internalImport(factory, {
  169. override: true
  170. });
  171. }
  172. });
  173. });
  174. // the create function exposed on the mathjs instance is bound to
  175. // the factory functions passed before
  176. math.create = create.bind(null, factories);
  177. // export factory function
  178. math.factory = factory;
  179. // import the factory functions like createAdd as an array instead of object,
  180. // else they will get a different naming (`createAdd` instead of `add`).
  181. math.import(values(deepFlatten(factories)));
  182. math.ArgumentsError = ArgumentsError;
  183. math.DimensionError = DimensionError;
  184. math.IndexError = IndexError;
  185. return math;
  186. }