Source: classes/KernelSet.js

import Base from './Base'
import { encodeStrings, stackPutArray } from '../helpers'
import { getInstance } from '../initialiser'
import Kernel from './Kernel'

const kernelNameOptions = {
  SUM: 'sum',
  DIFF: 'diff',
  MUL: 'mul',
  DIV: 'div',
  PDIV: 'pdiv',
  SIN: 'sin',
  COS: 'cos',
  LOG: 'log',
  EXP: 'exp',
  GAUSSIAN: 'gaussian',
  SQRT: 'sqrt',
}

/**
 * @class
 * @param {...String } [kernelNames] Names of the kernels to add to the set.
 */
class KernelSet extends Base {
  static SUM = kernelNameOptions.SUM
  static DIFF = kernelNameOptions.DIFF
  static MUL = kernelNameOptions.MUL
  static DIV = kernelNameOptions.DIV
  static PDIV = kernelNameOptions.PDIV
  static SIN = kernelNameOptions.SIN
  static COS = kernelNameOptions.COS
  static LOG = kernelNameOptions.LOG
  static EXP = kernelNameOptions.EXP
  static GAUSSIAN = kernelNameOptions.GAUSSIAN
  static SQRT = kernelNameOptions.SQRT

  static ALL_KERNELS = Object.values(kernelNameOptions)

  /**
   * @param {...String } [kernelNames] Names of the kernels to add to the set.
   */
  constructor(...kernelNames) {
    super()

    const {
      exports: { kernel_set_constructor_0, kernel_set_constructor_1 },
      memory: { U8 },
    } = getInstance()

    if (kernelNames.length > 0) {
      const options = Object.values(kernelNameOptions)

      kernelNames.forEach(kernelName => {
        if (options.indexOf(kernelName) === -1) {
          throw new TypeError(
            `Provided kernelName ${kernelName} is not a valid kernel identifier.`
          )
        }
      })

      const stackStart = this._stackSave()

      const encodedStrings = encodeStrings(...kernelNames)
      const namesPointer = stackPutArray(encodedStrings, U8)

      const receivedPointer = kernel_set_constructor_1(
        namesPointer,
        kernelNames.length
      )

      Object.defineProperty(this, 'pointer', { value: receivedPointer })

      this._stackRestore(stackStart)
      return
    }

    const receivedPointer = kernel_set_constructor_0()
    Object.defineProperty(this, 'pointer', { value: receivedPointer })
  }

  /**
   * Adds a kernel to the set.
   *
   * @memberof KernelSet
   * @param {(string|Kernel)} kernel The kernel to add by name or instance.
   */
  push(kernel) {
    this._throwIfDestroyed()

    const {
      exports: { kernel_set_push_back_0, kernel_set_push_back_1 },
      memory: { U8 },
    } = getInstance()

    if (kernel instanceof Kernel) {
      kernel_set_push_back_1(this.pointer, kernel.pointer)
      return
    }

    const options = Object.values(kernelNameOptions)

    if (options.indexOf(kernel) === -1) {
      throw new TypeError(
        `Provided kernel is not an instance of a Kernel or a valid kernel identifier.`
      )
    }

    const stackStart = this._stackSave()

    const textArray = encodeStrings(kernel)
    const textPointer = stackPutArray(textArray, U8)

    kernel_set_push_back_0(this.pointer, textPointer)

    this._stackRestore(stackStart)
  }

  /**
   * @readonly
   * @type {[Kernel]}
   */
  get kernels() {
    this._throwIfDestroyed()

    const {
      exports: { kernel_set_num_kernels, kernel_set_get_kernels },
      memory: { U32 },
    } = getInstance()

    const stackStart = this._stackSave()

    const numKernels = kernel_set_num_kernels(this.pointer)

    if (numKernels === 0) {
      return []
    }

    const pointersArrPointer = this._stackAlloc(
      U32.BYTES_PER_ELEMENT * numKernels
    )

    kernel_set_get_kernels(this.pointer, pointersArrPointer)

    const kernelPointers = new Uint32Array(
      U32.buffer,
      pointersArrPointer,
      numKernels
    )

    const kernels = new Array(numKernels)

    for (let index = 0; index < kernelPointers.length; index++) {
      kernels[index] = new Kernel(null, kernelPointers[index])
    }

    this._stackRestore(stackStart)

    return kernels
  }

  /**
   * Gets the kernel at `index` of the list.
   *
   * @memberof KernelSet
   * @param {number} index Index of the kernel to return.
   * @returns {Kernel} The kernel at `index` of the set.
   */
  kernel(index) {
    this._throwIfDestroyed()

    const {
      exports: { kernel_set_get_kernel, kernel_set_num_kernels },
    } = getInstance()

    const numKernels = kernel_set_num_kernels(this.pointer)
    const maxIndex = numKernels - 1

    if (index > maxIndex) {
      throw new RangeError(
        `Provided index ${index} but the maximal index is ${maxIndex}.`
      )
    }

    const pointer = kernel_set_get_kernel(this.pointer, index)

    return new Kernel(null, pointer)
  }

  /**
   * Get the names of the Kernels in the Set.
   * @readonly
   * @memberof KernelSet
   * @type {string[]} The names of the Kernels in the set.
   */
  get names() {
    this._throwIfDestroyed()

    const names = this.kernels.map(kernel => {
      const name = kernel.name

      kernel.destroy()

      return name
    })

    return names
  }

  /**
   * @readonly
   * @memberof KernelSet
   * @type {string} The string 'KernelSet'
   */
  get [Symbol.toStringTag]() {
    return 'KernelSet'
  }

  /**
   * Remove all kernels from the set.
   * @memberof KernelSet
   */
  clear() {
    this._throwIfDestroyed()

    const {
      exports: { kernel_set_clear },
    } = getInstance()

    kernel_set_clear(this.pointer)
  }

  /**
   * Removes the C++ object from memory.
   * @memberof KernelSet
   */
  destroy() {
    this._throwIfDestroyed()

    const {
      exports: { kernel_set_destroy },
    } = getInstance()

    kernel_set_destroy(this.pointer)

    super.destroy()
  }
}

export default KernelSet