# Crear extensiones adicionales

&kst; posee una interfaz simple y estándar que facilita la creación de extensiones adicionales. Además de detallar los requerimientos básicos para desarrollar extensiones, las secciones siguientes describen como hacer uso de archivos de cabecera ya escritos para crear ciertos tipos de extensión.

## Crear una extensión básica

Una extensión de &kst; está formado por dos archivos: un archivo XML y un archivo de biblioteca dinámica.

### El archivo XML

El archivo XML proporciona información sobre la extensión y describe sus entradas y salidas. Éste es un ejemplo de un archivo XML usado como extensión de &kst;:

    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE Module SYSTEM "file:/Repository/Level2/Soft/ProC/moduledef.dtd">

    <module>

    <intro>
    <modulename name="extensiondeprueba"/>    <!-- El nombre del módulo -->
    <author name="Rick Chern"/> <!-- El nombre del autor -->
    <description text="Una extensión de pruebas para mí"/> <!-- Descripción del módulo -->
    <version minor="1" major="0"/>  <!-- Número de versión del módulo -->
    <state devstate="release"/>     <!-- Estado de desarrollo del módulo (opcional)-->
    </intro>

    <interface>

    <!--La entradas aquí-->
    <input>
    <table type="float" name="Vector de entrada 1" descr="El primer vector de entrada" />
    </input>

    <input>
    <float name="Escalar de entrada 1" descr="El primer escalar de entrada" />
    </input>

    <!--Las salidas aquí->
    <output>
    <table type="float" name="Vector de salida1" descr="El primer vector de salida" />
    </output>

    <output>
    <float name="Escalar de salida 1" descr="El primer vector de salida" />
    </output>

    </interface>

    </module>

En general, puede usar el ejemplo anterior como plantilla y modificar sus secciones para ajustarse a sus necesidades. Como puede verse en el ejemplo, el archivo XML está formado por un nodo `module`. El nodo `module` tiene un nodo `intro` y un nodo `interface`. Debería modificar el nodo `intro` usando como guía los comentarios dados en el archivo XML. Es importante que en `modulename`, el atributo `name` se corresponda con el nombre de la biblioteca dinámica que va a usar.

El nodo `interface` describe las entradas y salidas actuales de la extensión. Cada entrada se describe por un nodo `input`, y cada salida se describe con un nodo `output`. Cada input u output deberían tener como nodos hijos o bien `table` o `float`. El atributo `type` de `table` debe ponerse como `"float"`. Tenga en cuenta que el orden de las entradas y de las salidas es importante: tal orden se usa para determinar los valores del índice para cada conjunto de datos de entrada y salida de la librería dinámica, y es el mismo orden que el utilizado para mostrar los campos de entrada y salida de la interfaz de la extensión de &kst;.

Una vez que haya completado el archivo XML, guardelo como `[modulename].xml`, donde `[nombremodulo]` es el valor dado al atributo `name` del nodo `nombremodulo`.

### La biblioteca dinámica

La biblioteca dinámica contiene la funcionalidad actual de la extensión. En otras palabras, determina cómo derivar las salidas de unas entradas dadas. A continuación se muestran los requerimientos para una biblioteca dinámica:

- El objeto debe exportar el siguiente enlace simbólico en C:

      int symbol(const double *const inArrays[],
                 const int inArrayLens[],
                 const double inScalars[],
                 double *outArrays[],
                 int outArrayLens[],
                 double outScalars[])

  donde symbol debe corresponder al valor del atributo `name` del nodo `modulename` en el archivo XML. Ésta es la única función que será llamada por &kst; (aunque usted pueda tener otras funciones en su extensión). A continuación se describen los argumentos de esta función:

  `const double *const inArrays[]`

    El vector de vectores de entrada. Cada vector de entrada se corresponderá con un vector de entrada. Los vectores deben estar en el mismo orden que los vectores definidos en el archivo XML, de tal forma que `inArrays[0]` sea el vector que represente al primer vector de entrada, `inArrays[1]` sea el vector que represente al segundo vector de entrada, y así sucesivamente.

  `const int inArraysLens[]`

    El vector conteniendo las longitudes de cada vector de entrada. Las longitudes se guardan en el mismo orden que los vectores en `inArrays[]` (es decir, `inArrayLens[0]` es la longitud de `inArrays[0]`).

  `const double inScalars[]`

    El vector de escalares de entrada. Los escalares se guardan en el mismo orden que están definidos en el archivo XML.

  `double *outArrays[]`

    El vector de vectores de salida. Cada vector de salida corresponde a un vector de salida, y los vectores deben estar en el mismo orden que los vectores de salida listados en el archivo XML.

  `int outArrayLens[]`

    El vector que debería contener las longitudes de los vectores de salida. Tales longitudes deben almacenarse en el mismo orden que los vectores en `outArrays[]`.

  `double outScalars[]`

    El vector de escalares de salida. Los escalares deben estar en el mismo orden que su listado en el archivo XML.

- La función debe devolver `0` si se ha ejecutado con éxito, y `-1` si no.

- El código de la biblioteca debe ser capaz de trabajar con entradas inesperadas, tales como vectores vacíos (en la mayoría de los casos, es suficiente con devolver un valor de `-1` cuando se presentan tales casos).

- El número y el tipo de las salidas debe ser exactamente como el especificado en en archivo XML.

- Probablemente necesitará redimensionar los vectores en `outArrays[]`. Para ello, use la función `realloc()`. Por ejemplo,

      outArrays[0]=(double*)realloc(outArrays[0], 5*sizeof(double));

  va a reservar un espacio de 5 «doubles» para `outArrays[0]`. Use solo `realloc()` para reservar memoria.

- Los argumentos de entrada deben permanecer constantes. No haga un «cast» a tipos no constantes.

A continuación se muestra un ejemplo de un archivo con el código de una biblioteca dinámica que es una extensión simple:

    #include <stdlib.h>

    extern "C" int testplugin(const double *const inArrays[], const int inArrayLens[],
                    const double is[],
                    double *outArrays[], int outArrayLens[],
                    double outScalars[]);

    int testplugin(const double *const inArrays[], const int inArrayLens[],
                    const double is[],
                    double *outArrays[], int outArrayLens[],
                    double outScalars[])

    //Acepta 1 vector y 1 escalar. Multiplica todos los elementos del vector por el
    //escalar, y da como salida el vector resultante. También da como salida el escalar original.
    {
            // Asigna las salidas
            outArrayLens[0]=inArrayLens[0];

            //Redimensiona los vectores de salida
            outArrays[0]=(double*)realloc(outArrays[0], inArrayLens[0]*sizeof(double));

            //multiplica cada elemento del vector de entrada
            //con el escalar
            for (int i=0; i<inArrayLens[0]; i++)
            {
                    outArrays[0][i]=inArrays[0][i] * is[0];
            }

            //ahora, asignar el escalar de salida
            outScalars[0]=is[0];

            return 0;
    }

### Compilar la extensión

Si usa &gcc; para compilar su extensión, simplemente compile para obtener el archivo objeto:

    cc -Wall -c -o miextension.o miextension.c -fPIC -DPIC

y entonces, cree la biblioteca dinámica:

    ld -o miextension.so -shared miextension.o

Los archivos resultantes `*.so` y `*.xml` deben ponerse en el mismo directorio. Cuando use el administrador de extensiones de &kst; para cargar el archivo XML, buscará automáticamente la librería dinámica en el mismo directorio.

## Crear extensiones para ajustes lineales

Para crear una extensión para un ajuste lineal, podría implementar sus propios algoritmos de ajuste y devolver los vectores apropiados. Sin embargo, &kst; ya viene con archivos de cabecera que le facilitan la implementación de extensiones para ajuste por mínimos cuadrados con tal de que codifique unas pocas funciones. Esta sección le muestra como sacar partido a tales archivos.

### Archivos de cabecera

Se suministran dos archivos de cabecera para hacer ajustes lineales, `linear.h` (para ajustes sin ponderación) y `linear_weighted.h` (para ajustes ponderados). Ambos se encuentran en las fuentes de &kst; dentro del directorio `kst/plugins/fits/`. Para usar dichos archivos, incluya solo uno de ellos en el código fuente de su extensión:

    #include <../linear.h>

o

    #include <../linear_weighted.h>

(por convención, vamos a situar el código fuente de la extensión en un directorio más abajo que el que contiene los archivos de cabecera).

### Implementar las funciones requeridas

Dar un modelo lineal general:

donde `y` es un vector de `n` observaciones, `X` es una matriz `n` por `p` de variables de predicción, y `c` es el vector de `p` parámetros de mejor ajuste que van a estimarse, los archivos de cabecera dan funciones para la estimación de `c` para un `y` y un `X` dados. Para dar `X`, esta función debe implementarse en el código de la extensión:

    double calculate_matrix_entry( double dX, int iPos )

Esta función debe devolver el valor de la entrada en la columna `iPos` de la matriz de variables de predicción, usando el valor de `x` `dX`. Esta función la llamará linear.h o linear_weighted.h. La implementación de esta función depende del modelo que desee usar para el ajuste, y es única para cada extensión de ajuste lineal. Por ejemplo, para ajustar a un modelo polinomial, `calculate_matrix_entry` debe implementarse como sigue:

    double calculate_matrix_entry( double dX, int iPos ) {
      double dY;
      dY = pow( dX, (double)iPos );
      return dY;
    }

### Llamando a las funciones de ajuste

Una vez que se han incluído los archivos de cabecera y que se ha implementado la función `calculate_matrix_entry`, se deberá llamar a la función de ajuste apropiada incluída desde el archivo de cabecera:

    kstfit_linear_unweighted( inArrays, inArrayLens,
                              outArrays, outArrayLens,
                              outScalars, iNumParams );

o

    kstfit_linear_weighted( inArrays, inArrayLens,
                            outArrays, outArrayLens,
                            outScalars, iNumParams );

Cada función devolverá `0` si hay éxito, y `-1` en error, así que es una buena idea que el valor que devuelve la función C exportada sea igual al valor de devuleto por la función de ajuste. Para mantener la simplicidad, el código para la extensión puede simplemente pasar los argumentos dados a la función C exportada a la función de ajuste. Observe, sin embargo, que la variable inArrays debe estructurarse de esta forma:

- `inArrays[0]` debe contener un vector de coordenadas x de puntos de datos.

- `inArrays[1]` debe contener un vector de coordenadas y de puntos de datos.

- `inArrays[2]` solo existe si se llama la función `kstfit_linear_weighted`, y debe contener un vector con los valores de los pesos usados para el ajuste.

La manera más sencilla de asegurar que inArrays está estructurado correctamente es especificando el orden correcto de vectores de entrada en el archivo XML para la extensión.

`iNumParams` es el número de parámetros en el modelo usado, el cual debe ser igual al número de columnas en la matriz `X` de variables de predicción. `iNumParams` debe estar correcto antes de llamar a la función de ajuste.

Después de llamar a `kstfit_linear_unweighted` o `kstfit_linear_weighted`, las variables `outArrays` y `outScalars` serán asignadas como sigue:

- `outArrays[0]` contendrá el vector con los valores y ajustados.

- `outArrays[1]` contendrá el vector con los residuos.

- `outArrays[2]` contendrá el vector con los valores de los parámetros calculados de mejor ajuste.

- `outArrays[3]` contendrá la matriz de covariancia, retornada fila a fila en un vector.

- `outScalars[0]` contendrá chi^2/nu, donde chi^2 es la suma ponderada de los cuadrados de los residuos, y nu son los grados de libertad.

`outArrayLens` se asignará correctamente para indicar la longitud de cada vector de salida.

Asegúrese de que cada salida especificada en el archivo XML se corresponde a aquellas retornadas por la función C exportada (que en la mayoría de los casos será la salida devuelta por la función de ajuste).

### Ejemplo

A continuación se muestra un ejemplo de código de una extensión para un ajuste lineal.

    /*
     *  Extensión de ajuste polinomial para KST.
     *  Derechos de autor. 2004. La universidad de British Columbia
     *  Liberado bajo los términos de la licencia GPL.
     */

    #include "../linear.h"

    double calculate_matrix_entry( double dX, int iPos ) {
      double dY;

      dY = pow( dX, (double)iPos );

      return dY;
    }

    extern "C" int kstfit_polynomial_unweighted(
      const double *const inArrays[],
      const int inArrayLens[],
      const double inScalars[],
      double *outArrays[], int outArrayLens[],
      double outScalars[]);

    int kstfit_polynomial_unweighted(
      const double *const inArrays[],
      const int inArrayLens[],
            const double inScalars[],
            double *outArrays[], int outArrayLens[],
            double outScalars[])
    {
      int iRetVal = -1;
      int iNumParams;

      iNumParams = 1 + (int)floor( inScalars[0] );
      if( iNumParams > 0 ) {
        iRetVal = kstfit_linear_unweighted( inArrays, inArrayLens,
                                            outArrays, outArrayLens,
                                            outScalars, iNumParams );
      }

      return iRetVal;
    }

## Crear extensiones para ajustes no lineales

&kst; proporciona archivos de cabecera diseñados para simplificar la creación de extensiones para ajustes no lineales por mínimos cuadrados. La sección siguiente detalla cómo usar tales archivos de cabecera.

### Archivos de cabecera y definiciones

Los archivos de cabecera para ajustes no lineales están situados en el directorio `kst/plugins/fits_nonlinear` de las fuentes de &kst;. Los archivos se llaman `non_linear.h` y `non_linear_weighted.h` para ajustes normales y ponderados, respectivamente. Para usar estos archivos, incluya solo uno de ellos en el código de su extensión:

    #include <../non_linear.h>

o

    #include <../non_linear_weighted.h>

(por convención, vamos a poner el código fuente de la extensión en un directorio más abajo que el que contiene los archivos de cabecera).

Como un ajuste no lineal es un proceso iterativo, debe definir el número máximo de iteraciones que deben hacerse. El algoritmo de ajuste no lineal se detendrá cuando al menos una de las condiciones siguientes sea verdad:

- Se ha alcanzado el máximo número de iteraciones.

- Se ha alcanzado una precisión de 10-4.

Además, necesita definir el número de parámetros en el modelo, pues no se pasa de forma explícita a la función de ajuste. Para definir estos dos valores, incluya lo siguiente en la parte superior de su código:

    #define NUM_PARAMS [num1]
    #define MAX_NUM_ITERATIONS [num2]

reemplazando `[num1]` con el número de parámetros en el modelo, y `[num2]` con el número máximo de iteraciones a realizar.

### Implementar las funciones requeridas

Para usar los archivos de cabecera para ajustes no lineales, debe proveer una función a usar como modelo, las derivadas parciales de la función con respecto a cada parámetro, y las estimaciones iniciales de los parámetros de mejor ajuste. Para hacer esto, deben implementarse 3 funciones. Estas funciones se describen a continuación.

`double function_calculate( double `dX`, double* `pdParameters` )`

  Esta función calcula el valor y del modelo de ajuste para un valor x dado `dX`, usando el vector de parámetros `pdParameters` pasado a la función. El orden de los parámetros en `pdParameters` es arbitrario, pero debe ser consistente con el orden en las otras dos funciones implementadas. Por ejemplo, para un modelo exponencial, se puede implementar `function_calculate` como sigue:

      double function_calculate( double dX, double* pdParameters ) {
        double dScale         = pdParameters[0];
        double dLambda = pdParameters[1];
        double dOffset = pdParameters[2];
        double dY;

        dY  = ( dScale * exp( -dLambda * dX ) ) + dOffset;

        return dY;
      }

`void function_derivative( double `dX`, double* `pdParameters`, double* `pdDerivatives` )`

  Esta función calcula las derivadas parciales de la función modelo para un valor de x dado `dX`. Las derivadas parciales deben devolverse en `pdDerivatives`. El orden de las derivadas parciales en el vector `pdDerivatives` debe corresponderse al orden de los parámetros en `pdParameters` (esto es, si `pdParameters[0]` contiene el parámetro lambda para un modelo exponencial, `pdDerivatives[0]` debe contener un modelo de la derivada del modelo con respecto a lambda).

`void function_initial_estimate( const double* `pdX`, const double* `pdY`, int `iLength`, double* `pdParameterEstimates` )`

  Esta función da una estimación inicial de los parámetros de mejor ajuste para la función de ajuste. El vector de valores x e y de puntos de datos se pasan en `pdX` y `pdY` respectivamente, y el número de puntos de datos es suministrado por `iLength`. Puede usar o no cualquiera de estos parámetros para calcular una estimación inicial. La función debe poner las estimación inicial calculada en `pdParameterEstimates`, con el orden de las estimaciones correspondiéndose al orden de los parametros en `pdParameters` de `function_calculate` y `function_derivative`. Tenga presente que la estimación inicial es importante en la determinación de si la función de ajuste converge o no a una solución.

### Llamando a las funciones de ajuste

Una vez que todas las funciones requeridas están implementadas, se puede llamar a la función de ajuste del archivo de cabecera incluído:

    kstfit_nonlinear( inArrays, inArrayLens,
                      inScalars, outArrays,
                      outArrayLens, outScalars );

o

    kstfit_nonlinear_weighted( inArrays, inArrayLens,
                               inScalars, outArrays,
                               outArrayLens, outScalars );

dependiendo de si se implementa un ajuste normal o ponderado.

Cada función devolverá `0` si hay éxito, y `-1` en caso de error, así que es una buena idea que el valor de devolución de la función C exportada sea igual al valor de retorno de la función de ajuste. Para mantener la simplicidad, el código para la extensión puede simplemente pasar los argumentos dados a la función C exportada a la función de ajuste. Observe, sin embargo, que la variable inArrays debe estructurarse de esta forma:

- `inArrays[0]` debe contener un vector de coordenadas x de puntos de datos.

- `inArrays[1]` debe contener un vector de coordenadas y de puntos de datos.

- `inArrays[2]` solo existe si se llama la función `kstfit_linear_weighted`, y debe contener un vector con los valores de los pesos usados para el ajuste.

La manera más sencilla de asegurar que inArrays está estructurado correctamente es especificando el orden correcto de vectores de entrada en el archivo XML para la extensión.

Después de llamar a `kstfit_linear_unweighted` o `kstfit_linear_weighted`, las variables `outArrays` y `outScalars` serán asignadas como sigue:

- `outArrays[0]` contendrá el vector con los valores y ajustados.

- `outArrays[1]` contendrá el vector con los residuos.

- `outArrays[2]` contendrá el vector con los valores de los parámetros calculados de mejor ajuste.

- `outArrays[3]` contendrá la matriz de covariancia, retornada fila a fila en un vector.

- `outScalars[0]` contendrá chi^2/nu, donde chi^2 es la suma ponderada de los cuadrados de los residuos, y nu son los grados de libertad.

`outArrayLens` se asignará correctamente para indicar la longitud de cada vector de salida.

Asegúrese de que cada salida especificada en el archivo XML se corresponde a aquellas retornadas por la función C exportada (que en la mayoría de los casos será la salida devuelta por la función de ajuste).

### Ejemplo

Lo que sigue es un ejemplo de una extensión para ajustes no lineales que hace un ajuste según un modelo exponencial.

    /*
     *  Extensión de ajuste exponencial para KST.
     *  Derecheos de autor. 2004. La universidad de British Columbia
     *  Liberado bajo los términos de la GPL.
     */

    #define NUM_PARAMS 3
    #define MAX_NUM_ITERATIONS 500

    #include "../non_linear.h"

    void function_initial_estimate( const double* pdX, const double* pdY,
                                    int iLength, double* pdParameterEstimates ) {
      KST_UNUSED( pdX )
      KST_UNUSED( pdY )
      KST_UNUSED( iLength )

      pdParameterEstimates[0] =  1.0;
      pdParameterEstimates[1] =  0.0;
      pdParameterEstimates[2] =  0.0;
    }

    double function_calculate( double dX, double* pdParameters ) {
      double dScale         = pdParameters[0];
      double dLambda = pdParameters[1];
      double dOffset = pdParameters[2];
      double dY;

      dY  = ( dScale * exp( -dLambda * dX ) ) + dOffset;

      return dY;
    }

    void function_derivative( double dX, double* pdParameters, double* pdDerivatives ) {
      double dScale         = pdParameters[0];
      double dLambda = pdParameters[1];
      double dExp;
      double ddScale;
      double ddLambda;
      double ddOffset;

      dExp     = exp( -dLambda * dX );
      ddScale  = dExp;
      ddLambda = -dX * dScale * dExp;
      ddOffset = 1.0;

      pdDerivatives[0] = ddScale;
      pdDerivatives[1] = ddLambda;
      pdDerivatives[2] = ddOffset;
    }

    extern "C" int kstfit_exponential(const double *const inArrays[], const int inArrayLens[],
                    const double inScalars[],
                    double *outArrays[], int outArrayLens[],
                    double outScalars[]);

    int kstfit_exponential(const double *const inArrays[], const int inArrayLens[],
                    const double inScalars[],
                    double *outArrays[], int outArrayLens[],
                    double outScalars[])
    {
      return kstfit_nonlinear( inArrays, inArrayLens,
                               inScalars, outArrays,
                               outArrayLens, outScalars );
    }

## Crear extensiones de filtro de pasos

&kst; proporciona archivos de cabecera diseñados para simplificar la creación de extensiones para filtros de paso. A continuación se detalla como usar tales archivos de cabecera.

### Archivos de cabecera

El archivo de cabecera para filtros de paso está situado en el directorio `kst/plugins/pass_filter` de las fuentes de &kst;. El archivo se llama `filters.h`. Para usar este archivo, inclúyalo en el código de su extensión:

    #include <../filters.h>

(por convención, vamos a poner el código fuente de la extensión en un directorio más abajo del que contiene los archivos de cabecera).

### Funciones requeridas

El archivo de cabecera `filters.h` contiene una función simple que calcula la transformada de Fourier de una función dada, aplica el filtro suministrado a la transformada de Fourier, y luego calcula la transformada inversa de Fourier en la transformada de Fourier ya filtrada. Para suministrar el filtro, se necesita implementar la función siguiente en el código de la extensión:

`double filter_calculate( double `dFreqValue`, const double `inScalars[]` )`

Esta función debe calcular la amplitud filtrada para la frecuencia `dFreqValue`. `inScalars[]` contendrá los escalares inalterados para el plugin, específicados en el archivo XML. Lo más probable es que `inScalars[]` contenga las frecuencias de corte u otras propiedades del filtro. Por ejemplo, para implementar un filtro de paso alto Butterworth, se puede implementar `filter_calculate` como sigue:

    double filter_calculate( double dFreqValue, const double inScalars[] ) {
      double dValue;
      if( dFreqValue
    > 0.0 ) {
        dValue = 1.0 / ( 1.0 +
                        pow( inScalars[1] / dFreqValue, 2.0 * (double)inScalars[0] ) );
      } else {
        dValue = 0.0;
      }
      return dValue;
    }

### Llamando a la función de filtrado

Una vez que se ha implementado la función `filter_calculate`, se puede llamar a la función de filtrado del archivo de cabecera:

    kst_pass_filter( inArrays,
                     inArrayLens,
                     inScalars,
                     outArrays,
                     outArrayLens,
                     outScalars );

Los argumentos suministrados a la función C exportada pueden pasarse a `kst_pass_filter` sin modificarse. Sin embargo, hay unas cuantas restricciones en los argumentos:

- `inArrays[0]` debe contener el vector de datos a filtrar.

- `inScalars` debe contener los parámetros específicos del filtro que se usarán por la función `filter_calculate`.

Despues de la llamada de la función, `outArrays[0]` contendrá un vector de datos filtrados, y se asignará `outArrayLens` de manera consecuente. La función `kst_pass_filter` no usa `outScalars`.

### Ejemplo

A continuación se muestra un ejemplo de un filtro de paso que implementa el filtro de paso alto de Butterworth.

    /*
     *  Extensión de filtro de paso bajo de Butterworth para KST.
     *  Derechos de autor. 2004. La universidad de British Columbia
     *  Liberado bajo los términos de la licencia GPL.
     */

    #include <stdlib.h>
    #include <math.h>
    #include "../filters.h"

    extern "C" int butterworth_highpass(const double *const inArrays[], const int inArrayLens[],
                    const double inScalars[],
                    double *outArrays[], int outArrayLens[],
                    double outScalars[]);

    int butterworth_highpass(const double *const inArrays[], const int inArrayLens[],
                    const double inScalars[],
                    double *outArrays[], int outArrayLens[],
                    double outScalars[])
    {
      int iReturn;

      iReturn = kst_pass_filter( inArrays,
                                 inArrayLens,
                                 inScalars,
                                 outArrays,
                                 outArrayLens,
                                 outScalars );

      return iReturn;
    }

    double filter_calculate( double dFreqValue, const double inScalars[] ) {
      double dValue;

      if( dFreqValue > 0.0 ) {
        dValue = 1.0 / ( 1.0 + pow( inScalars[1] / dFreqValue, 2.0 * (double)inScalars[0] ) );
      } else {
        dValue = 0.0;
      }

      return dValue;
    }