LCALIFLayer kernel has some additional global buffers so that LCALIFLayer can save additional variables during checkpointing.

git-svn-id: 602a26e73ae9f2a14e2bb95676f2e137fcc39919@6521 7c0444ca-5550-0410-9008-faecdbe78eb5

#include "LIF_params.h"
#include "cl_random.hcl"

#ifndef PV_USE_OPENCL
#  include <math.h>
#  define EXP expf
#  define CL_KERNEL
#  define CL_MEM_GLOBAL
#  define CL_MEM_CONST
#  define CL_MEM_LOCAL
#else  /* compiling with OpenCL */
#  define EXP exp
#  define CL_KERNEL       __kernel
#  define CL_MEM_GLOBAL   __global
#  define CL_MEM_CONST    __constant
#  define CL_MEM_LOCAL    __local
#  include "conversions.hcl"
#  define CHANNEL_EXC   0
#  define CHANNEL_INH   1
#  define CHANNEL_INHB  2
#  define CHANNEL_GAP   3
#endif

inline
float LCALIF_tauInf(const float tau, const float G_E, const float G_I, const float G_IB, const float sum_gap) {
   return tau/(1+G_E+G_I+G_IB+sum_gap);
}

inline
float LCALIF_VmemInf(const float Vrest, const float V_E, const float V_I, const float V_B, const float G_E, const float G_I, const float G_B, const float G_gap, const float sumgap) {
   return (Vrest + V_E*G_E + V_I*G_I + V_B*G_B + G_gap)/(1+G_E+G_I+G_B+sumgap);
}

// The commented-out code below is only used for the Heun's method integration, which works very poorly when the equation is stiff.
/*
inline
float LIFGap_Vmem_derivative(
      const float Vmem,
      const float G_E,
      const float G_I,
      const float G_IB,
      const float G_Gap,
      const float V_E,
      const float V_I,
      const float V_IB,
      const float sum_gap,
      const float Vrest,
      const float tau) {
   float totalconductance = 1.0 + G_E + G_I + G_IB + sum_gap;
   float Vmeminf = (Vrest + V_E*G_E + V_I*G_I + V_IB*G_IB + G_Gap)/totalconductance;
   return totalconductance*(Vmeminf-Vmem)/tau;
}
 */

//
// update the state of a LCALIF layer (spiking)
//
//    assume called with 1D kernel
//
// LCALIF_update_state_beginning uses a Heun scheme for V, using values of the conductances at both the beginning and end of the timestep.  Spikes in the input are applied at the beginning of the timestep.
//
CL_KERNEL
void LCALIF_update_state(
    const int numNeurons,
    const double timed,
    const double dt,

    const int nx,
    const int ny,
    const int nf,
    const int nb,

    float Vscale,
    float * Vadpt,
    float tauTHR,
    const float targetRateHz,

    pvdata_t * integratedSpikeCount,

    CL_MEM_CONST LIF_params * params,
    CL_MEM_GLOBAL uint4 * rnd,
    CL_MEM_GLOBAL float * V,
    CL_MEM_GLOBAL float * Vth,
    CL_MEM_GLOBAL float * G_E,
    CL_MEM_GLOBAL float * G_I,
    CL_MEM_GLOBAL float * G_IB,
    CL_MEM_GLOBAL float * GSynHead,
//    CL_MEM_GLOBAL float * GSynExc,
//    CL_MEM_GLOBAL float * GSynInh,
//    CL_MEM_GLOBAL float * GSynInhB,
//    CL_MEM_GLOBAL float * GSynGap,
    CL_MEM_GLOBAL float * activity,

    const float sum_gap,
    CL_MEM_GLOBAL float * G_Gap,
    CL_MEM_GLOBAL float * Vattained,
    CL_MEM_GLOBAL float * Vmeminf,
    const int normalizeInputFlag,
    CL_MEM_GLOBAL float * GSynExcEffective,
    CL_MEM_GLOBAL float * GSynInhEffective,
    CL_MEM_GLOBAL float * excitatoryNoise,
    CL_MEM_GLOBAL float * inhibitoryNoise,
    CL_MEM_GLOBAL float * inhibNoiseB)
{

   // convert target rate from Hz to kHz
   float targetRatekHz = targetRateHz/1000;

   // tau parameters
   const float tauO = 1/targetRatekHz;   //Convert target rate from kHz to ms (tauO)

   const float decayE   = EXP(-dt/params->tauE);
   const float decayI   = EXP(-dt/params->tauI);
   const float decayIB  = EXP(-dt/params->tauIB);
   const float decayVth = EXP(-dt/params->tauVth);
   const float decayO   = EXP(-dt/tauO);

   //Convert dt to seconds
   const float dt_sec = .001 * dt;

#ifndef PV_USE_OPENCL

for (int k = 0; k < nx*ny*nf; k++) {
#else
   k = get_global_id(0);
#endif

   int kex = kIndexExtended(k, nx, ny, nf, nb);

   //
   // kernel (nonheader part) begins here
   //

   // local param variables
   float tau, Vrest, VthRest, Vexc, Vinh, VinhB, deltaVth;

   // const float GMAX = 10.0;

   // local variables
   float l_activ;

   uint4 l_rnd = rnd[k];

   float l_V   = V[k];
   float l_Vth = Vth[k];

   // The correction factors to the conductances are so that if l_GSyn_* is the same every timestep,
   // then the asymptotic value of l_G_* will be l_GSyn_*
   float l_G_E  = G_E[k];
   float l_G_I  = G_I[k];
   float l_G_IB = G_IB[k];
   float l_G_Gap = G_Gap[k];

#define CHANNEL_NORM (CHANNEL_GAP+1)
   CL_MEM_GLOBAL float * GSynExc = &GSynHead[CHANNEL_EXC*numNeurons];
   CL_MEM_GLOBAL float * GSynInh = &GSynHead[CHANNEL_INH*numNeurons];
   CL_MEM_GLOBAL float * GSynInhB = &GSynHead[CHANNEL_INHB*numNeurons];
   CL_MEM_GLOBAL float * GSynGap = &GSynHead[CHANNEL_GAP*numNeurons];
   CL_MEM_GLOBAL float * GSynNorm = &GSynHead[CHANNEL_NORM*numNeurons];
   float l_GSynExc  = GSynExc[k];
   float l_GSynInh  = GSynInh[k];
   float l_GSynInhB = GSynInhB[k];
   float l_GSynGap  = GSynGap[k];
   float l_GSynNorm = normalizeInputFlag ? GSynNorm[k] : 1.0f;

   // define local param variables
   //
   tau        = params->tau;
   Vexc       = params->Vexc;
   Vinh       = params->Vinh;
   VinhB      = params->VinhB;
   Vrest      = params->Vrest;

   VthRest  = params->VthRest;
   deltaVth = params->deltaVth;

   if (normalizeInputFlag && l_GSynNorm==0 && l_GSynExc != 0) {
      fprintf(stderr, "time = %f, k = %d, normalizeInputFlag is true but GSynNorm is zero and l_GSynExc = %f\n", timed, k, l_GSynExc);
      abort();
   };
   l_GSynExc /= (l_GSynNorm + (l_GSynNorm==0 ? 1 : 0));
   GSynExcEffective[k] = l_GSynExc;
   GSynInhEffective[k] = l_GSynInh;

   // add noise
   //
   excitatoryNoise[k] = 0.0f;
   l_rnd = cl_random_get(l_rnd);
   if (cl_random_prob(l_rnd) < dt_sec*params->noiseFreqE) {
      l_rnd = cl_random_get(l_rnd);
      excitatoryNoise[k] = params->noiseAmpE*cl_random_prob(l_rnd);
      l_GSynExc = l_GSynExc + excitatoryNoise[k];
   }

   inhibitoryNoise[k] = 0.0f;
   l_rnd = cl_random_get(l_rnd);
   float r = cl_random_prob(l_rnd);
   if (r < dt_sec*params->noiseFreqI) {
      l_rnd = cl_random_get(l_rnd);
      r = cl_random_prob(l_rnd);
      inhibitoryNoise[k] = params->noiseAmpI*r;
      l_GSynInh = l_GSynInh + inhibitoryNoise[k];
   }

   inhibNoiseB[k] = 0.0f;
   l_rnd = cl_random_get(l_rnd);
   if (cl_random_prob(l_rnd) < dt_sec*params->noiseFreqIB) {
      l_rnd = cl_random_get(l_rnd);
      inhibNoiseB[k] = params->noiseAmpIB*cl_random_prob(l_rnd);
      l_GSynInhB = l_GSynInhB + inhibNoiseB[k];
   }

   const float GMAX = FLT_MAX;

   // The portion of code below uses the newer method of calculating l_V.
   float G_E_initial, G_I_initial, G_IB_initial, G_E_final, G_I_final, G_IB_final;

   G_E_initial = l_G_E + l_GSynExc;
   G_I_initial = l_G_I + l_GSynInh;
   G_IB_initial = l_G_IB + l_GSynInhB;

   G_E_initial  = (G_E_initial  > GMAX) ? GMAX : G_E_initial;
   G_I_initial  = (G_I_initial  > GMAX) ? GMAX : G_I_initial;
   G_IB_initial = (G_IB_initial > GMAX) ? GMAX : G_IB_initial;

   float totalconductance = 1.0 + G_E_initial + G_I_initial + G_IB_initial + sum_gap;
   Vmeminf[k] = (Vrest + Vexc*G_E_initial + Vinh*G_I_initial + VinhB*G_IB_initial + l_GSynGap)/totalconductance;

   G_E_final = G_E_initial * decayE;
   G_I_final = G_I_initial * decayI;
   G_IB_final = G_IB_initial * decayIB;

   l_G_Gap = l_GSynGap;

   float VmemInf1 = LCALIF_VmemInf(Vrest, Vexc, Vinh, VinhB, G_E_initial, G_I_initial, G_IB_initial, l_G_Gap, sum_gap);
   float tauInf1 = LCALIF_tauInf(tau, G_E_initial, G_I_initial, G_IB_initial, l_G_Gap);
   // float VmemInf2 = LCALIF_VmemInf(Vrest, Vexc, Vinh, VinhB, G_E_final, G_I_final, G_IB_final, l_G_Gap, sum_gap);
   // float tauInf2 = LCALIF_tauInf(tau, G_E_final, G_I_final, G_IB_final, l_G_Gap);
   l_V = exp(-dt/tauInf1)*(l_V - VmemInf1) + VmemInf1;
   // The commented-out code works very poorly if the equation for the membrane potential is stiff, which happens if tauInf < dt
   // dV1 = LIFGap_Vmem_derivative(l_V, G_E_initial, G_I_initial, G_IB_initial, l_G_Gap, Vexc, Vinh, VinhB, sum_gap, Vrest, tau);
   // dV2 = LIFGap_Vmem_derivative(l_V+dt*dV1, G_E_final, G_I_final, G_IB_final, l_G_Gap, Vexc, Vinh, VinhB, sum_gap, Vrest, tau);
   // dV = (dV1+dV2)*0.5;
   // l_V = l_V + dt*dV;

   l_G_E = G_E_final;
   l_G_I = G_I_final;
   l_G_IB = G_IB_final;

   //l_Vth updates according to traditional LIF rule in addition to the slow threshold adaptation
   //      See LCA_Equations.pdf in the documentation for a full description of the neuron adaptive firing threshold.

   Vadpt[k] = -60.0f;
   // Vadpt[k] += (dt/tauTHR) * ((integratedSpikeCount[k]/tauO) - targetRatekHz) * (Vscale/targetRatekHz);
   // float Vadpt_floor = params->Vrest + 5.0f;
   // Vadpt[k] = Vadpt[k] < Vadpt_floor ? Vadpt_floor : Vadpt[k];

   l_Vth = Vadpt[k] + decayVth * (l_Vth - Vadpt[k]);

   bool fired_flag = (l_V > l_Vth);

   l_activ = fired_flag ? 1.0f             : 0.0f;
   Vattained[k] = l_V; // Save the value of V before it drops due to the spike
   l_V     = fired_flag ? Vrest            : l_V;
   l_Vth   = fired_flag ? l_Vth + deltaVth : l_Vth;
   l_G_IB  = fired_flag ? l_G_IB + 1.0f    : l_G_IB;

   //integratedSpikeCount is the trace activity of the neuron, with an exponential decay
   integratedSpikeCount[k] = decayO * (l_activ + integratedSpikeCount[k]);

   //
   // These actions must be done outside of kernel
   //    1. set activity to 0 in boundary (if needed)
   //    2. update active indices
   //

   // store local variables back to global memory
   //
   rnd[k] = l_rnd;

   activity[kex] = l_activ;

   V[k]   = l_V;
   Vth[k] = l_Vth;

   G_E[k]  = l_G_E;
   G_I[k]  = l_G_I;
   G_IB[k] = l_G_IB;
   G_Gap[k] = l_G_Gap;

   GSynExc[k]  = 0.0f;
   GSynInh[k]  = 0.0f;
   GSynInhB[k] = 0.0f;
   GSynGap[k]  = 0.0f;
   GSynNorm[k] = 0.0f;

#ifndef PV_USE_OPENCL
   } // loop over k
#endif

}

#include "LIF_params.h"
#include "cl_random.hcl"

#ifndef PV_USE_OPENCL
#  include <math.h>
#  define EXP expf
#  define CL_KERNEL
#  define CL_MEM_GLOBAL
#  define CL_MEM_CONST
#  define CL_MEM_LOCAL
#else  /* compiling with OpenCL */
#  define EXP exp
#  define CL_KERNEL       __kernel
#  define CL_MEM_GLOBAL   __global
#  define CL_MEM_CONST    __constant
#  define CL_MEM_LOCAL    __local
#  include "conversions.hcl"
#  define CHANNEL_EXC   0
#  define CHANNEL_INH   1
#  define CHANNEL_INHB  2
#  define CHANNEL_GAP   3
#endif

inline
float LCALIF_tauInf(const float tau, const float G_E, const float G_I, const float G_IB, const float sum_gap) {
   return tau/(1+G_E+G_I+G_IB+sum_gap);
}

inline
float LCALIF_VmemInf(const float Vrest, const float V_E, const float V_I, const float V_B, const float G_E, const float G_I, const float G_B, const float G_gap, const float sumgap) {
   return (Vrest + V_E*G_E + V_I*G_I + V_B*G_B + G_gap)/(1+G_E+G_I+G_B+sumgap);
}

// The commented-out code below is only used for the Heun's method integration, which works very poorly when the equation is stiff.
/*
inline
float LIFGap_Vmem_derivative(
      const float Vmem,
      const float G_E,
      const float G_I,
      const float G_IB,
      const float G_Gap,
      const float V_E,
      const float V_I,
      const float V_IB,
      const float sum_gap,
      const float Vrest,
      const float tau) {
   float totalconductance = 1.0 + G_E + G_I + G_IB + sum_gap;
   float Vmeminf = (Vrest + V_E*G_E + V_I*G_I + V_IB*G_IB + G_Gap)/totalconductance;
   return totalconductance*(Vmeminf-Vmem)/tau;
}
 */

//
// update the state of a LCALIF layer (spiking)
//
//    assume called with 1D kernel
//
// LCALIF_update_state_beginning uses a Heun scheme for V, using values of the conductances at both the beginning and end of the timestep.  Spikes in the input are applied at the beginning of the timestep.
//
CL_KERNEL
void LCALIF_update_state(
    const int numNeurons,
    const double timed,
    const double dt,

    const int nx,
    const int ny,
    const int nf,
    const int nb,

    float Vscale,
    float * Vadpt,
    float tauTHR,
    const float targetRateHz,

    pvdata_t * integratedSpikeCount,

    CL_MEM_CONST LIF_params * params,
    CL_MEM_GLOBAL uint4 * rnd,
    CL_MEM_GLOBAL float * V,
    CL_MEM_GLOBAL float * Vth,
    CL_MEM_GLOBAL float * G_E,
    CL_MEM_GLOBAL float * G_I,
    CL_MEM_GLOBAL float * G_IB,
    CL_MEM_GLOBAL float * GSynHead,
//    CL_MEM_GLOBAL float * GSynExc,
//    CL_MEM_GLOBAL float * GSynInh,
//    CL_MEM_GLOBAL float * GSynInhB,
//    CL_MEM_GLOBAL float * GSynGap,
    CL_MEM_GLOBAL float * activity,

    const float sum_gap,
    CL_MEM_GLOBAL float * G_Gap,
    CL_MEM_GLOBAL float * Vattained,
    CL_MEM_GLOBAL float * Vmeminf,
    const int normalizeInputFlag,
    CL_MEM_GLOBAL float * GSynExcEffective)
{

   // convert target rate from Hz to kHz
   float targetRatekHz = targetRateHz/1000;

   // tau parameters
   const float tauO = 1/targetRatekHz;   //Convert target rate from kHz to ms (tauO)

   const float decayE   = EXP(-dt/params->tauE);
   const float decayI   = EXP(-dt/params->tauI);
   const float decayIB  = EXP(-dt/params->tauIB);
   const float decayVth = EXP(-dt/params->tauVth);
   const float decayO   = EXP(-dt/tauO);

   //Convert dt to seconds
   const float dt_sec = .001 * dt;

#ifndef PV_USE_OPENCL

for (int k = 0; k < nx*ny*nf; k++) {
#else
   k = get_global_id(0);
#endif

   int kex = kIndexExtended(k, nx, ny, nf, nb);

   //
   // kernel (nonheader part) begins here
   //

   // local param variables
   float tau, Vrest, VthRest, Vexc, Vinh, VinhB, deltaVth;

   // const float GMAX = 10.0;

   // local variables
   float l_activ;

   uint4 l_rnd = rnd[k];

   float l_V   = V[k];
   float l_Vth = Vth[k];

   // The correction factors to the conductances are so that if l_GSyn_* is the same every timestep,
   // then the asymptotic value of l_G_* will be l_GSyn_*
   float l_G_E  = G_E[k];
   float l_G_I  = G_I[k];
   float l_G_IB = G_IB[k];
   float l_G_Gap = G_Gap[k];

#define CHANNEL_NORM (CHANNEL_GAP+1)
   CL_MEM_GLOBAL float * GSynExc = &GSynHead[CHANNEL_EXC*numNeurons];
   CL_MEM_GLOBAL float * GSynInh = &GSynHead[CHANNEL_INH*numNeurons];
   CL_MEM_GLOBAL float * GSynInhB = &GSynHead[CHANNEL_INHB*numNeurons];
   CL_MEM_GLOBAL float * GSynGap = &GSynHead[CHANNEL_GAP*numNeurons];
   CL_MEM_GLOBAL float * GSynNorm = &GSynHead[CHANNEL_NORM*numNeurons];
   float l_GSynExc  = GSynExc[k];
   float l_GSynInh  = GSynInh[k];
   float l_GSynInhB = GSynInhB[k];
   float l_GSynGap  = GSynGap[k];
   float l_GSynNorm = normalizeInputFlag ? GSynNorm[k] : 1.0f;

   // define local param variables
   //
   tau        = params->tau;
   Vexc       = params->Vexc;
   Vinh       = params->Vinh;
   VinhB      = params->VinhB;
   Vrest      = params->Vrest;

   VthRest  = params->VthRest;
   deltaVth = params->deltaVth;

   if (normalizeInputFlag && l_GSynNorm==0 && l_GSynExc != 0) {
      fprintf(stderr, "time = %f, k = %d, normalizeInputFlag is true but GSynNorm is zero and l_GSynExc = %f\n", timed, k, l_GSynExc);
      abort();
   };
   l_GSynExc /= (l_GSynNorm + (l_GSynNorm==0 ? 1 : 0));
   GSynExcEffective[k] = l_GSynExc;

   // add noise
   //
   l_rnd = cl_random_get(l_rnd);
   if (cl_random_prob(l_rnd) < dt_sec*params->noiseFreqE) {
      l_rnd = cl_random_get(l_rnd);
      l_GSynExc = l_GSynExc + params->noiseAmpE*cl_random_prob(l_rnd);
   }

   l_rnd = cl_random_get(l_rnd);
   if (cl_random_prob(l_rnd) < dt_sec*params->noiseFreqI) {
      l_rnd = cl_random_get(l_rnd);
      l_GSynInh = l_GSynInh + params->noiseAmpI*cl_random_prob(l_rnd);
   }

   l_rnd = cl_random_get(l_rnd);
   if (cl_random_prob(l_rnd) < dt_sec*params->noiseFreqIB) {
      l_rnd = cl_random_get(l_rnd);
      l_GSynInhB = l_GSynInhB + params->noiseAmpIB*cl_random_prob(l_rnd);
   }

   const float GMAX = FLT_MAX;

   // The portion of code below uses the newer method of calculating l_V.
   float G_E_initial, G_I_initial, G_IB_initial, G_E_final, G_I_final, G_IB_final;

   G_E_initial = l_G_E + l_GSynExc;
   G_I_initial = l_G_I + l_GSynInh;
   G_IB_initial = l_G_IB + l_GSynInhB;

   G_E_initial  = (G_E_initial  > GMAX) ? GMAX : G_E_initial;
   G_I_initial  = (G_I_initial  > GMAX) ? GMAX : G_I_initial;
   G_IB_initial = (G_IB_initial > GMAX) ? GMAX : G_IB_initial;

   float totalconductance = 1.0 + G_E_initial + G_I_initial + G_IB_initial + sum_gap;
   Vmeminf[k] = (Vrest + Vexc*G_E_initial + Vinh*G_I_initial + VinhB*G_IB_initial + l_GSynGap)/totalconductance;

   G_E_final = G_E_initial * decayE;
   G_I_final = G_I_initial * decayI;
   G_IB_final = G_IB_initial * decayIB;

   l_G_Gap = l_GSynGap;

   float VmemInf1 = LCALIF_VmemInf(Vrest, Vexc, Vinh, VinhB, G_E_initial, G_I_initial, G_IB_initial, l_G_Gap, sum_gap);
   float tauInf1 = LCALIF_tauInf(tau, G_E_initial, G_I_initial, G_IB_initial, l_G_Gap);
   // float VmemInf2 = LCALIF_VmemInf(Vrest, Vexc, Vinh, VinhB, G_E_final, G_I_final, G_IB_final, l_G_Gap, sum_gap);
   // float tauInf2 = LCALIF_tauInf(tau, G_E_final, G_I_final, G_IB_final, l_G_Gap);
   l_V = exp(-dt/tauInf1)*(l_V - VmemInf1) + VmemInf1;
   // The commented-out code works very poorly if the equation for the membrane potential is stiff, which happens if tauInf < dt
   // dV1 = LIFGap_Vmem_derivative(l_V, G_E_initial, G_I_initial, G_IB_initial, l_G_Gap, Vexc, Vinh, VinhB, sum_gap, Vrest, tau);
   // dV2 = LIFGap_Vmem_derivative(l_V+dt*dV1, G_E_final, G_I_final, G_IB_final, l_G_Gap, Vexc, Vinh, VinhB, sum_gap, Vrest, tau);
   // dV = (dV1+dV2)*0.5;
   // l_V = l_V + dt*dV;

   l_G_E = G_E_final;
   l_G_I = G_I_final;
   l_G_IB = G_IB_final;

   //l_Vth updates according to traditional LIF rule in addition to the slow threshold adaptation
   //      See LCA_Equations.pdf in the documentation for a full description of the neuron adaptive firing threshold.

   Vadpt[k] += (dt/tauTHR) * ((integratedSpikeCount[k]/tauO) - targetRatekHz) * (Vscale/targetRatekHz);

   l_Vth = Vadpt[k] + decayVth * (l_Vth - Vadpt[k]);

   bool fired_flag = (l_V > l_Vth);

   l_activ = fired_flag ? 1.0f             : 0.0f;
   Vattained[k] = l_V; // Save the value of V before it drops due to the spike
   l_V     = fired_flag ? Vrest            : l_V;
   l_Vth   = fired_flag ? l_Vth + deltaVth : l_Vth;
   l_G_IB  = fired_flag ? l_G_IB + 1.0f    : l_G_IB;

   //integratedSpikeCount is the trace activity of the neuron, with an exponential decay
   integratedSpikeCount[k] = decayO * (l_activ + integratedSpikeCount[k]);

   //
   // These actions must be done outside of kernel
   //    1. set activity to 0 in boundary (if needed)
   //    2. update active indices
   //

   // store local variables back to global memory
   //
   rnd[k] = l_rnd;

   activity[kex] = l_activ;

   V[k]   = l_V;
   Vth[k] = l_Vth;

   G_E[k]  = l_G_E;
   G_I[k]  = l_G_I;
   G_IB[k] = l_G_IB;
   G_Gap[k] = l_G_Gap;

   GSynExc[k]  = 0.0f;
   GSynInh[k]  = 0.0f;
   GSynInhB[k] = 0.0f;
   GSynGap[k]  = 0.0f;
   GSynNorm[k] = 0.0f;

#ifndef PV_USE_OPENCL
   } // loop over k
#endif

}


INTEGRATION: CWS dr44 (1.1.1.1.1032); FILE MERGED 2006/01/09 19:05:25 er 1.1.1.1.1032.1: #i54546# get rid of duplicated strings not needed for translation anyway and generate a .c file containing at least the English strings

/*************************************************************************
 *
 *  OpenOffice.org - a multi-platform office productivity suite
 *
 *  $RCSfile: dfa.cl,v $
 *
 *  $Revision: 1.2 $
 *
 *  last change: $Author: kz $ $Date: 2006-01-31 18:33:27 $
 *
 *  The Contents of this file are made available subject to
 *  the terms of GNU Lesser General Public License Version 2.1.
 *
 *
 *    GNU Lesser General Public License Version 2.1
 *    =============================================
 *    Copyright 2005 by Sun Microsystems, Inc.
 *    901 San Antonio Road, Palo Alto, CA 94303, USA
 *
 *    This library is free software; you can redistribute it and/or
 *    modify it under the terms of the GNU Lesser General Public
 *    License version 2.1, as published by the Free Software Foundation.
 *
 *    This library is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *    Lesser General Public License for more details.
 *
 *    You should have received a copy of the GNU Lesser General Public
 *    License along with this library; if not, write to the Free Software
 *    Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 *    MA  02111-1307  USA
 *
 ************************************************************************/

static char datefunc_Id[]="@(#) StarCalc Datefunc AddIn (c) 1998-2000 Sun Microsystems, Inc.";

#include <string.h>
#include <stdlib.h>
#include <math.h>

#include <xlang.h>
#include <addin.h>
#include <dfa.hrc>

/**
 * the current language the Addin is using
 */
static USHORT _nLanguage=LANGUAGE_ENGLISH;

/**
 * StarCalc calls this function to set a new current Language for the Addin
 *
 * @param *nLanguage
 *
 */
void CALLTYPE SetLanguage( USHORT* nLanguage )
{
	_nLanguage = GetNeutralLanguage( *nLanguage );
}

/**
 * Null Date, initialized in GetFunctionCount
 *
 * StarCalc uses a BaseDate 12/30/1899
 * If not specified otherwise in the Settings for the Spreedsheet Document.
 *
 * There's no way to get the Spreadsheet settings from whithin a simple addin,
 * so this Addin can only be used by documents using the default BaseDate setting.
 *
 * The functions in this Addin use a BaseDate 01/01/0001
 * The nNullDate Variable is the StarCalc BaseDate converted to
 * this internal date representation.
 *
 * @see #GetFunctionCount
 *
 */

static ULONG nNullDate=0;

#define NULLDATE_Year  1899
#define NULLDATE_Month 12
#define NULLDATE_Day   30

/**
 * Array holding values for month length, used in DaysInMonth() function
 *
 * @see #DaysInMonth
 *
 */
static USHORT aDaysInMonth[12] = { 31, 28, 31, 30, 31, 30,
                                   31, 31, 30, 31, 30, 31 };

/**
 * Check if a year is a leap year in the Gregorian calendar
 *
 * @param nYear the year which should be checked
 * @return true if the year is a leap year, false otherwise.
 *
 * @see #DaysInMonth, #IsLeapYear,
 * @see #ScDate_DaysInMonth, #ScDate_IsLeapYear, #ScDate_WeeksInYear
 *
 */
static BOOL IsLeapYear( USHORT nYear )
{
    return (((nYear % 4) == 0) && ((nYear % 100) != 0) || ((nYear % 400) == 0));
}

/**
 * Get the number of days in a specified month
 *
 * @param nMonth the number of the Month
 * @param nYear the year
 * @return number of days
 *
 */
static USHORT DaysInMonth( USHORT nMonth, USHORT nYear )
{
    if ( nMonth != 2 )
        return aDaysInMonth[nMonth-1];
    else
    {
        if ( IsLeapYear(nYear) )
            return aDaysInMonth[nMonth-1] + 1;
        else
            return aDaysInMonth[nMonth-1];
    }
}

/**
 * Convert a date to a count of days starting from 01/01/0001
 *
 * The internal representation of a Date used in this Addin
 * is the number of days between 01/01/0001 and the date
 * this function converts a Day , Month, Year representation
 * to this internal Date value.
 *
 * @param nDay the day of the Month
 * @param nMonth the number of the Month
 * @param nYear the Year
 * @return count of days from 01/01/0001 to the date specified
 *
 */
static long DateToDays( USHORT nDay, USHORT nMonth, USHORT nYear )
{
    long nDays;
    USHORT i;

    nDays = ((ULONG)nYear-1) * 365;
    nDays += ((nYear-1) / 4) - ((nYear-1) / 100) + ((nYear-1) / 400);

    for( i = 1; i < nMonth; i++ )
        nDays += DaysInMonth(i,nYear);
    nDays += nDay;

    return nDays;
}

/**
 * Convert a count of days starting from 01/01/0001 to a date
 *
 * The internal representation of a Date used in this Addin
 * is the number of days between 01/01/0001 and the date
 * this function converts this internal Date value
 * to a Day , Month, Year representation of a Date.
 *
 * @param nDay count of days from 01/01/0001
 * @param *pDay pointer to a variable for the day of the month
 * @param *pMonth pointer to a variable for the month
 * @param *pYear pointer to a variable for the year
 *
 */
static void DaysToDate( long nDays,
                        USHORT *pDay, USHORT *pMonth, USHORT *pYear )
{
    long    nTempDays;
    long    i = 0;
    BOOL    bCalc;

    do
    {
        nTempDays = (long)nDays;
        *pYear = (USHORT)((nTempDays / 365) - i);
        nTempDays -= ((ULONG) *pYear -1) * 365;
        nTempDays -= (( *pYear -1) / 4) - (( *pYear -1) / 100) + ((*pYear -1) / 400);
        bCalc = FALSE;
        if ( nTempDays < 1 )
        {
            i++;
            bCalc = TRUE;
        }
        else
        {
            if ( nTempDays > 365 )
            {
                if ( (nTempDays != 366) || !IsLeapYear( *pYear ) )
                {
                    i--;
                    bCalc = TRUE;
                }
            }
        }
    }
    while ( bCalc );

    *pMonth = 1;
    while ( (ULONG)nTempDays > DaysInMonth( *pMonth, *pYear ) )
    {
        nTempDays -= DaysInMonth( *pMonth, *pYear );
        (*pMonth)++;
    }
    *pDay = (USHORT)nTempDays;
}

/**
 * Get week difference between 2 dates
 *
 * new Weeks(date1,date2,mode) function for StarCalc
 *
 * Two modes of operation are provided.
 * The first is just a simple division by 7 calculation.
 *
 * The second calculates the diffence by week of year.
 *
 * The International Standard IS-8601 has decreed that Monday
 * shall be the first day of the week.
 *
 * A week that lies partly in one year and partly in annother
 * is assigned a number in the the year in which most of its days lie.
 *
 * That means that week 1 of any year is the week that contains the 4. January
 *
 * The internal representation of a Date used in the Addin is the number of days based on 01/01/0001
 *
 * A WeekDay can be then calculated by substracting 1 and calculating the rest of
 * a division by 7, which gives a 0 - 6 value for Monday - Sunday
 *
 * Using the 4. January rule explained above the formula
 *
 *	nWeek1= ( nDays1 - nJan4 + ( (nJan4-1) % 7 ) ) / 7 + 1;
 *
 * calculates a number between 0-53 for each day which is in the same year as nJan4
 * where 0 means that this week belonged to the year before.
 *
 * If a day in the same or annother year is used in this formula this calculates
 * an calendar week offset from a given 4. January
 *
 *	nWeek2 = ( nDays2 - nJan4 + ( (nJan4-1) % 7 ) ) / 7 + 1;
 *
 * The 4.January of first Date Argument can thus be used to calculate
 * the week difference by calendar weeks which is then nWeek = nWeek2 - nWeek1
 *
 * which can be optimized to
 *
 * nWeek = ( (nDays2-nJan4+((nJan4-1)%7))/7 ) - ( (nDays1-nJan4+((nJan4-1)%7))/7 )
 *
 * Note: All calculations are operating on the long integer data type
 * % is the modulo operator in C which calculates the rest of an Integer division
 *
 *
 * @param *r - return value for the StarCalc function
 * @param d1 - date value (in StarCalc representation based 12/30/1899), usually the older date
 * @param d2 - date value (in StarCalc representation based 12/30/1899), usually the younger date
 * @param dMode - mode of operation
 *
 * mode 0 is the interval between the dates in month, that is days / 7
 *
 * mode 1 is the difference by week of year
 *
 */
void CALLTYPE ScDate_GetDiffWeeks(double *r, double *d1, double *d2, double *dMode)
{
  long nDays1=0;
  long nDays2=0;
  int nMode=0;

  if ( d1 ) nDays1=(long)floor(*d1)+nNullDate;
  if ( d2 ) nDays2=(long)floor(*d2)+nNullDate;

  if ( dMode) nMode=(int)*dMode;

  if ( nMode == 1 ) {

	USHORT nDay,nMonth,nYear;
	long nJan4;

	DaysToDate(nDays1,&nDay,&nMonth,&nYear);
	nJan4=DateToDays(4,1,nYear);

	*r=(double) ( ( (nDays2-nJan4+((nJan4-1)%7))/7 ) - ( (nDays1-nJan4+((nJan4-1)%7))/7 ) );

  } else {

	*r= (double) ( (nDays2 - nDays1) / 7 ) ;
  }

}

/**
 * Get month difference between 2 dates
 * =Month(start, end, mode) Function for StarCalc
 *
 * two modes are provided
 *
 * @param *r - return value for the StarCalc function
 * @param d1 - date value, start date
 * @param d2 - date value, end date
 * @param dMode - mode of operation
 *
 * mode 0 is the interval between the dates in month
 *
 * mode 1 is the difference in calendar month
 *
 */
void CALLTYPE ScDate_GetDiffMonths(double *r, double *d1, double *d2, double *dMode)
{
  USHORT nDay1,nMonth1,nYear1;
  USHORT nDay2,nMonth2,nYear2;
  long nDays1=0;
  long nDays2=0;
  int nMode=0;

  if ( dMode) nMode=(int)*dMode;

  if ( d1 ) nDays1=(long)floor(*d1)+nNullDate;
  if ( d2 ) nDays2=(long)floor(*d2)+nNullDate;

  DaysToDate(nDays1,&nDay1,&nMonth1,&nYear1);
  DaysToDate(nDays2,&nDay2,&nMonth2,&nYear2);

  *r=(double) ( nMonth2 - nMonth1 + (nYear2 - nYear1) * 12 );
  if ( nMode == 1 || nDays1 == nDays2 ) return;

  if ( nDays1 < nDays2 ) {
	if ( nDay1 > nDay2 ) {
		*r -= 1;
	}
  } else {
	if ( nDay1 < nDay2 ) {
		*r += 1;
	}
  }

}

/**
 * Get Year difference between 2 dates
 *
 * two modes are provided
 *
 * @param *r - return value for the StarCalc function
 * @param d1 - date value, start date
 * @param d2 - date value, end date
 * @param dMode - mode of operation
 *
 * mode 0 is the interval between the dates in years
 *
 * mode 1 is the difference in calendar years
 *
 */
void CALLTYPE ScDate_GetDiffYears(double *r, double *d1, double *d2, double *dMode)
{
  USHORT nDay1,nMonth1,nYear1;
  USHORT nDay2,nMonth2,nYear2;
  long nDays1=0;
  long nDays2=0;
  int nMode=0;

  if ( dMode) nMode=(int)*dMode;

  if ( d1 ) nDays1=(long)floor(*d1)+nNullDate;
  if ( d2 ) nDays2=(long)floor(*d2)+nNullDate;

  DaysToDate(nDays1,&nDay1,&nMonth1,&nYear1);
  DaysToDate(nDays2,&nDay2,&nMonth2,&nYear2);
  if ( nMode != 1 ) {
	ScDate_GetDiffMonths(r,d1,d2,dMode);
	*r= (double) ( ((int) *r) / 12 );
  } else {
	  *r=(double) ( nYear2 - nYear1 );
  }
}

/**
 * Check if a Date is in a leap year in the Gregorian calendar
 *
 * @param *r - return value for the StarCalc function
 * @param d - date value (in StarCalc representation based 12/30/1899)
 *
 */
void CALLTYPE ScDate_IsLeapYear(double *r, double *d)
{
  ULONG nDays;
  USHORT nDay, nMonth, nYear;
  double v=0.0;

  if ( d ) v=*d;
  nDays=(int) v + nNullDate;

  DaysToDate(nDays,&nDay,&nMonth,&nYear);

  *r=(double) ( IsLeapYear(nYear) );

}

/**
 * Get the Number of Days in the month for a date
 *
 * @param *r - return value for the StarCalc function
 * @param d - date value (in StarCalc representation based 12/30/1899)
 *
 */
void CALLTYPE ScDate_DaysInMonth(double *r, double *d)
{
  ULONG nDays;
  USHORT nDay, nMonth, nYear;
  double v=0.0;

  if ( d ) v=*d;
  nDays=(int) v + nNullDate;

  DaysToDate(nDays,&nDay,&nMonth,&nYear);
  *r=(double) ( DaysInMonth( nMonth, nYear) );

}

/**
 * Get number of weeks in the year for a date
 *
 * Most years have 52 weeks, but years that start on a Thursday
 * and leep years that start on a Wednesday have 53 weeks
 *
 * The International Standard IS-8601 has decreed that Monday
 * shall be the first day of the week.
 *
 * A WeekDay can be calculated by substracting 1 and calculating the rest of
 * a division by 7 from the internal date represention
 * which gives a 0 - 6 value for Monday - Sunday
 *
 * @param *r - return value for the StarCalc function
 * @param d - date value (in StarCalc represantaion based 30.12.1899)
 *
 * @see #IsLeapYear #WeekNumber
 *
 */
void CALLTYPE ScDate_WeeksInYear(double *r, double *d)
{
  ULONG nDays;
  USHORT nDay, nMonth, nYear;
  double v=0.0;
  long nJan1WeekDay;

  if ( d ) v=*d;
  nDays=(int) v + nNullDate;

  DaysToDate(nDays,&nDay,&nMonth,&nYear);

  nJan1WeekDay= ( DateToDays(1,1,nYear) - 1) % 7;

  if ( nJan1WeekDay == 3 ) { /* Thursday */
	*r=(double) 53;
	return;
  } else if ( nJan1WeekDay == 2 ) { /* Wednesday */
	*r= (double) ( IsLeapYear(nYear) ? 53 : 52 );
  } else {
	*r= (double) 52;
  }
}

/**
 * Get number of days in the year of a date specified
 *
 * @param *r - return value for the StarCalc function
 * @param d - date value (in StarCalc represantaion based 30.12.1899)
 *
 */
void CALLTYPE ScDate_DaysInYear(double *r, double *d)
{
  ULONG nDays;
  USHORT nDay, nMonth, nYear;
  double v=0.0;

  if ( d ) v=*d;
  nDays=(int) v + nNullDate;

  DaysToDate(nDays,&nDay,&nMonth,&nYear);
  *r=(double) ( IsLeapYear(nYear) ? 366 : 365 );

}

/**
 * Tell StarCalc how many new functions this Addin provides.
 *
 * It's called before any of these new functions is actually
 * executed and is also used to initialize the NullDate here.
 *
 * StarCalc uses a Date Base 12/30/1899
 * If not specified otherwise in the Options for the Spreedsheet Document
 *
 *
 * @param *nCount - returns the number of functions which are exported to StarCalc
 *
 */
void CALLTYPE GetFunctionCount( USHORT *nCount )
{

  /* initialize nNullDate Value 0 is 12/30/1899 */
  nNullDate=DateToDays(NULLDATE_Day, NULLDATE_Month, NULLDATE_Year);

  *nCount = 7;
}

/**
 * Provides neccessary data for each new function to StarCalc
 *
 * @param *nNo Input: Function number between 0 and nCount - 1
 * @param *pFuncName Output: Functionname which should be called in the AddIn-DLL
 * @param *nParamCount Output: Number of Parameter. Must be greater than 0, because there's always a return-Value. Maximum is 16.
 * @param *peType Output: Pointer to arrray with exactly 16 variables of typ Paramtype. nParamCount Entries are set to the type of the corresponding Parameters.
 * @param *pInternalName Output: Functionname as seen by the Spreadsheet user
 *
 * @see #GetFunctionCount, #GetParameterDescription
 *
 */
void CALLTYPE GetFunctionData( USHORT *    nNo,
                               char *      pFuncName,
                               USHORT *    nParamCount,
                               ParamType * peType,
                               char *      pInternalName )
{

	 switch( *nNo ) {
	 case 0:
	 SO_StringCopy( pInternalName, getText(DFA_WEEK_NAME) );
	 SO_StringCopy( pFuncName,     "ScDate_GetDiffWeeks" );
	 peType[0] = PTR_DOUBLE;
	 peType[1] = PTR_DOUBLE;
	 peType[2] = PTR_DOUBLE;
	 peType[3] = PTR_DOUBLE;
	 *nParamCount=4;
	 break;

	 case 1:
	 SO_StringCopy( pInternalName, getText(DFA_MONTHS_NAME) );
	 SO_StringCopy( pFuncName,     "ScDate_GetDiffMonths" );
	 peType[0] = PTR_DOUBLE;
	 peType[1] = PTR_DOUBLE;
	 peType[2] = PTR_DOUBLE;
	 peType[3] = PTR_DOUBLE;
	 *nParamCount=4;
	 break;

	 case 2:
	 SO_StringCopy( pInternalName, getText(DFA_YEARS_NAME) );
	 SO_StringCopy( pFuncName,     "ScDate_GetDiffYears" );
	 peType[0] = PTR_DOUBLE;
	 peType[1] = PTR_DOUBLE;
	 peType[2] = PTR_DOUBLE;
	 peType[3] = PTR_DOUBLE;
	 *nParamCount=4;
	 break;

	 case 3:
	 SO_StringCopy( pInternalName, getText(DFA_ISLEAPYEAR_NAME) );
	 SO_StringCopy( pFuncName,     "ScDate_IsLeapYear" );
	 peType[0] = PTR_DOUBLE;
	 peType[1] = PTR_DOUBLE;
	 *nParamCount=2;
	 break;

	 case 4:
	 SO_StringCopy( pInternalName, getText(DFA_DAYSINMONTH_NAME) );
	 SO_StringCopy( pFuncName,     "ScDate_DaysInMonth" );
	 peType[0] = PTR_DOUBLE;
	 peType[1] = PTR_DOUBLE;
	 *nParamCount=2;
	 break;

	 case 5:
	 SO_StringCopy( pInternalName, getText(DFA_DAYSINYEAR_NAME) );
	 SO_StringCopy( pFuncName,     "ScDate_DaysInYear" );
	 peType[0] = PTR_DOUBLE;
	 peType[1] = PTR_DOUBLE;
	 *nParamCount=2;
	 break;

	 case 6:
	 SO_StringCopy( pInternalName, getText(DFA_WEEKSINYEAR_NAME) );
	 SO_StringCopy( pFuncName,     "ScDate_WeeksInYear" );
	 peType[0] = PTR_DOUBLE;
	 peType[1] = PTR_DOUBLE;
	 *nParamCount=2;
	 break;

	 default:
            *nParamCount    = 0;
            *pFuncName     = 0;
            *pInternalName = 0;
            break;
    }
}

/**
 * Provides descriptions for each new function to StarCalc
 * which are shown is the autopilot
 *
 * @param *nNo Input Parameter, Function number between 0 and nCount - 1
 * @param *nParam Parameter Number
 * @param *pName Output: Name of the parameter
 * @param *pDesc Output: Description of the parameter
 *
 * @see #GetFunctionCount, #GetParameterDescription
 */
void CALLTYPE GetParameterDescription( USHORT* nNo, USHORT* nParam,
char* pName, char* pDesc )
{
	*pName = 0;
	*pDesc = 0;

	switch ( *nNo ) {
	case 0:	/* Weeks */
		switch ( *nParam ) {
		case 0:
			SO_StringCopy(pDesc,getText(DFA_WEEK_DESC));
			break;
		case 1:
			SO_StringCopy(pName,getText(DFA_PAR_DATE1_NAME));
			SO_StringCopy(pDesc,getText(DFA_WEEK_PAR1_DESC));
			break;
		case 2:
			SO_StringCopy(pName,getText(DFA_PAR_DATE2_NAME));
			SO_StringCopy(pDesc,getText(DFA_WEEK_PAR2_DESC));
			break;
		case 3:
			SO_StringCopy(pName,getText(DFA_PAR_MODE_NAME));
			SO_StringCopy(pDesc,getText(DFA_WEEK_PAR3_DESC));
			break;
		}
		break;
	case 1: /* Months */
		switch ( *nParam ) {
		case 0:
			SO_StringCopy(pDesc,getText(DFA_MONTHS_DESC));
			break;
		case 1:
			SO_StringCopy(pName,getText(DFA_PAR_DATE1_NAME));
			SO_StringCopy(pDesc,getText(DFA_MONTHS_PAR1_DESC));
			break;
		case 2:
			SO_StringCopy(pName,getText(DFA_PAR_DATE2_NAME));
			SO_StringCopy(pDesc,getText(DFA_MONTHS_PAR2_DESC));
			break;
		case 3:
			SO_StringCopy(pName,getText(DFA_PAR_MODE_NAME));
			SO_StringCopy(pDesc,getText(DFA_MONTHS_PAR3_DESC));
			break;
		}
		break;
	case 2: /* Years */
		switch ( *nParam ) {
		case 0:
			SO_StringCopy(pDesc,getText(DFA_YEARS_DESC));
			break;
		case 1:
			SO_StringCopy(pName,getText(DFA_PAR_DATE1_NAME));
			SO_StringCopy(pDesc,getText(DFA_YEARS_PAR1_DESC));
			break;
		case 2:
			SO_StringCopy(pName,getText(DFA_PAR_DATE2_NAME));
			SO_StringCopy(pDesc,getText(DFA_YEARS_PAR2_DESC));
			break;
		case 3:
			SO_StringCopy(pName,getText(DFA_PAR_MODE_NAME));
			SO_StringCopy(pDesc,getText(DFA_YEARS_PAR3_DESC));
			break;
		}
	   break;
	case 3:	/* IsLeapYear */
		switch ( *nParam ) {
		case 0:
			SO_StringCopy(pDesc,getText(DFA_ISLEAPYEAR_DESC));
			break;
		case 1:
			SO_StringCopy(pName,getText(DFA_PAR_DATE_NAME));
			SO_StringCopy(pDesc,getText(DFA_PAR_DATE_DESC)); /* StarCalc Value */
			break;
		}
		break;
	case 4:	/* DaysInMonth */
		switch ( *nParam ) {
		case 0:
			SO_StringCopy(pDesc,getText(DFA_DAYSINMONTH_DESC));
			break;
		case 1:
			SO_StringCopy(pName,getText(DFA_PAR_DATE_NAME));
			SO_StringCopy(pDesc,getText(DFA_PAR_DATE_DESC)); /* StarCalc Value */
			break;
		}
		break;
	case 5:	/* DaysInYear */
		switch ( *nParam ) {
		case 0:
			SO_StringCopy(pDesc,getText(DFA_DAYSINYEAR_DESC));
			break;
		case 1:
			SO_StringCopy(pName,getText(DFA_PAR_DATE_NAME));
			SO_StringCopy(pDesc,getText(DFA_PAR_DATE_DESC)); /* StarCalc Value */
			break;
		}
		break;

	case 6:	/* WeeksInYear */
		switch ( *nParam ) {
		case 0:
			SO_StringCopy(pDesc,getText(DFA_WEEKSINYEAR_DESC));
			break;
		case 1:
			SO_StringCopy(pName,getText(DFA_PAR_DATE_NAME));
			SO_StringCopy(pDesc,getText(DFA_PAR_DATE_DESC)); /* StarCalc Value */
			break;
		}
		break;
	}

}

/*------------------------------------------------------------------------

$Workfile:   DFA.CL  $

$Header: /zpool/svn/migration/cvs_rep_09_09_08/code/sc/addin/datefunc/dfa.cl,v 1.1.1.1 2000-09-18 16:44:46 hr Exp $

Description:	StarCalc Datefunc AddIn Example

(c) Copyright 1998 - 2000, Sun Microsystems, Inc.

------------------------------------------------------------------------*/

static char datefunc_Id[]="@(#) StarCalc Datefunc AddIn (c) 1998-2000 Sun Microsystems, Inc.";

#include <string.h>
#include <stdlib.h>
#include <math.h>

#include <xlang.h>
#include <addin.h>
#include <dfa.hrc>

/**
 * the current language the Addin is using
 */
static USHORT _nLanguage=LANGUAGE_ENGLISH;

/**
 * StarCalc calls this function to set a new current Language for the Addin
 *
 * @param *nLanguage
 *
 */
void CALLTYPE SetLanguage( USHORT* nLanguage )
{
	_nLanguage = GetNeutralLanguage( *nLanguage );
}

/**
 * Null Date, initialized in GetFunctionCount
 *
 * StarCalc uses a BaseDate 12/30/1899
 * If not specified otherwise in the Settings for the Spreedsheet Document.
 *
 * There's no way to get the Spreadsheet settings from whithin a simple addin,
 * so this Addin can only be used by documents using the default BaseDate setting.
 *
 * The functions in this Addin use a BaseDate 01/01/0001
 * The nNullDate Variable is the StarCalc BaseDate converted to
 * this internal date representation.
 *
 * @see #GetFunctionCount
 *
 */

static ULONG nNullDate=0;

#define NULLDATE_Year  1899
#define NULLDATE_Month 12
#define NULLDATE_Day   30

/**
 * Array holding values for month length, used in DaysInMonth() function
 *
 * @see #DaysInMonth
 *
 */
static USHORT aDaysInMonth[12] = { 31, 28, 31, 30, 31, 30,
                                   31, 31, 30, 31, 30, 31 };

/**
 * Check if a year is a leap year in the Gregorian calendar
 *
 * @param nYear the year which should be checked
 * @return true if the year is a leap year, false otherwise.
 *
 * @see #DaysInMonth, #IsLeapYear,
 * @see #ScDate_DaysInMonth, #ScDate_IsLeapYear, #ScDate_WeeksInYear
 *
 */
static BOOL IsLeapYear( USHORT nYear )
{
    return (((nYear % 4) == 0) && ((nYear % 100) != 0) || ((nYear % 400) == 0));
}

/**
 * Get the number of days in a specified month
 *
 * @param nMonth the number of the Month
 * @param nYear the year
 * @return number of days
 *
 */
static USHORT DaysInMonth( USHORT nMonth, USHORT nYear )
{
    if ( nMonth != 2 )
        return aDaysInMonth[nMonth-1];
    else
    {
        if ( IsLeapYear(nYear) )
            return aDaysInMonth[nMonth-1] + 1;
        else
            return aDaysInMonth[nMonth-1];
    }
}

/**
 * Convert a date to a count of days starting from 01/01/0001
 *
 * The internal representation of a Date used in this Addin
 * is the number of days between 01/01/0001 and the date
 * this function converts a Day , Month, Year representation
 * to this internal Date value.
 *
 * @param nDay the day of the Month
 * @param nMonth the number of the Month
 * @param nYear the Year
 * @return count of days from 01/01/0001 to the date specified
 *
 */
static long DateToDays( USHORT nDay, USHORT nMonth, USHORT nYear )
{
    long nDays;
    USHORT i;

    nDays = ((ULONG)nYear-1) * 365;
    nDays += ((nYear-1) / 4) - ((nYear-1) / 100) + ((nYear-1) / 400);

    for( i = 1; i < nMonth; i++ )
        nDays += DaysInMonth(i,nYear);
    nDays += nDay;

    return nDays;
}

/**
 * Convert a count of days starting from 01/01/0001 to a date
 *
 * The internal representation of a Date used in this Addin
 * is the number of days between 01/01/0001 and the date
 * this function converts this internal Date value
 * to a Day , Month, Year representation of a Date.
 *
 * @param nDay count of days from 01/01/0001
 * @param *pDay pointer to a variable for the day of the month
 * @param *pMonth pointer to a variable for the month
 * @param *pYear pointer to a variable for the year
 *
 */
static void DaysToDate( long nDays,
                        USHORT *pDay, USHORT *pMonth, USHORT *pYear )
{
    long    nTempDays;
    long    i = 0;
    BOOL    bCalc;

    do
    {
        nTempDays = (long)nDays;
        *pYear = (USHORT)((nTempDays / 365) - i);
        nTempDays -= ((ULONG) *pYear -1) * 365;
        nTempDays -= (( *pYear -1) / 4) - (( *pYear -1) / 100) + ((*pYear -1) / 400);
        bCalc = FALSE;
        if ( nTempDays < 1 )
        {
            i++;
            bCalc = TRUE;
        }
        else
        {
            if ( nTempDays > 365 )
            {
                if ( (nTempDays != 366) || !IsLeapYear( *pYear ) )
                {
                    i--;
                    bCalc = TRUE;
                }
            }
        }
    }
    while ( bCalc );

    *pMonth = 1;
    while ( (ULONG)nTempDays > DaysInMonth( *pMonth, *pYear ) )
    {
        nTempDays -= DaysInMonth( *pMonth, *pYear );
        (*pMonth)++;
    }
    *pDay = (USHORT)nTempDays;
}

/**
 * Get week difference between 2 dates
 *
 * new Weeks(date1,date2,mode) function for StarCalc
 *
 * Two modes of operation are provided.
 * The first is just a simple division by 7 calculation.
 *
 * The second calculates the diffence by week of year.
 *
 * The International Standard IS-8601 has decreed that Monday
 * shall be the first day of the week.
 *
 * A week that lies partly in one year and partly in annother
 * is assigned a number in the the year in which most of its days lie.
 *
 * That means that week 1 of any year is the week that contains the 4. January
 *
 * The internal representation of a Date used in the Addin is the number of days based on 01/01/0001
 *
 * A WeekDay can be then calculated by substracting 1 and calculating the rest of
 * a division by 7, which gives a 0 - 6 value for Monday - Sunday
 *
 * Using the 4. January rule explained above the formula
 *
 *	nWeek1= ( nDays1 - nJan4 + ( (nJan4-1) % 7 ) ) / 7 + 1;
 *
 * calculates a number between 0-53 for each day which is in the same year as nJan4
 * where 0 means that this week belonged to the year before.
 *
 * If a day in the same or annother year is used in this formula this calculates
 * an calendar week offset from a given 4. January
 *
 *	nWeek2 = ( nDays2 - nJan4 + ( (nJan4-1) % 7 ) ) / 7 + 1;
 *
 * The 4.January of first Date Argument can thus be used to calculate
 * the week difference by calendar weeks which is then nWeek = nWeek2 - nWeek1
 *
 * which can be optimized to
 *
 * nWeek = ( (nDays2-nJan4+((nJan4-1)%7))/7 ) - ( (nDays1-nJan4+((nJan4-1)%7))/7 )
 *
 * Note: All calculations are operating on the long integer data type
 * % is the modulo operator in C which calculates the rest of an Integer division
 *
 *
 * @param *r - return value for the StarCalc function
 * @param d1 - date value (in StarCalc representation based 12/30/1899), usually the older date
 * @param d2 - date value (in StarCalc representation based 12/30/1899), usually the younger date
 * @param dMode - mode of operation
 *
 * mode 0 is the interval between the dates in month, that is days / 7
 *
 * mode 1 is the difference by week of year
 *
 */
void CALLTYPE ScDate_GetDiffWeeks(double *r, double *d1, double *d2, double *dMode)
{
  long nDays1=0;
  long nDays2=0;
  int nMode=0;

  if ( d1 ) nDays1=(long)floor(*d1)+nNullDate;
  if ( d2 ) nDays2=(long)floor(*d2)+nNullDate;

  if ( dMode) nMode=(int)*dMode;

  if ( nMode == 1 ) {

	USHORT nDay,nMonth,nYear;
	long nJan4;

	DaysToDate(nDays1,&nDay,&nMonth,&nYear);
	nJan4=DateToDays(4,1,nYear);

	*r=(double) ( ( (nDays2-nJan4+((nJan4-1)%7))/7 ) - ( (nDays1-nJan4+((nJan4-1)%7))/7 ) );

  } else {

	*r= (double) ( (nDays2 - nDays1) / 7 ) ;
  }

}

/**
 * Get month difference between 2 dates
 * =Month(start, end, mode) Function for StarCalc
 *
 * two modes are provided
 *
 * @param *r - return value for the StarCalc function
 * @param d1 - date value, start date
 * @param d2 - date value, end date
 * @param dMode - mode of operation
 *
 * mode 0 is the interval between the dates in month
 *
 * mode 1 is the difference in calendar month
 *
 */
void CALLTYPE ScDate_GetDiffMonths(double *r, double *d1, double *d2, double *dMode)
{
  USHORT nDay1,nMonth1,nYear1;
  USHORT nDay2,nMonth2,nYear2;
  long nDays1=0;
  long nDays2=0;
  int nMode=0;

  if ( dMode) nMode=(int)*dMode;

  if ( d1 ) nDays1=(long)floor(*d1)+nNullDate;
  if ( d2 ) nDays2=(long)floor(*d2)+nNullDate;

  DaysToDate(nDays1,&nDay1,&nMonth1,&nYear1);
  DaysToDate(nDays2,&nDay2,&nMonth2,&nYear2);

  *r=(double) ( nMonth2 - nMonth1 + (nYear2 - nYear1) * 12 );
  if ( nMode == 1 || nDays1 == nDays2 ) return;

  if ( nDays1 < nDays2 ) {
	if ( nDay1 > nDay2 ) {
		*r -= 1;
	}
  } else {
	if ( nDay1 < nDay2 ) {
		*r += 1;
	}
  }

}

/**
 * Get Year difference between 2 dates
 *
 * two modes are provided
 *
 * @param *r - return value for the StarCalc function
 * @param d1 - date value, start date
 * @param d2 - date value, end date
 * @param dMode - mode of operation
 *
 * mode 0 is the interval between the dates in years
 *
 * mode 1 is the difference in calendar years
 *
 */
void CALLTYPE ScDate_GetDiffYears(double *r, double *d1, double *d2, double *dMode)
{
  USHORT nDay1,nMonth1,nYear1;
  USHORT nDay2,nMonth2,nYear2;
  long nDays1=0;
  long nDays2=0;
  int nMode=0;

  if ( dMode) nMode=(int)*dMode;

  if ( d1 ) nDays1=(long)floor(*d1)+nNullDate;
  if ( d2 ) nDays2=(long)floor(*d2)+nNullDate;

  DaysToDate(nDays1,&nDay1,&nMonth1,&nYear1);
  DaysToDate(nDays2,&nDay2,&nMonth2,&nYear2);
  if ( nMode != 1 ) {
	ScDate_GetDiffMonths(r,d1,d2,dMode);
	*r= (double) ( ((int) *r) / 12 );
  } else {
	  *r=(double) ( nYear2 - nYear1 );
  }
}

/**
 * Check if a Date is in a leap year in the Gregorian calendar
 *
 * @param *r - return value for the StarCalc function
 * @param d - date value (in StarCalc representation based 12/30/1899)
 *
 */
void CALLTYPE ScDate_IsLeapYear(double *r, double *d)
{
  ULONG nDays;
  USHORT nDay, nMonth, nYear;
  double v=0.0;

  if ( d ) v=*d;
  nDays=(int) v + nNullDate;

  DaysToDate(nDays,&nDay,&nMonth,&nYear);

  *r=(double) ( IsLeapYear(nYear) );

}

/**
 * Get the Number of Days in the month for a date
 *
 * @param *r - return value for the StarCalc function
 * @param d - date value (in StarCalc representation based 12/30/1899)
 *
 */
void CALLTYPE ScDate_DaysInMonth(double *r, double *d)
{
  ULONG nDays;
  USHORT nDay, nMonth, nYear;
  double v=0.0;

  if ( d ) v=*d;
  nDays=(int) v + nNullDate;

  DaysToDate(nDays,&nDay,&nMonth,&nYear);
  *r=(double) ( DaysInMonth( nMonth, nYear) );

}

/**
 * Get number of weeks in the year for a date
 *
 * Most years have 52 weeks, but years that start on a Thursday
 * and leep years that start on a Wednesday have 53 weeks
 *
 * The International Standard IS-8601 has decreed that Monday
 * shall be the first day of the week.
 *
 * A WeekDay can be calculated by substracting 1 and calculating the rest of
 * a division by 7 from the internal date represention
 * which gives a 0 - 6 value for Monday - Sunday
 *
 * @param *r - return value for the StarCalc function
 * @param d - date value (in StarCalc represantaion based 30.12.1899)
 *
 * @see #IsLeapYear #WeekNumber
 *
 */
void CALLTYPE ScDate_WeeksInYear(double *r, double *d)
{
  ULONG nDays;
  USHORT nDay, nMonth, nYear;
  double v=0.0;
  long nJan1WeekDay;

  if ( d ) v=*d;
  nDays=(int) v + nNullDate;

  DaysToDate(nDays,&nDay,&nMonth,&nYear);

  nJan1WeekDay= ( DateToDays(1,1,nYear) - 1) % 7;

  if ( nJan1WeekDay == 3 ) { /* Thursday */
	*r=(double) 53;
	return;
  } else if ( nJan1WeekDay == 2 ) { /* Wednesday */
	*r= (double) ( IsLeapYear(nYear) ? 53 : 52 );
  } else {
	*r= (double) 52;
  }
}

/**
 * Get number of days in the year of a date specified
 *
 * @param *r - return value for the StarCalc function
 * @param d - date value (in StarCalc represantaion based 30.12.1899)
 *
 */
void CALLTYPE ScDate_DaysInYear(double *r, double *d)
{
  ULONG nDays;
  USHORT nDay, nMonth, nYear;
  double v=0.0;

  if ( d ) v=*d;
  nDays=(int) v + nNullDate;

  DaysToDate(nDays,&nDay,&nMonth,&nYear);
  *r=(double) ( IsLeapYear(nYear) ? 366 : 365 );

}

/**
 * Tell StarCalc how many new functions this Addin provides.
 *
 * It's called before any of these new functions is actually
 * executed and is also used to initialize the NullDate here.
 *
 * StarCalc uses a Date Base 12/30/1899
 * If not specified otherwise in the Options for the Spreedsheet Document
 *
 *
 * @param *nCount - returns the number of functions which are exported to StarCalc
 *
 */
void CALLTYPE GetFunctionCount( USHORT *nCount )
{

  /* initialize nNullDate Value 0 is 12/30/1899 */
  nNullDate=DateToDays(NULLDATE_Day, NULLDATE_Month, NULLDATE_Year);

  *nCount = 7;
}

/**
 * Provides neccessary data for each new function to StarCalc
 *
 * @param *nNo Input: Function number between 0 and nCount - 1
 * @param *pFuncName Output: Functionname which should be called in the AddIn-DLL
 * @param *nParamCount Output: Number of Parameter. Must be greater than 0, because there's always a return-Value. Maximum is 16.
 * @param *peType Output: Pointer to arrray with exactly 16 variables of typ Paramtype. nParamCount Entries are set to the type of the corresponding Parameters.
 * @param *pInternalName Output: Functionname as seen by the Spreadsheet user
 *
 * @see #GetFunctionCount, #GetParameterDescription
 *
 */
void CALLTYPE GetFunctionData( USHORT *    nNo,
                               char *      pFuncName,
                               USHORT *    nParamCount,
                               ParamType * peType,
                               char *      pInternalName )
{

	 switch( *nNo ) {
	 case 0:
	 SO_StringCopy( pInternalName, getText(DFA_WEEK_NAME) );
	 SO_StringCopy( pFuncName,     "ScDate_GetDiffWeeks" );
	 peType[0] = PTR_DOUBLE;
	 peType[1] = PTR_DOUBLE;
	 peType[2] = PTR_DOUBLE;
	 peType[3] = PTR_DOUBLE;
	 *nParamCount=4;
	 break;

	 case 1:
	 SO_StringCopy( pInternalName, getText(DFA_MONTHS_NAME) );
	 SO_StringCopy( pFuncName,     "ScDate_GetDiffMonths" );
	 peType[0] = PTR_DOUBLE;
	 peType[1] = PTR_DOUBLE;
	 peType[2] = PTR_DOUBLE;
	 peType[3] = PTR_DOUBLE;
	 *nParamCount=4;
	 break;

	 case 2:
	 SO_StringCopy( pInternalName, getText(DFA_YEARS_NAME) );
	 SO_StringCopy( pFuncName,     "ScDate_GetDiffYears" );
	 peType[0] = PTR_DOUBLE;
	 peType[1] = PTR_DOUBLE;
	 peType[2] = PTR_DOUBLE;
	 peType[3] = PTR_DOUBLE;
	 *nParamCount=4;
	 break;

	 case 3:
	 SO_StringCopy( pInternalName, getText(DFA_ISLEAPYEAR_NAME) );
	 SO_StringCopy( pFuncName,     "ScDate_IsLeapYear" );
	 peType[0] = PTR_DOUBLE;
	 peType[1] = PTR_DOUBLE;
	 *nParamCount=2;
	 break;

	 case 4:
	 SO_StringCopy( pInternalName, getText(DFA_DAYSINMONTH_NAME) );
	 SO_StringCopy( pFuncName,     "ScDate_DaysInMonth" );
	 peType[0] = PTR_DOUBLE;
	 peType[1] = PTR_DOUBLE;
	 *nParamCount=2;
	 break;

	 case 5:
	 SO_StringCopy( pInternalName, getText(DFA_DAYSINYEAR_NAME) );
	 SO_StringCopy( pFuncName,     "ScDate_DaysInYear" );
	 peType[0] = PTR_DOUBLE;
	 peType[1] = PTR_DOUBLE;
	 *nParamCount=2;
	 break;

	 case 6:
	 SO_StringCopy( pInternalName, getText(DFA_WEEKSINYEAR_NAME) );
	 SO_StringCopy( pFuncName,     "ScDate_WeeksInYear" );
	 peType[0] = PTR_DOUBLE;
	 peType[1] = PTR_DOUBLE;
	 *nParamCount=2;
	 break;

	 default:
            *nParamCount    = 0;
            *pFuncName     = 0;
            *pInternalName = 0;
            break;
    }
}

/**
 * Provides descriptions for each new function to StarCalc
 * which are shown is the autopilot
 *
 * @param *nNo Input Parameter, Function number between 0 and nCount - 1
 * @param *nParam Parameter Number
 * @param *pName Output: Name of the parameter
 * @param *pDesc Output: Description of the parameter
 *
 * @see #GetFunctionCount, #GetParameterDescription
 */
void CALLTYPE GetParameterDescription( USHORT* nNo, USHORT* nParam,
char* pName, char* pDesc )
{
	*pName = 0;
	*pDesc = 0;

	switch ( *nNo ) {
	case 0:	/* Weeks */
		switch ( *nParam ) {
		case 0:
			SO_StringCopy(pDesc,getText(DFA_WEEK_DESC));
			break;
		case 1:
			SO_StringCopy(pName,getText(DFA_PAR_DATE1_NAME));
			SO_StringCopy(pDesc,getText(DFA_WEEK_PAR1_DESC));
			break;
		case 2:
			SO_StringCopy(pName,getText(DFA_PAR_DATE2_NAME));
			SO_StringCopy(pDesc,getText(DFA_WEEK_PAR2_DESC));
			break;
		case 3:
			SO_StringCopy(pName,getText(DFA_PAR_MODE_NAME));
			SO_StringCopy(pDesc,getText(DFA_WEEK_PAR3_DESC));
			break;
		}
		break;
	case 1: /* Months */
		switch ( *nParam ) {
		case 0:
			SO_StringCopy(pDesc,getText(DFA_MONTHS_DESC));
			break;
		case 1:
			SO_StringCopy(pName,getText(DFA_PAR_DATE1_NAME));
			SO_StringCopy(pDesc,getText(DFA_MONTHS_PAR1_DESC));
			break;
		case 2:
			SO_StringCopy(pName,getText(DFA_PAR_DATE2_NAME));
			SO_StringCopy(pDesc,getText(DFA_MONTHS_PAR2_DESC));
			break;
		case 3:
			SO_StringCopy(pName,getText(DFA_PAR_MODE_NAME));
			SO_StringCopy(pDesc,getText(DFA_MONTHS_PAR3_DESC));
			break;
		}
		break;
	case 2: /* Years */
		switch ( *nParam ) {
		case 0:
			SO_StringCopy(pDesc,getText(DFA_YEARS_DESC));
			break;
		case 1:
			SO_StringCopy(pName,getText(DFA_PAR_DATE1_NAME));
			SO_StringCopy(pDesc,getText(DFA_YEARS_PAR1_DESC));
			break;
		case 2:
			SO_StringCopy(pName,getText(DFA_PAR_DATE2_NAME));
			SO_StringCopy(pDesc,getText(DFA_YEARS_PAR2_DESC));
			break;
		case 3:
			SO_StringCopy(pName,getText(DFA_PAR_MODE_NAME));
			SO_StringCopy(pDesc,getText(DFA_YEARS_PAR3_DESC));
			break;
		}
	   break;
	case 3:	/* IsLeapYear */
		switch ( *nParam ) {
		case 0:
			SO_StringCopy(pDesc,getText(DFA_ISLEAPYEAR_DESC));
			break;
		case 1:
			SO_StringCopy(pName,getText(DFA_PAR_DATE_NAME));
			SO_StringCopy(pDesc,getText(DFA_PAR_DATE_DESC)); /* StarCalc Value */
			break;
		}
		break;
	case 4:	/* DaysInMonth */
		switch ( *nParam ) {
		case 0:
			SO_StringCopy(pDesc,getText(DFA_DAYSINMONTH_DESC));
			break;
		case 1:
			SO_StringCopy(pName,getText(DFA_PAR_DATE_NAME));
			SO_StringCopy(pDesc,getText(DFA_PAR_DATE_DESC)); /* StarCalc Value */
			break;
		}
		break;
	case 5:	/* DaysInYear */
		switch ( *nParam ) {
		case 0:
			SO_StringCopy(pDesc,getText(DFA_DAYSINYEAR_DESC));
			break;
		case 1:
			SO_StringCopy(pName,getText(DFA_PAR_DATE_NAME));
			SO_StringCopy(pDesc,getText(DFA_PAR_DATE_DESC)); /* StarCalc Value */
			break;
		}
		break;

	case 6:	/* WeeksInYear */
		switch ( *nParam ) {
		case 0:
			SO_StringCopy(pDesc,getText(DFA_WEEKSINYEAR_DESC));
			break;
		case 1:
			SO_StringCopy(pName,getText(DFA_PAR_DATE_NAME));
			SO_StringCopy(pDesc,getText(DFA_PAR_DATE_DESC)); /* StarCalc Value */
			break;
		}
		break;
	}

}

/*------------------------------------------------------------------------

$Log: not supported by cvs2svn $
Revision 1.6  1999/08/10 12:47:12  NN
#68039# copyright

      Rev 1.5   10 Aug 1999 14:47:12   NN
   #68039# copyright

      Rev 1.4   20 May 1999 12:59:40   ER
   #66274# GetFunctionData: DFA_WEEKSINYEAR_NAME statt DFA_DAYSINYEAR_NAME

      Rev 1.3   29 Apr 1999 14:55:50   ER
   #57689# xlang.h statt tools/lang.hxx

      Rev 1.3   29 Apr 1999 14:45:32   ER
   #57689# xlang.hxx statt tools/lang.hxx

      Rev 1.2   29 Apr 1999 12:54:04   ER
   #57689# Internationalisierung

      Rev 1.1   15 Mar 1999 16:27:50   HJS
   aufgeraeumt

      Rev 1.0   10 Mar 1999 12:47:04   BEI
   Initial revision.

      Rev 1.1   13 Jul 1998 18:15:40   BEI
   2 Languages

      Rev 1.0   07 Jul 1998 20:36:22   NN
   Initial revision.

------------------------------------------------------------------------*/