function _importFactory (factory, options, name = factory.fn) {
    if (name.includes('.')) {
      throw new Error('Factory name should not contain a nested path. ' +
        'Name: ' + JSON.stringify(name))
    }

    const namespace = isTransformFunctionFactory(factory)
      ? math.expression.transform
      : math

    const existingTransform = name in math.expression.transform
    const existing = hasOwnProperty(namespace, name) ? namespace[name] : undefined

    const resolver = function () {
      // collect all dependencies, handle finding both functions and classes and other special cases
      const dependencies = {}
      factory.dependencies
        .map(stripOptionalNotation)
        .forEach(dependency => {
          if (dependency.includes('.')) {
            throw new Error('Factory dependency should not contain a nested path. ' +
              'Name: ' + JSON.stringify(dependency))
          }

          if (dependency === 'math') {
            dependencies.math = math
          } else if (dependency === 'mathWithTransform') {
            dependencies.mathWithTransform = math.expression.mathWithTransform
          } else if (dependency === 'classes') { // special case for json reviver
            dependencies.classes = math
          } else {
            dependencies[dependency] = math[dependency]
          }
        })

      const instance = /* #__PURE__ */ factory(dependencies)

      if (instance && typeof instance.transform === 'function') {
        throw new Error('Transforms cannot be attached to factory functions. ' +
            'Please create a separate function for it with export const path = "expression.transform"')
      }

      if (existing === undefined || options.override) {
        return instance
      }

      if (typed.isTypedFunction(existing) && typed.isTypedFunction(instance)) {
        // merge the existing and new typed function
        return typed(existing, instance)
      }

      if (options.silent) {
        // keep existing, ignore imported function
        return existing
      } else {
        throw new Error('Cannot import "' + name + '": already exists')
      }
    }

    const former = factory.meta?.formerly ?? ''
    const needsTransform = isTransformFunctionFactory(factory) ||
      factoryAllowedInExpressions(factory)
    const withTransform = math.expression.mathWithTransform

    // TODO: add unit test with non-lazy factory
    if (!factory.meta || factory.meta.lazy !== false) {
      lazy(namespace, name, resolver)
      if (former) lazy(namespace, former, resolver)

      // FIXME: remove the `if (existing &&` condition again. Can we make sure subset is loaded before subset.transform? (Name collision, and no dependencies between the two)
      if (existing && existingTransform) {
        _deleteTransform(name)
        if (former) _deleteTransform(former)
      } else {
        if (needsTransform) {
          lazy(withTransform, name, () => namespace[name])
          if (former) lazy(withTransform, former, () => namespace[name])
        }
      }
    } else {
      namespace[name] = resolver()
      if (former) namespace[former] = namespace[name]

      // FIXME: remove the `if (existing &&` condition again. Can we make sure subset is loaded before subset.transform? (Name collision, and no dependencies between the two)
      if (existing && existingTransform) {
        _deleteTransform(name)
        if (former) _deleteTransform(former)
      } else {
        if (needsTransform) {
          lazy(withTransform, name, () => namespace[name])
          if (former) lazy(withTransform, former, () => namespace[name])
        }
      }
    }

    // TODO: improve factories, store a list with imports instead which can be re-played
    importedFactories[name] = factory

    math.emit('import', name, resolver)
  }
Import an instance of a factory into math.js
@param {function(scope: object)} factory
@param {Object} options  See import for a description of the options
@param {string} [name=factory.name] Optional custom name
@private