<commit_before>//
// Copyright (C) 2008 SIPfoundry Inc.
// Licensed by SIPfoundry under the LGPL license.
//
// Copyright (C) 2008 SIPez LLC.
// Licensed to SIPfoundry under a Contributor Agreement.
//
// $$
//////////////////////////////////////////////////////////////////////////////

// Author: Alexander Chemeris <Alexander DOT Chemeris AT SIPez DOT com>

// SYSTEM INCLUDES
// APPLICATION INCLUDES
#include "mp/MpBridgeAlgSimple.h"
#include "mp/MpMisc.h"

// EXTERNAL FUNCTIONS
// EXTERNAL VARIABLES
// CONSTANTS
// TYPEDEFS
// DEFINES
// MACROS
// STATIC VARIABLE INITIALIZATIONS

/* //////////////////////////////// PUBLIC //////////////////////////////// */

/* =============================== CREATORS =============================== */

MpBridgeAlgSimple::MpBridgeAlgSimple(int inputs, int outputs,
                                     UtlBoolean mixSilence,
                                     int samplesPerFrame)
: MpBridgeAlgBase(inputs, outputs, mixSilence)
, mpGainMatrix(NULL)
, mpMixAccumulator(NULL)
{
   // Allocate mix matrix.
   mpGainMatrix = new MpBridgeGain[maxInputs()*maxOutputs()];
   assert(mpGainMatrix != NULL);

   // Initially set matrix to inversed unity matrix, with zeros along
   // main diagonal.
   MpBridgeGain *pGain = mpGainMatrix;
   *pGain = MP_BRIDGE_GAIN_MUTED;
   pGain++;
   for (int row=0; row<maxOutputs()-1; row++)
   {
      for (int i=0; i<maxInputs(); i++)
      {
         *pGain = MP_BRIDGE_GAIN_PASSTHROUGH;
         pGain++;
      }
      *pGain = MP_BRIDGE_GAIN_MUTED;
      pGain++;
   }

   // Allocate temporary storage for mixing data.
   mpMixAccumulator = new MpBridgeAccum[samplesPerFrame];
   assert(mpMixAccumulator != NULL);
}

MpBridgeAlgSimple::~MpBridgeAlgSimple()
{
   delete[] mpGainMatrix;
   delete[] mpMixAccumulator;
}

/* ============================= MANIPULATORS ============================= */

UtlBoolean MpBridgeAlgSimple::doMix(MpBufPtr inBufs[], int inBufsSize,
                                    MpBufPtr outBufs[], int outBufsSize,
                                    int samplesPerFrame)
{
   // Initialize amplitudes if they haven't been initialized yet.
   for (int i=0; i<inBufsSize; i++)
   {
      if (mpPrevAmplitudes[i] < 0 && inBufs[i].isValid())
      {
         MpAudioBufPtr pAudioBuf = inBufs[i];
         mpPrevAmplitudes[i] = pAudioBuf->getAmplitude();
      }
   }

#define DEBUG_AGC
#ifdef DEBUG_AGC
   static int debugCounter = 0;
   debugCounter++;
   if (debugCounter % 50 == 0)
   {
      printf("\nInput:  ");
      for (int inputNum=0; inputNum<inBufsSize; inputNum++)
      {
         if (inBufs[inputNum].isValid())
         {
            MpAudioBufPtr pFrame = inBufs[inputNum];
            printf("%d ", pFrame->getAmplitude());
         }
         else
         {
            printf("- ");
         }
      }
      printf("\n");
      printf("Output: ");
   }
#endif

   // Loop over all outputs and mix
   for (int outputNum=0; outputNum<outBufsSize; outputNum++)
   {
      MpBridgeGain *pInputGains = &mpGainMatrix[outputNum*maxInputs()];
      int32_t amplitude = 0;

      // Initialize accumulator
      for (int i=0; i<samplesPerFrame; i++)
      {
         mpMixAccumulator[i] = MPF_BRIDGE_FLOAT(0.0f);
      }

      // Get buffer for output data.
      MpAudioBufPtr pOutBuf = MpMisc.RawAudioPool->getBuffer();
      assert(pOutBuf.isValid());
      pOutBuf->setSamplesNumber(samplesPerFrame);
      pOutBuf->setSpeechType(MP_SPEECH_SILENT);

      // Mix input data to accumulator
      for (int inputNum=0; inputNum<inBufsSize; inputNum++)
      {
         if (inBufs[inputNum].isValid() &&
             pInputGains[inputNum] != MP_BRIDGE_GAIN_MUTED)
         {
            MpAudioBufPtr pFrame = inBufs[inputNum];
            assert(pFrame->getSamplesNumber() == samplesPerFrame);

            // Do not mix muted audio.
            if (pFrame->getSpeechType() == MP_SPEECH_MUTED)
            {
               continue;
            }

            MpAudioSample prevAmplitude = mpPrevAmplitudes[inputNum];
            MpAudioSample curAmplitude = pFrame->getAmplitude();

            if (  pInputGains[inputNum] == MP_BRIDGE_GAIN_PASSTHROUGH
               && prevAmplitude == MpSpeechParams::MAX_AMPLITUDE
               && curAmplitude == MpSpeechParams::MAX_AMPLITUDE)
            {
               MpDspUtils::add_IGain(pFrame->getSamplesPtr(), mpMixAccumulator,
                                     samplesPerFrame, MP_BRIDGE_FRAC_LENGTH);

               // Update output amplitude value.
               amplitude += MpSpeechParams::MAX_AMPLITUDE;
#ifdef DEBUG_AGC
               if (  debugCounter % 50 == 0
                  && (outputNum==0 || outputNum==3) )
               {
                  printf("|P|");
               }
#endif
            }
            else if (curAmplitude == prevAmplitude)
            {
               // Calculate gain taking into account input amplitude.
               MpBridgeGain scaledGain = (MpBridgeGain)
                  ((pInputGains[inputNum]*MAX_AMPLITUDE_ROUND)/curAmplitude);

               MpDspUtils::addMul_I(pFrame->getSamplesPtr(),
                                    scaledGain,
                                    mpMixAccumulator,
                                    samplesPerFrame);

               // Update output amplitude value.
               amplitude += (curAmplitude*scaledGain)>>MP_BRIDGE_FRAC_LENGTH;
#ifdef DEBUG_AGC
               if (  debugCounter % 50 == 0
                  && (outputNum==0 || outputNum==3) )
               {
                  printf("|F:%d|", scaledGain);
               }
#endif
            }
            else
            {
               // Calculate gain start and end taking into account previous
               // and current input amplitudes.
               MpBridgeGain scaledGainStart = (MpBridgeGain)
                  ((pInputGains[inputNum]*MAX_AMPLITUDE_ROUND)/prevAmplitude);
               MpBridgeGain scaledGainEnd = (MpBridgeGain)
                  ((pInputGains[inputNum]*MAX_AMPLITUDE_ROUND)/curAmplitude);

               MpDspUtils::addMulLinear_I(pFrame->getSamplesPtr(),
                                          scaledGainStart,
                                          scaledGainEnd,
                                          mpMixAccumulator,
                                          samplesPerFrame);

               // Update output amplitude value.
               MpBridgeGain scaledGainMax = MpDspUtils::maximum(scaledGainStart, scaledGainEnd);
               amplitude += (curAmplitude*scaledGainMax) >> MP_BRIDGE_FRAC_LENGTH;
#ifdef DEBUG_AGC
               if (  debugCounter % 50 == 0
                  && (outputNum==0 || outputNum==3) )
               {
                  printf("|V:%d:%d|", scaledGainStart, scaledGainEnd);
               }
#endif
            }

            // Update output frame speech type.
            pOutBuf->setSpeechType(mixSpeechTypes(pOutBuf->getSpeechType(),
                                                  pFrame->getSpeechType()));
         }
      }

      // Set amplitude of the output frame.
      pOutBuf->setAmplitude(MPF_SATURATE16(amplitude));

#ifdef DEBUG_AGC
      if (  debugCounter % 50 == 0
         && (outputNum==0 || outputNum==3) )
      {
         printf("%d ", MPF_SATURATE16(amplitude));
      }
#endif

      // Move data from accumulator to output.
      MpDspUtils::convert_Att(mpMixAccumulator, pOutBuf->getSamplesWritePtr(),
                              samplesPerFrame, MP_BRIDGE_FRAC_LENGTH);
      outBufs[outputNum].swap(pOutBuf);
   }

#ifdef DEBUG_AGC
   if (debugCounter % 50 == 0)
   {
      printf("\n");
   }
#endif

   // Save input amplitudes for later use.
   saveAmplitudes(inBufs, inBufsSize);

   return TRUE;
}

void MpBridgeAlgSimple::setGainMatrixValue(int column, int row, MpBridgeGain val)
{
   mpGainMatrix[row*maxInputs() + column] = val;
}

void MpBridgeAlgSimple::setGainMatrixRow(int row, int numValues, const MpBridgeGain val[])
{
   // Copy gain data to mix matrix row.
   MpBridgeGain *pCurGain = &mpGainMatrix[row*maxInputs()];
   for (int i=0; i<numValues; i++)
   {
      if (val[i] != MP_BRIDGE_GAIN_UNDEFINED)
      {
         *pCurGain = val[i];
      }
      pCurGain++;
   }
}

void MpBridgeAlgSimple::setGainMatrixColumn(int column, int numValues, const MpBridgeGain val[])
{
   // Copy gain data to mix matrix column.
   MpBridgeGain *pCurGain = &mpGainMatrix[column];
   for (int i=0; i<numValues; i++)
   {
      if (val[i] != MP_BRIDGE_GAIN_UNDEFINED)
      {
         *pCurGain = val[i];
      }
      pCurGain += maxInputs();
   }
}

/* ============================== ACCESSORS =============================== */

/* =============================== INQUIRY ================================ */

/* ////////////////////////////// PROTECTED /////////////////////////////// */

/* /////////////////////////////// PRIVATE //////////////////////////////// */

/* ============================== FUNCTIONS =============================== */

<commit_msg>Disable chatty debug output in MpBridgeAlgSimple.<commit_after>//
// Copyright (C) 2008 SIPfoundry Inc.
// Licensed by SIPfoundry under the LGPL license.
//
// Copyright (C) 2008 SIPez LLC.
// Licensed to SIPfoundry under a Contributor Agreement.
//
// $$
//////////////////////////////////////////////////////////////////////////////

// Author: Alexander Chemeris <Alexander DOT Chemeris AT SIPez DOT com>

// SYSTEM INCLUDES
// APPLICATION INCLUDES
#include "mp/MpBridgeAlgSimple.h"
#include "mp/MpMisc.h"

// EXTERNAL FUNCTIONS
// EXTERNAL VARIABLES
// CONSTANTS
// TYPEDEFS
// DEFINES
#define DEBUG_AGC
#undef DEBUG_AGC

// MACROS
// STATIC VARIABLE INITIALIZATIONS

/* //////////////////////////////// PUBLIC //////////////////////////////// */

/* =============================== CREATORS =============================== */

MpBridgeAlgSimple::MpBridgeAlgSimple(int inputs, int outputs,
                                     UtlBoolean mixSilence,
                                     int samplesPerFrame)
: MpBridgeAlgBase(inputs, outputs, mixSilence)
, mpGainMatrix(NULL)
, mpMixAccumulator(NULL)
{
   // Allocate mix matrix.
   mpGainMatrix = new MpBridgeGain[maxInputs()*maxOutputs()];
   assert(mpGainMatrix != NULL);

   // Initially set matrix to inversed unity matrix, with zeros along
   // main diagonal.
   MpBridgeGain *pGain = mpGainMatrix;
   *pGain = MP_BRIDGE_GAIN_MUTED;
   pGain++;
   for (int row=0; row<maxOutputs()-1; row++)
   {
      for (int i=0; i<maxInputs(); i++)
      {
         *pGain = MP_BRIDGE_GAIN_PASSTHROUGH;
         pGain++;
      }
      *pGain = MP_BRIDGE_GAIN_MUTED;
      pGain++;
   }

   // Allocate temporary storage for mixing data.
   mpMixAccumulator = new MpBridgeAccum[samplesPerFrame];
   assert(mpMixAccumulator != NULL);
}

MpBridgeAlgSimple::~MpBridgeAlgSimple()
{
   delete[] mpGainMatrix;
   delete[] mpMixAccumulator;
}

/* ============================= MANIPULATORS ============================= */

UtlBoolean MpBridgeAlgSimple::doMix(MpBufPtr inBufs[], int inBufsSize,
                                    MpBufPtr outBufs[], int outBufsSize,
                                    int samplesPerFrame)
{
   // Initialize amplitudes if they haven't been initialized yet.
   for (int i=0; i<inBufsSize; i++)
   {
      if (mpPrevAmplitudes[i] < 0 && inBufs[i].isValid())
      {
         MpAudioBufPtr pAudioBuf = inBufs[i];
         mpPrevAmplitudes[i] = pAudioBuf->getAmplitude();
      }
   }

#ifdef DEBUG_AGC
   static int debugCounter = 0;
   debugCounter++;
   if (debugCounter % 50 == 0)
   {
      printf("\nInput:  ");
      for (int inputNum=0; inputNum<inBufsSize; inputNum++)
      {
         if (inBufs[inputNum].isValid())
         {
            MpAudioBufPtr pFrame = inBufs[inputNum];
            printf("%d ", pFrame->getAmplitude());
         }
         else
         {
            printf("- ");
         }
      }
      printf("\n");
      printf("Output: ");
   }
#endif

   // Loop over all outputs and mix
   for (int outputNum=0; outputNum<outBufsSize; outputNum++)
   {
      MpBridgeGain *pInputGains = &mpGainMatrix[outputNum*maxInputs()];
      int32_t amplitude = 0;

      // Initialize accumulator
      for (int i=0; i<samplesPerFrame; i++)
      {
         mpMixAccumulator[i] = MPF_BRIDGE_FLOAT(0.0f);
      }

      // Get buffer for output data.
      MpAudioBufPtr pOutBuf = MpMisc.RawAudioPool->getBuffer();
      assert(pOutBuf.isValid());
      pOutBuf->setSamplesNumber(samplesPerFrame);
      pOutBuf->setSpeechType(MP_SPEECH_SILENT);

      // Mix input data to accumulator
      for (int inputNum=0; inputNum<inBufsSize; inputNum++)
      {
         if (inBufs[inputNum].isValid() &&
             pInputGains[inputNum] != MP_BRIDGE_GAIN_MUTED)
         {
            MpAudioBufPtr pFrame = inBufs[inputNum];
            assert(pFrame->getSamplesNumber() == samplesPerFrame);

            // Do not mix muted audio.
            if (pFrame->getSpeechType() == MP_SPEECH_MUTED)
            {
               continue;
            }

            MpAudioSample prevAmplitude = mpPrevAmplitudes[inputNum];
            MpAudioSample curAmplitude = pFrame->getAmplitude();

            if (  pInputGains[inputNum] == MP_BRIDGE_GAIN_PASSTHROUGH
               && prevAmplitude == MpSpeechParams::MAX_AMPLITUDE
               && curAmplitude == MpSpeechParams::MAX_AMPLITUDE)
            {
               MpDspUtils::add_IGain(pFrame->getSamplesPtr(), mpMixAccumulator,
                                     samplesPerFrame, MP_BRIDGE_FRAC_LENGTH);

               // Update output amplitude value.
               amplitude += MpSpeechParams::MAX_AMPLITUDE;
#ifdef DEBUG_AGC
               if (  debugCounter % 50 == 0
                  && (outputNum==0 || outputNum==3) )
               {
                  printf("|P|");
               }
#endif
            }
            else if (curAmplitude == prevAmplitude)
            {
               // Calculate gain taking into account input amplitude.
               MpBridgeGain scaledGain = (MpBridgeGain)
                  ((pInputGains[inputNum]*MAX_AMPLITUDE_ROUND)/curAmplitude);

               MpDspUtils::addMul_I(pFrame->getSamplesPtr(),
                                    scaledGain,
                                    mpMixAccumulator,
                                    samplesPerFrame);

               // Update output amplitude value.
               amplitude += (curAmplitude*scaledGain)>>MP_BRIDGE_FRAC_LENGTH;
#ifdef DEBUG_AGC
               if (  debugCounter % 50 == 0
                  && (outputNum==0 || outputNum==3) )
               {
                  printf("|F:%d|", scaledGain);
               }
#endif
            }
            else
            {
               // Calculate gain start and end taking into account previous
               // and current input amplitudes.
               MpBridgeGain scaledGainStart = (MpBridgeGain)
                  ((pInputGains[inputNum]*MAX_AMPLITUDE_ROUND)/prevAmplitude);
               MpBridgeGain scaledGainEnd = (MpBridgeGain)
                  ((pInputGains[inputNum]*MAX_AMPLITUDE_ROUND)/curAmplitude);

               MpDspUtils::addMulLinear_I(pFrame->getSamplesPtr(),
                                          scaledGainStart,
                                          scaledGainEnd,
                                          mpMixAccumulator,
                                          samplesPerFrame);

               // Update output amplitude value.
               MpBridgeGain scaledGainMax = MpDspUtils::maximum(scaledGainStart, scaledGainEnd);
               amplitude += (curAmplitude*scaledGainMax) >> MP_BRIDGE_FRAC_LENGTH;
#ifdef DEBUG_AGC
               if (  debugCounter % 50 == 0
                  && (outputNum==0 || outputNum==3) )
               {
                  printf("|V:%d:%d|", scaledGainStart, scaledGainEnd);
               }
#endif
            }

            // Update output frame speech type.
            pOutBuf->setSpeechType(mixSpeechTypes(pOutBuf->getSpeechType(),
                                                  pFrame->getSpeechType()));
         }
      }

      // Set amplitude of the output frame.
      pOutBuf->setAmplitude(MPF_SATURATE16(amplitude));

#ifdef DEBUG_AGC
      if (  debugCounter % 50 == 0
         && (outputNum==0 || outputNum==3) )
      {
         printf("%d ", MPF_SATURATE16(amplitude));
      }
#endif

      // Move data from accumulator to output.
      MpDspUtils::convert_Att(mpMixAccumulator, pOutBuf->getSamplesWritePtr(),
                              samplesPerFrame, MP_BRIDGE_FRAC_LENGTH);
      outBufs[outputNum].swap(pOutBuf);
   }

#ifdef DEBUG_AGC
   if (debugCounter % 50 == 0)
   {
      printf("\n");
   }
#endif

   // Save input amplitudes for later use.
   saveAmplitudes(inBufs, inBufsSize);

   return TRUE;
}

void MpBridgeAlgSimple::setGainMatrixValue(int column, int row, MpBridgeGain val)
{
   mpGainMatrix[row*maxInputs() + column] = val;
}

void MpBridgeAlgSimple::setGainMatrixRow(int row, int numValues, const MpBridgeGain val[])
{
   // Copy gain data to mix matrix row.
   MpBridgeGain *pCurGain = &mpGainMatrix[row*maxInputs()];
   for (int i=0; i<numValues; i++)
   {
      if (val[i] != MP_BRIDGE_GAIN_UNDEFINED)
      {
         *pCurGain = val[i];
      }
      pCurGain++;
   }
}

void MpBridgeAlgSimple::setGainMatrixColumn(int column, int numValues, const MpBridgeGain val[])
{
   // Copy gain data to mix matrix column.
   MpBridgeGain *pCurGain = &mpGainMatrix[column];
   for (int i=0; i<numValues; i++)
   {
      if (val[i] != MP_BRIDGE_GAIN_UNDEFINED)
      {
         *pCurGain = val[i];
      }
      pCurGain += maxInputs();
   }
}

/* ============================== ACCESSORS =============================== */

/* =============================== INQUIRY ================================ */

/* ////////////////////////////// PROTECTED /////////////////////////////// */

/* /////////////////////////////// PRIVATE //////////////////////////////// */

/* ============================== FUNCTIONS =============================== */

<|endoftext|>


<commit_before>//
// Copyright (C) 2008 SIPfoundry Inc.
// Licensed by SIPfoundry under the LGPL license.
//
// Copyright (C) 2008 SIPez LLC.
// Licensed to SIPfoundry under a Contributor Agreement.
//
// $$
//////////////////////////////////////////////////////////////////////////////

// Author: Alexander Chemeris <Alexander DOT Chemeris AT SIPez DOT com>

// SYSTEM INCLUDES
// APPLICATION INCLUDES
#include "mp/MpBridgeAlgSimple.h"
#include "mp/MpMisc.h"

// EXTERNAL FUNCTIONS
// EXTERNAL VARIABLES
// CONSTANTS
// TYPEDEFS
// DEFINES
#define DEBUG_AGC
#undef DEBUG_AGC

// MACROS
// STATIC VARIABLE INITIALIZATIONS

/* //////////////////////////////// PUBLIC //////////////////////////////// */

/* =============================== CREATORS =============================== */

MpBridgeAlgSimple::MpBridgeAlgSimple(int inputs, int outputs,
                                     UtlBoolean mixSilence,
                                     int samplesPerFrame)
: MpBridgeAlgBase(inputs, outputs, mixSilence)
, mpGainMatrix(NULL)
, mpMixAccumulator(NULL)
{
   // Allocate mix matrix.
   mpGainMatrix = new MpBridgeGain[maxInputs()*maxOutputs()];
   assert(mpGainMatrix != NULL);

   // Initially set matrix to inversed unity matrix, with zeros along
   // main diagonal.
   MpBridgeGain *pGain = mpGainMatrix;
   *pGain = MP_BRIDGE_GAIN_MUTED;
   pGain++;
   for (int row=0; row<maxOutputs()-1; row++)
   {
      for (int i=0; i<maxInputs(); i++)
      {
         *pGain = MP_BRIDGE_GAIN_PASSTHROUGH;
         pGain++;
      }
      *pGain = MP_BRIDGE_GAIN_MUTED;
      pGain++;
   }

   // Allocate temporary storage for mixing data.
   mpMixAccumulator = new MpBridgeAccum[samplesPerFrame];
   assert(mpMixAccumulator != NULL);
}

MpBridgeAlgSimple::~MpBridgeAlgSimple()
{
   delete[] mpGainMatrix;
   delete[] mpMixAccumulator;
}

/* ============================= MANIPULATORS ============================= */

UtlBoolean MpBridgeAlgSimple::doMix(MpBufPtr inBufs[], int inBufsSize,
                                    MpBufPtr outBufs[], int outBufsSize,
                                    int samplesPerFrame)
{
   // Initialize amplitudes if they haven't been initialized yet.
   for (int i=0; i<inBufsSize; i++)
   {
      if (mpPrevAmplitudes[i] < 0 && inBufs[i].isValid())
      {
         MpAudioBufPtr pAudioBuf = inBufs[i];
         MpAudioSample amplitude = pAudioBuf->getAmplitude();
         mpPrevAmplitudes[i] = amplitude == 0 ? 1 : amplitude;
      }
   }

#ifdef DEBUG_AGC
   static int debugCounter = 0;
   bool debugFlag = false;
   debugCounter++;
   if (debugCounter % 50 == 0)
   {
      debugFlag = true;
   }
   if (debugFlag)
   {
      printf("\nInput:  ");
      for (int inputNum=0; inputNum<inBufsSize; inputNum++)
      {
         if (inBufs[inputNum].isValid())
         {
            MpAudioBufPtr pFrame = inBufs[inputNum];
            printf("%d ", pFrame->getAmplitude());
         }
         else
         {
            printf("- ");
         }
      }
      printf("\n");
      printf("Output: ");
   }
#endif

   // Loop over all outputs and mix
   for (int outputNum=0; outputNum<outBufsSize; outputNum++)
   {
      MpBridgeGain *pInputGains = &mpGainMatrix[outputNum*maxInputs()];
      int32_t amplitude = 0;

      // Initialize accumulator
      for (int i=0; i<samplesPerFrame; i++)
      {
         mpMixAccumulator[i] = MPF_BRIDGE_FLOAT(0.0f);
      }

      // Get buffer for output data.
      MpAudioBufPtr pOutBuf = MpMisc.RawAudioPool->getBuffer();
      assert(pOutBuf.isValid());
      pOutBuf->setSamplesNumber(samplesPerFrame);
      pOutBuf->setSpeechType(MP_SPEECH_SILENT);
      pOutBuf->setEnergy(-1);

      // Mix input data to accumulator
      for (int inputNum=0; inputNum<inBufsSize; inputNum++)
      {
         if (inBufs[inputNum].isValid() &&
             pInputGains[inputNum] != MP_BRIDGE_GAIN_MUTED)
         {
            MpAudioBufPtr pFrame = inBufs[inputNum];
            assert(pFrame->getSamplesNumber() == samplesPerFrame);

            // Do not mix muted audio.
            if (pFrame->getSpeechType() == MP_SPEECH_MUTED)
            {
               continue;
            }

            MpAudioSample prevAmplitude = mpPrevAmplitudes[inputNum];
            MpAudioSample curAmplitude = pFrame->getAmplitude();
            if (curAmplitude == 0)
            {
               curAmplitude = 1;
            }

            if (  pInputGains[inputNum] == MP_BRIDGE_GAIN_PASSTHROUGH
               && prevAmplitude == MpSpeechParams::MAX_AMPLITUDE
               && curAmplitude == MpSpeechParams::MAX_AMPLITUDE)
            {
               MpDspUtils::add_IGain(pFrame->getSamplesPtr(), mpMixAccumulator,
                                     samplesPerFrame, MP_BRIDGE_FRAC_LENGTH);

               // Update output amplitude value.
               amplitude += MpSpeechParams::MAX_AMPLITUDE;
#ifdef DEBUG_AGC
               if ( debugFlag && (outputNum==0 || outputNum==3) )
               {
                  printf("P");
               }
#endif
            }
            else if (curAmplitude == prevAmplitude)
            {
               // Calculate gain taking into account input amplitude.
               MpBridgeGain scaledGain = (MpBridgeGain)
                  ((pInputGains[inputNum]*MAX_AMPLITUDE_ROUND)/curAmplitude);

               MpDspUtils::addMul_I(pFrame->getSamplesPtr(),
                                    scaledGain,
                                    mpMixAccumulator,
                                    samplesPerFrame);

               // Update output amplitude value.
               amplitude += (curAmplitude*scaledGain)>>MP_BRIDGE_FRAC_LENGTH;
#ifdef DEBUG_AGC
               if ( debugFlag && (outputNum==0 || outputNum==3) )
               {
                  printf("F %d", scaledGain);
               }
#endif
            }
            else
            {
               // Calculate gain start and end taking into account previous
               // and current input amplitudes.
               MpBridgeGain scaledGainStart = (MpBridgeGain)
                  ((pInputGains[inputNum]*MAX_AMPLITUDE_ROUND)/prevAmplitude);
               MpBridgeGain scaledGainEnd = (MpBridgeGain)
                  ((pInputGains[inputNum]*MAX_AMPLITUDE_ROUND)/curAmplitude);

               MpDspUtils::addMulLinear_I(pFrame->getSamplesPtr(),
                                          scaledGainStart,
                                          scaledGainEnd,
                                          mpMixAccumulator,
                                          samplesPerFrame);

               // Update output amplitude value.
               MpBridgeGain scaledGainMax = MpDspUtils::maximum(scaledGainStart, scaledGainEnd);
               amplitude += (curAmplitude*scaledGainMax) >> MP_BRIDGE_FRAC_LENGTH;
#ifdef DEBUG_AGC
               if ( debugFlag && (outputNum==0 || outputNum==3) )
               {
                  printf("V %d %d", scaledGainStart, scaledGainEnd);
               }
#endif
            }

            // Update output frame speech type.
            pOutBuf->setSpeechType(mixSpeechTypes(pOutBuf->getSpeechType(),
                                                  pFrame->getSpeechType()));
         }
      }

      // Set amplitude and clipping flag of the output frame.
      pOutBuf->setAmplitude(MPF_SATURATE16(amplitude));
      if (amplitude >= MpSpeechParams::MAX_AMPLITUDE)
      {
         pOutBuf->setClipping(TRUE);
      }

#ifdef DEBUG_AGC
      if ( debugFlag && (outputNum==0 || outputNum==3) )
      {
         printf(" * %d ", MPF_SATURATE16(amplitude));
      }
#endif

      // Move data from accumulator to output.
      MpDspUtils::convert_Att(mpMixAccumulator, pOutBuf->getSamplesWritePtr(),
                              samplesPerFrame, MP_BRIDGE_FRAC_LENGTH);
      outBufs[outputNum].swap(pOutBuf);
   }

#ifdef DEBUG_AGC
   if (debugFlag)
   {
      printf("\n");
   }
#endif

   // Save input amplitudes for later use.
   saveAmplitudes(inBufs, inBufsSize);

   return TRUE;
}

void MpBridgeAlgSimple::setGainMatrixValue(int column, int row, MpBridgeGain val)
{
   mpGainMatrix[row*maxInputs() + column] = val;
}

void MpBridgeAlgSimple::setGainMatrixRow(int row, int numValues, const MpBridgeGain val[])
{
   // Copy gain data to mix matrix row.
   MpBridgeGain *pCurGain = &mpGainMatrix[row*maxInputs()];
   for (int i=0; i<numValues; i++)
   {
      if (val[i] != MP_BRIDGE_GAIN_UNDEFINED)
      {
         *pCurGain = val[i];
      }
      pCurGain++;
   }
}

void MpBridgeAlgSimple::setGainMatrixColumn(int column, int numValues, const MpBridgeGain val[])
{
   // Copy gain data to mix matrix column.
   MpBridgeGain *pCurGain = &mpGainMatrix[column];
   for (int i=0; i<numValues; i++)
   {
      if (val[i] != MP_BRIDGE_GAIN_UNDEFINED)
      {
         *pCurGain = val[i];
      }
      pCurGain += maxInputs();
   }
}

/* ============================== ACCESSORS =============================== */

/* =============================== INQUIRY ================================ */

/* ////////////////////////////// PROTECTED /////////////////////////////// */

/* /////////////////////////////// PRIVATE //////////////////////////////// */

/* ============================== FUNCTIONS =============================== */

<commit_msg>Fixed compiler warning in MpBridgeAlgSimple.cpp<commit_after>//
// Copyright (C) 2008-2017 SIPez LLC. All rights reserved.
//
// Copyright (C) 2008 SIPfoundry Inc.
// Licensed by SIPfoundry under the LGPL license.
//
// $$
//////////////////////////////////////////////////////////////////////////////

// Author: Alexander Chemeris <Alexander DOT Chemeris AT SIPez DOT com>

// SYSTEM INCLUDES
// APPLICATION INCLUDES
#include "mp/MpBridgeAlgSimple.h"
#include "mp/MpMisc.h"

// EXTERNAL FUNCTIONS
// EXTERNAL VARIABLES
// CONSTANTS
// TYPEDEFS
// DEFINES
#define DEBUG_AGC
#undef DEBUG_AGC

// MACROS
// STATIC VARIABLE INITIALIZATIONS

/* //////////////////////////////// PUBLIC //////////////////////////////// */

/* =============================== CREATORS =============================== */

MpBridgeAlgSimple::MpBridgeAlgSimple(int inputs, int outputs,
                                     UtlBoolean mixSilence,
                                     int samplesPerFrame)
: MpBridgeAlgBase(inputs, outputs, mixSilence)
, mpGainMatrix(NULL)
, mpMixAccumulator(NULL)
{
   // Allocate mix matrix.
   mpGainMatrix = new MpBridgeGain[maxInputs()*maxOutputs()];
   assert(mpGainMatrix != NULL);

   // Initially set matrix to inversed unity matrix, with zeros along
   // main diagonal.
   MpBridgeGain *pGain = mpGainMatrix;
   *pGain = MP_BRIDGE_GAIN_MUTED;
   pGain++;
   for (int row=0; row<maxOutputs()-1; row++)
   {
      for (int i=0; i<maxInputs(); i++)
      {
         *pGain = MP_BRIDGE_GAIN_PASSTHROUGH;
         pGain++;
      }
      *pGain = MP_BRIDGE_GAIN_MUTED;
      pGain++;
   }

   // Allocate temporary storage for mixing data.
   mpMixAccumulator = new MpBridgeAccum[samplesPerFrame];
   assert(mpMixAccumulator != NULL);
}

MpBridgeAlgSimple::~MpBridgeAlgSimple()
{
   delete[] mpGainMatrix;
   delete[] mpMixAccumulator;
}

/* ============================= MANIPULATORS ============================= */

UtlBoolean MpBridgeAlgSimple::doMix(MpBufPtr inBufs[], int inBufsSize,
                                    MpBufPtr outBufs[], int outBufsSize,
                                    int samplesPerFrame)
{
   // Initialize amplitudes if they haven't been initialized yet.
   for (int i=0; i<inBufsSize; i++)
   {
      if (mpPrevAmplitudes[i] < 0 && inBufs[i].isValid())
      {
         MpAudioBufPtr pAudioBuf = inBufs[i];
         MpAudioSample amplitude = pAudioBuf->getAmplitude();
         mpPrevAmplitudes[i] = amplitude == 0 ? 1 : amplitude;
      }
   }

#ifdef DEBUG_AGC
   static int debugCounter = 0;
   bool debugFlag = false;
   debugCounter++;
   if (debugCounter % 50 == 0)
   {
      debugFlag = true;
   }
   if (debugFlag)
   {
      printf("\nInput:  ");
      for (int inputNum=0; inputNum<inBufsSize; inputNum++)
      {
         if (inBufs[inputNum].isValid())
         {
            MpAudioBufPtr pFrame = inBufs[inputNum];
            printf("%d ", pFrame->getAmplitude());
         }
         else
         {
            printf("- ");
         }
      }
      printf("\n");
      printf("Output: ");
   }
#endif

   // Loop over all outputs and mix
   for (int outputNum=0; outputNum<outBufsSize; outputNum++)
   {
      MpBridgeGain *pInputGains = &mpGainMatrix[outputNum*maxInputs()];
      int32_t amplitude = 0;

      // Initialize accumulator
      for (int i=0; i<samplesPerFrame; i++)
      {
         mpMixAccumulator[i] = MPF_BRIDGE_FLOAT(0.0f);
      }

      // Get buffer for output data.
      MpAudioBufPtr pOutBuf = MpMisc.RawAudioPool->getBuffer();
      assert(pOutBuf.isValid());
      pOutBuf->setSamplesNumber(samplesPerFrame);
      pOutBuf->setSpeechType(MP_SPEECH_SILENT);
      pOutBuf->setEnergy(-1);

      // Mix input data to accumulator
      for (int inputNum=0; inputNum<inBufsSize; inputNum++)
      {
         if (inBufs[inputNum].isValid() &&
             pInputGains[inputNum] != MP_BRIDGE_GAIN_MUTED)
         {
            MpAudioBufPtr pFrame = inBufs[inputNum];
            assert((int)(pFrame->getSamplesNumber()) == samplesPerFrame);

            // Do not mix muted audio.
            if (pFrame->getSpeechType() == MP_SPEECH_MUTED)
            {
               continue;
            }

            MpAudioSample prevAmplitude = mpPrevAmplitudes[inputNum];
            MpAudioSample curAmplitude = pFrame->getAmplitude();
            if (curAmplitude == 0)
            {
               curAmplitude = 1;
            }

            if (  pInputGains[inputNum] == MP_BRIDGE_GAIN_PASSTHROUGH
               && prevAmplitude == MpSpeechParams::MAX_AMPLITUDE
               && curAmplitude == MpSpeechParams::MAX_AMPLITUDE)
            {
               MpDspUtils::add_IGain(pFrame->getSamplesPtr(), mpMixAccumulator,
                                     samplesPerFrame, MP_BRIDGE_FRAC_LENGTH);

               // Update output amplitude value.
               amplitude += MpSpeechParams::MAX_AMPLITUDE;
#ifdef DEBUG_AGC
               if ( debugFlag && (outputNum==0 || outputNum==3) )
               {
                  printf("P");
               }
#endif
            }
            else if (curAmplitude == prevAmplitude)
            {
               // Calculate gain taking into account input amplitude.
               MpBridgeGain scaledGain = (MpBridgeGain)
                  ((pInputGains[inputNum]*MAX_AMPLITUDE_ROUND)/curAmplitude);

               MpDspUtils::addMul_I(pFrame->getSamplesPtr(),
                                    scaledGain,
                                    mpMixAccumulator,
                                    samplesPerFrame);

               // Update output amplitude value.
               amplitude += (curAmplitude*scaledGain)>>MP_BRIDGE_FRAC_LENGTH;
#ifdef DEBUG_AGC
               if ( debugFlag && (outputNum==0 || outputNum==3) )
               {
                  printf("F %d", scaledGain);
               }
#endif
            }
            else
            {
               // Calculate gain start and end taking into account previous
               // and current input amplitudes.
               MpBridgeGain scaledGainStart = (MpBridgeGain)
                  ((pInputGains[inputNum]*MAX_AMPLITUDE_ROUND)/prevAmplitude);
               MpBridgeGain scaledGainEnd = (MpBridgeGain)
                  ((pInputGains[inputNum]*MAX_AMPLITUDE_ROUND)/curAmplitude);

               MpDspUtils::addMulLinear_I(pFrame->getSamplesPtr(),
                                          scaledGainStart,
                                          scaledGainEnd,
                                          mpMixAccumulator,
                                          samplesPerFrame);

               // Update output amplitude value.
               MpBridgeGain scaledGainMax = MpDspUtils::maximum(scaledGainStart, scaledGainEnd);
               amplitude += (curAmplitude*scaledGainMax) >> MP_BRIDGE_FRAC_LENGTH;
#ifdef DEBUG_AGC
               if ( debugFlag && (outputNum==0 || outputNum==3) )
               {
                  printf("V %d %d", scaledGainStart, scaledGainEnd);
               }
#endif
            }

            // Update output frame speech type.
            pOutBuf->setSpeechType(mixSpeechTypes(pOutBuf->getSpeechType(),
                                                  pFrame->getSpeechType()));
         }
      }

      // Set amplitude and clipping flag of the output frame.
      pOutBuf->setAmplitude(MPF_SATURATE16(amplitude));
      if (amplitude >= MpSpeechParams::MAX_AMPLITUDE)
      {
         pOutBuf->setClipping(TRUE);
      }

#ifdef DEBUG_AGC
      if ( debugFlag && (outputNum==0 || outputNum==3) )
      {
         printf(" * %d ", MPF_SATURATE16(amplitude));
      }
#endif

      // Move data from accumulator to output.
      MpDspUtils::convert_Att(mpMixAccumulator, pOutBuf->getSamplesWritePtr(),
                              samplesPerFrame, MP_BRIDGE_FRAC_LENGTH);
      outBufs[outputNum].swap(pOutBuf);
   }

#ifdef DEBUG_AGC
   if (debugFlag)
   {
      printf("\n");
   }
#endif

   // Save input amplitudes for later use.
   saveAmplitudes(inBufs, inBufsSize);

   return TRUE;
}

void MpBridgeAlgSimple::setGainMatrixValue(int column, int row, MpBridgeGain val)
{
   mpGainMatrix[row*maxInputs() + column] = val;
}

void MpBridgeAlgSimple::setGainMatrixRow(int row, int numValues, const MpBridgeGain val[])
{
   // Copy gain data to mix matrix row.
   MpBridgeGain *pCurGain = &mpGainMatrix[row*maxInputs()];
   for (int i=0; i<numValues; i++)
   {
      if (val[i] != MP_BRIDGE_GAIN_UNDEFINED)
      {
         *pCurGain = val[i];
      }
      pCurGain++;
   }
}

void MpBridgeAlgSimple::setGainMatrixColumn(int column, int numValues, const MpBridgeGain val[])
{
   // Copy gain data to mix matrix column.
   MpBridgeGain *pCurGain = &mpGainMatrix[column];
   for (int i=0; i<numValues; i++)
   {
      if (val[i] != MP_BRIDGE_GAIN_UNDEFINED)
      {
         *pCurGain = val[i];
      }
      pCurGain += maxInputs();
   }
}

/* ============================== ACCESSORS =============================== */

/* =============================== INQUIRY ================================ */

/* ////////////////////////////// PROTECTED /////////////////////////////// */

/* /////////////////////////////// PRIVATE //////////////////////////////// */

/* ============================== FUNCTIONS =============================== */

<|endoftext|>


<commit_before>#ifndef DUNE_STUFF_FUNCTION_EXPRESSION_HH
#define DUNE_STUFF_FUNCTION_EXPRESSION_HH

#ifdef HAVE_CMAKE_CONFIG
  #include "cmake_config.h"
#else
  #include "config.h"
#endif // ifdef HAVE_CMAKE_CONFIG

#include <sstream>
#include <vector>

#include <dune/common/fvector.hh>
#include <dune/common/dynvector.hh>
#include <dune/common/exceptions.hh>
#include <dune/common/static_assert.hh>

#if HAVE_DUNE_FEM
  #include <dune/fem/function/common/function.hh>
  #include <dune/fem/space/common/functionspace.hh>
#endif

#include <dune/stuff/common/parameter/tree.hh>
#include <dune/stuff/common/string.hh>
#include <dune/stuff/common/color.hh>

#include "expression/base.hh"
#include "interface.hh"

namespace Dune {
namespace Stuff {

template< class DomainFieldImp, int domainDim,
          class RangeFieldImp, int rangeDim >
class FunctionExpression
  : public FunctionExpressionBase< DomainFieldImp, domainDim, RangeFieldImp, rangeDim >
#if HAVE_DUNE_FEM
  , public Dune::Fem::Function< Dune::FunctionSpace< DomainFieldImp, RangeFieldImp, domainDim, rangeDim >,
                                FunctionExpression< DomainFieldImp, domainDim, RangeFieldImp, rangeDim > >
#endif
  , public FunctionInterface< DomainFieldImp, domainDim, RangeFieldImp, rangeDim >
{
public:
  typedef FunctionExpression<  DomainFieldImp, domainDim, RangeFieldImp, rangeDim >     ThisType;
  typedef FunctionExpressionBase<  DomainFieldImp, domainDim, RangeFieldImp, rangeDim > BaseType;
  typedef FunctionInterface<  DomainFieldImp, domainDim, RangeFieldImp, rangeDim >      InterfaceType;

  using typename InterfaceType::DomainFieldType;
  using InterfaceType::dimDomain;
  typedef typename InterfaceType::DomainType  DomainType;
  using typename InterfaceType::RangeFieldType;
  using InterfaceType::dimRange;
  typedef typename InterfaceType::RangeType   RangeType;

  static const std::string id()
  {
    return InterfaceType::id() + ".expression";
  }

  FunctionExpression(const std::string _variable,
                     const std::string _expression,
                     const int _order = -1,
                     const std::string _name = id())
    : BaseType(_variable, _expression)
    , order_(_order)
    , name_(_name)
  {}

  FunctionExpression(const std::string _variable,
                     const std::vector< std::string > _expressions,
                     const int _order = -1,
                     const std::string _name = id())
    : BaseType(_variable, _expressions)
    , order_(_order)
    , name_(_name)
  {}

  FunctionExpression(const ThisType& other)
    : BaseType(other)
    , order_(other.order())
    , name_(other.name())
  {}

  ThisType& operator=(const ThisType& other)
  {
    if (this != &other) {
      BaseType::operator=(other);
      order_ = other.order();
      name_ = other.name();
    }
    return this;
  } // ThisType& operator=(const ThisType& other)

  static Dune::ParameterTree createSampleDescription(const std::string subName = "")
  {
    Dune::ParameterTree description;
    description["variable"] = "x";
    description["expression"] = "[x[0]; sin(x[0])]";
    description["order"] = "1";
    description["name"] = "function.expression";
    if (subName.empty())
      return description;
    else {
      Dune::Stuff::Common::ExtendedParameterTree extendedDescription;
      extendedDescription.add(description, subName);
      return extendedDescription;
    }
  } // ... createSampleDescription(...)

  static ThisType* create(const DSC::ExtendedParameterTree description)
  {
    // get necessary
    const std::string _variable = description.get< std::string >("variable", "x");
    std::vector< std::string > _expressions;
    // lets see, if there is a key or vector
    if (description.hasVector("expression")) {
      const std::vector< std::string > expr = description.getVector< std::string >("expression", 1);
      for (size_t ii = 0; ii < expr.size(); ++ii)
        _expressions.push_back(expr[ii]);
    } else if (description.hasKey("expression")) {
      const std::string expr = description.get< std::string >("expression");
      _expressions.push_back(expr);
    } else
      DUNE_THROW(Dune::IOError,
                 "\n" << Dune::Stuff::Common::colorStringRed("ERROR:")
                 << " neither key nor vector 'expression' found in the following description:\n"
                 << description.reportString("  "));
    // get optional
    const int _order = description.get< int >("order", -1);
    const std::string _name = description.get< std::string >("name", "function.expression");
    // create and return
    return new ThisType(_variable, _expressions, _order, _name);
  } // ... create(...)

  virtual int order() const
  {
    return order_;
  }

  virtual std::string name() const
  {
    return name_;
  }

  virtual void evaluate(const DomainType& _x, RangeType& _ret) const
  {
    BaseType::evaluate(_x, _ret);
  }

private:
  int order_;
  std::string name_;
}; // class FunctionExpression

} // namespace Stuff
} // namespace Dune

#endif // DUNE_STUFF_FUNCTION_EXPRESSION_HH
<commit_msg>[function.expression] fixed diamond<commit_after>#ifndef DUNE_STUFF_FUNCTION_EXPRESSION_HH
#define DUNE_STUFF_FUNCTION_EXPRESSION_HH

#ifdef HAVE_CMAKE_CONFIG
  #include "cmake_config.h"
#else
  #include "config.h"
#endif // ifdef HAVE_CMAKE_CONFIG

#include <sstream>
#include <vector>

#include <dune/common/fvector.hh>
#include <dune/common/dynvector.hh>
#include <dune/common/exceptions.hh>
#include <dune/common/static_assert.hh>

#if HAVE_DUNE_FEM
  #include <dune/fem/function/common/function.hh>
  #include <dune/fem/space/common/functionspace.hh>
#endif

#include <dune/stuff/common/parameter/tree.hh>
#include <dune/stuff/common/string.hh>
#include <dune/stuff/common/color.hh>

#include "expression/base.hh"
#include "interface.hh"

namespace Dune {
namespace Stuff {

template< class DomainFieldImp, int domainDim,
          class RangeFieldImp, int rangeDim >
class FunctionExpression
  : public FunctionExpressionBase< DomainFieldImp, domainDim, RangeFieldImp, rangeDim >
#if HAVE_DUNE_FEM
  , public Dune::Fem::Function< Dune::FunctionSpace< DomainFieldImp, RangeFieldImp, domainDim, rangeDim >,
                                FunctionExpression< DomainFieldImp, domainDim, RangeFieldImp, rangeDim > >
#endif
  , public FunctionInterface< DomainFieldImp, domainDim, RangeFieldImp, rangeDim >
{
public:
  typedef FunctionExpression<  DomainFieldImp, domainDim, RangeFieldImp, rangeDim >     ThisType;
  typedef FunctionExpressionBase<  DomainFieldImp, domainDim, RangeFieldImp, rangeDim > BaseType;
  typedef FunctionInterface<  DomainFieldImp, domainDim, RangeFieldImp, rangeDim >      InterfaceType;

  typedef typename InterfaceType::DomainFieldType DomainFieldType;
  static const int                                dimDomain = InterfaceType::dimDomain;
  typedef typename InterfaceType::DomainType      DomainType;
  typedef typename InterfaceType::RangeFieldType  RangeFieldType;
  static const int                                dimRange = InterfaceType::dimRange;
  typedef typename InterfaceType::RangeType       RangeType;

  static const std::string id()
  {
    return InterfaceType::id() + ".expression";
  }

  FunctionExpression(const std::string _variable,
                     const std::string _expression,
                     const int _order = -1,
                     const std::string _name = id())
    : BaseType(_variable, _expression)
    , order_(_order)
    , name_(_name)
  {}

  FunctionExpression(const std::string _variable,
                     const std::vector< std::string > _expressions,
                     const int _order = -1,
                     const std::string _name = id())
    : BaseType(_variable, _expressions)
    , order_(_order)
    , name_(_name)
  {}

  FunctionExpression(const ThisType& other)
    : BaseType(other)
    , order_(other.order())
    , name_(other.name())
  {}

  ThisType& operator=(const ThisType& other)
  {
    if (this != &other) {
      BaseType::operator=(other);
      order_ = other.order();
      name_ = other.name();
    }
    return this;
  } // ThisType& operator=(const ThisType& other)

  static Dune::ParameterTree createSampleDescription(const std::string subName = "")
  {
    Dune::ParameterTree description;
    description["variable"] = "x";
    description["expression"] = "[x[0]; sin(x[0])]";
    description["order"] = "1";
    description["name"] = "function.expression";
    if (subName.empty())
      return description;
    else {
      Dune::Stuff::Common::ExtendedParameterTree extendedDescription;
      extendedDescription.add(description, subName);
      return extendedDescription;
    }
  } // ... createSampleDescription(...)

  static ThisType* create(const DSC::ExtendedParameterTree description)
  {
    // get necessary
    const std::string _variable = description.get< std::string >("variable", "x");
    std::vector< std::string > _expressions;
    // lets see, if there is a key or vector
    if (description.hasVector("expression")) {
      const std::vector< std::string > expr = description.getVector< std::string >("expression", 1);
      for (size_t ii = 0; ii < expr.size(); ++ii)
        _expressions.push_back(expr[ii]);
    } else if (description.hasKey("expression")) {
      const std::string expr = description.get< std::string >("expression");
      _expressions.push_back(expr);
    } else
      DUNE_THROW(Dune::IOError,
                 "\n" << Dune::Stuff::Common::colorStringRed("ERROR:")
                 << " neither key nor vector 'expression' found in the following description:\n"
                 << description.reportString("  "));
    // get optional
    const int _order = description.get< int >("order", -1);
    const std::string _name = description.get< std::string >("name", "function.expression");
    // create and return
    return new ThisType(_variable, _expressions, _order, _name);
  } // ... create(...)

  virtual int order() const
  {
    return order_;
  }

  virtual std::string name() const
  {
    return name_;
  }

  virtual void evaluate(const DomainType& _x, RangeType& _ret) const
  {
    BaseType::evaluate(_x, _ret);
  }

private:
  int order_;
  std::string name_;
}; // class FunctionExpression

} // namespace Stuff
} // namespace Dune

#endif // DUNE_STUFF_FUNCTION_EXPRESSION_HH
<|endoftext|>


<commit_before>/*
 * Distributed under the OSI-approved Apache License, Version 2.0.  See
 * accompanying file Copyright.txt for details.
 *
 * HDF5WriterP.cpp
 *
 *  Created on: March 20, 2017
 *      Author: Junmin
 */

#include "HDF5WriterP.h"

#include "adios2/ADIOSMPI.h"
#include "adios2/helper/adiosFunctions.h" //CSVToVector

namespace adios2
{

HDF5WriterP::HDF5WriterP(IO &io, const std::string &name, const Mode mode,
                         MPI_Comm mpiComm)
: Engine("HDF5Writer", io, name, mode, mpiComm), m_H5File(io.m_DebugMode)
{
    m_EndMessage = ", in call to IO HDF5Writer Open " + m_Name + "\n";
    Init();
}

HDF5WriterP::~HDF5WriterP() { DoClose(); }

StepStatus HDF5WriterP::BeginStep(StepMode mode, const float timeoutSeconds)
{
    return StepStatus::OK;
}

void HDF5WriterP::EndStep() { m_H5File.Advance(); }

// PRIVATE
void HDF5WriterP::Init()
{
    if (m_OpenMode != Mode::Write && m_OpenMode != Mode::Append)
    {
        throw std::invalid_argument(
            "ERROR: HDF5Writer only support OpenMode::Write or "
            "OpenMode::Append "
            ", in call to ADIOS Open or HDF5Writer constructor\n");
    }

#ifdef NEVER
    m_H5File.Init(m_Name, m_MPIComm, true);
#else
    // enforce .h5 ending
    std::string suffix = ".h5";
    std::string wrongSuffix = ".bp";

    int ss = m_Name.size();
    int wpos = m_Name.find(wrongSuffix);

    if (wpos == ss - wrongSuffix.size())
    {
        // is a file with .bp ending
        std::string updatedName = m_Name.substr(0, wpos) + suffix;
        m_H5File.Init(updatedName, m_MPIComm, true);
    }
    else
    {
        m_H5File.Init(m_Name, m_MPIComm, true);
    }

#endif
}

#define declare_type(T)                                                        \
    void HDF5WriterP::DoPutSync(Variable<T> &variable, const T *values)        \
    {                                                                          \
        DoPutSyncCommon(variable, values);                                     \
    }                                                                          \
    void HDF5WriterP::DoPutDeferred(Variable<T> &variable, const T *values)    \
    {                                                                          \
        DoPutSyncCommon(variable, values);                                     \
    }
ADIOS2_FOREACH_TYPE_1ARG(declare_type)
#undef declare_type

template <class T>
void HDF5WriterP::DoPutSyncCommon(Variable<T> &variable, const T *values)
{

    bool isOrderC = IsRowMajor(m_IO.m_HostLanguage);

    if (!isOrderC)
    {
        int ndims = std::max(variable.m_Shape.size(), variable.m_Count.size());

        if (ndims > 1)
        {
            Dims c_shape(ndims), c_offset(ndims), c_count(ndims);
            for (int i = 0; i < ndims; i++)
            {
                c_shape[i] = variable.m_Shape[ndims - i - 1];
                c_offset[i] = variable.m_Start[ndims - i - 1];
                c_count[i] = variable.m_Count[ndims - i - 1];
            }

            Variable<T> dup =
                Variable<T>(variable.m_Name, c_shape, c_offset, c_count,
                            variable.m_ConstantDims, NULL, false);

            /*
             * duplicate var attributes and convert to c order before saving.
             */
            dup.SetData(values);
            m_H5File.Write(dup, values);
            return;
        }
    }
    variable.SetData(values);
    m_H5File.Write(variable, values);
}

void HDF5WriterP::DoClose(const int transportIndex) { m_H5File.Close(); }

} // end namespace adios2
<commit_msg>updated<commit_after>/*
 * Distributed under the OSI-approved Apache License, Version 2.0.  See
 * accompanying file Copyright.txt for details.
 *
 * HDF5WriterP.cpp
 *
 *  Created on: March 20, 2017
 *      Author: Junmin
 */

#include "HDF5WriterP.h"

#include "adios2/ADIOSMPI.h"
#include "adios2/helper/adiosFunctions.h" //CSVToVector

namespace adios2
{

HDF5WriterP::HDF5WriterP(IO &io, const std::string &name, const Mode mode,
                         MPI_Comm mpiComm)
: Engine("HDF5Writer", io, name, mode, mpiComm), m_H5File(io.m_DebugMode)
{
    m_EndMessage = ", in call to IO HDF5Writer Open " + m_Name + "\n";
    Init();
}

HDF5WriterP::~HDF5WriterP() { DoClose(); }

StepStatus HDF5WriterP::BeginStep(StepMode mode, const float timeoutSeconds)
{
    return StepStatus::OK;
}

void HDF5WriterP::EndStep() { m_H5File.Advance(); }

// PRIVATE
void HDF5WriterP::Init()
{
    if (m_OpenMode != Mode::Write && m_OpenMode != Mode::Append)
    {
        throw std::invalid_argument(
            "ERROR: HDF5Writer only support OpenMode::Write or "
            "OpenMode::Append "
            ", in call to ADIOS Open or HDF5Writer constructor\n");
    }

#ifdef NEVER
    m_H5File.Init(m_Name, m_MPIComm, true);
#else
    // enforce .h5 ending
    std::string suffix = ".h5";
    std::string wrongSuffix = ".bp";

    int ss = m_Name.size();
    int wpos = m_Name.find(wrongSuffix);

    if (wpos == ss - wrongSuffix.size())
    {
        // is a file with .bp ending
        std::string updatedName = m_Name.substr(0, wpos) + suffix;
        m_H5File.Init(updatedName, m_MPIComm, true);
    }
    else
    {
        m_H5File.Init(m_Name, m_MPIComm, true);
    }

#endif
}

#define declare_type(T)                                                        \
    void HDF5WriterP::DoPutSync(Variable<T> &variable, const T *values)        \
    {                                                                          \
        DoPutSyncCommon(variable, values);                                     \
    }                                                                          \
    void HDF5WriterP::DoPutDeferred(Variable<T> &variable, const T *values)    \
    {                                                                          \
        DoPutSyncCommon(variable, values);                                     \
    }
ADIOS2_FOREACH_TYPE_1ARG(declare_type)
#undef declare_type

template <class T>
void HDF5WriterP::DoPutSyncCommon(Variable<T> &variable, const T *values)
{

    bool isOrderC = IsRowMajor(m_IO.m_HostLanguage);

    if (!isOrderC)
    {
        int ndims = std::max(variable.m_Shape.size(), variable.m_Count.size());

        if (ndims > 1)
        {
            Dims c_shape(ndims), c_offset(ndims), c_count(ndims);
            for (int i = 0; i < ndims; i++)
            {
                c_shape[i] = variable.m_Shape[ndims - i - 1];
                c_offset[i] = variable.m_Start[ndims - i - 1];
                c_count[i] = variable.m_Count[ndims - i - 1];
            }

            Variable<T> dup =
                Variable<T>(variable.m_Name, c_shape, c_offset, c_count,
                            variable.IsConstantDims(), NULL, false);

            /*
             * duplicate var attributes and convert to c order before saving.
             */
            dup.SetData(values);
            m_H5File.Write(dup, values);
            return;
        }
    }
    variable.SetData(values);
    m_H5File.Write(variable, values);
}

void HDF5WriterP::DoClose(const int transportIndex) { m_H5File.Close(); }

} // end namespace adios2
<|endoftext|>


<commit_before>/***********************************************************************************************************************
**
** Copyright (c) 2011, 2015 ETH Zurich
** All rights reserved.
**
** Redistribution and use in source and binary forms, with or without modification, are permitted provided that the
** following conditions are met:
**
**    * Redistributions of source code must retain the above copyright notice, this list of conditions and the following
**      disclaimer.
**    * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the
**      following disclaimer in the documentation and/or other materials provided with the distribution.
**    * Neither the name of the ETH Zurich nor the names of its contributors may be used to endorse or promote products
**      derived from this software without specific prior written permission.
**
**
** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
** INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
** DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
** SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
** WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
**
***********************************************************************************************************************/

#include "EnvisionAstConsumer.h"

#include "APIData.h"
#include "EnvisionPPCallbacks.h"
#include "TypeUtilities.h"

EnvisionAstConsumer::EnvisionAstConsumer(clang::CompilerInstance& ci, QString currentFile)
	: compilerInstance_{ci}
{
	currentFile.replace(".cpp", ".h");
	currentFile_ = currentFile.toStdString();
	currentClassName_ = currentFile.split(QDir::separator()).last();
	currentClassName_ = currentClassName_.split(".").first();
}

void EnvisionAstConsumer::Initialize(clang::ASTContext&)
{
	compilerInstance_.getPreprocessor().addPPCallbacks(
				std::make_unique<EnvisionPPCallbacks>(compilerInstance_.getSourceManager(), currentFile_, attributes_));
	compilerInstance_.getPreprocessor().enableIncrementalProcessing();
}

void EnvisionAstConsumer::HandleTagDeclDefinition(clang::TagDecl* tagDecl)
{
	if (auto classDecl = llvm::dyn_cast<clang::CXXRecordDecl>(tagDecl))
		HandleClassDecl(classDecl);
	else if (auto enumDecl = llvm::dyn_cast<clang::EnumDecl>(tagDecl))
		HandleEnumDecl(enumDecl);
	else
		Q_ASSERT(false); // There should not be any other decl here, check clang documentation.
}

void EnvisionAstConsumer::HandleEnumDecl(clang::EnumDecl* enumDecl)
{
	// Only consider enums that are in the current class:
	auto classContext = llvm::dyn_cast<clang::RecordDecl>(enumDecl->getDeclContext());
	if (!classContext ||
		 QString::fromStdString(classContext->getNameAsString()) != currentClassName_)
		return;
	// Ignore private enums:
	if (enumDecl->getAccess() == clang::AccessSpecifier::AS_private) return;

	QString enumName = QString::fromStdString(enumDecl->getNameAsString());
	QString qualifiedName = QString::fromStdString(enumDecl->getQualifiedNameAsString());;
	EnumData eData{enumName, qualifiedName};

	for (auto enumConstant : enumDecl->enumerators())
	{
		QString constantName = QString::fromStdString(enumConstant->getNameAsString());
		eData.values_.append({constantName, QString("%1::%2").arg(eData.qualifiedName_, constantName)});
	}

	processedEnums_ << eData;
}

void EnvisionAstConsumer::HandleClassDecl(clang::CXXRecordDecl* classDecl)
{
	auto context = classDecl->getDeclContext();
	if (context->isNamespace())
	{
		auto namespaceDecl = llvm::dyn_cast<clang::NamespaceDecl>(context);
		auto namespaceName = QString::fromStdString(namespaceDecl->getNameAsString());
		auto className = QString::fromStdString(classDecl->getNameAsString());
		if (namespaceName == APIData::instance().namespaceName_ && className == currentClassName_)
		{
			QStringList bases = baseClasses(classDecl);
			if (!allowedBases_.contains(bases[0])) return; // We only consider classes which have a base that we allow.

			auto cData = buildClassInfo(classDecl);
			auto typedListIt = std::find_if(classDecl->bases_begin(), classDecl->bases_end(),
				[](const clang::CXXBaseSpecifier& base)
				{
					auto baseName = TypeUtilities::typePtrToString(base.getType().getTypePtr());
					return baseName.contains("TypedList");
				});
			if (typedListIt != classDecl->bases_end())
			{
				// Parse TypedList base class as well, this is usually in the form Reclect<TypedList<T>>
				// Therefore we extract the bases first template parameter and assume that it is the TypedList.
				auto baseDecl = typedListIt->getType().getTypePtr()->getAsCXXRecordDecl();
				if (auto templateClass = llvm::dyn_cast<clang::ClassTemplateSpecializationDecl>(baseDecl))
				{
					auto typedListDecl = templateClass->getTemplateInstantiationArgs()[0]
							.getAsType().getTypePtr()->getAsCXXRecordDecl();
					APIData::instance().insertClassData(buildClassInfo(typedListDecl), baseClasses(typedListDecl));
				}
			}

			addClassEnums(cData);
			// Add class to api structure
			APIData::instance().addIncludeFile(QString::fromStdString(currentFile_));
			cData.abstract_ = classDecl->isAbstract();
			APIData::instance().insertClassData(cData, bases);
		}
	}
}

void EnvisionAstConsumer::resolveOverloads(ClassData& cData,
														 const QMultiHash<QString, clang::CXXMethodDecl*>& overloads)
{
	auto keys = overloads.uniqueKeys();
	for (const auto& key : keys)
	{
		auto values = overloads.values(key);
		if (values.size() <= 1) continue;
		// Remove non-public and templated methods.
		values.erase(std::remove_if(values.begin(), values.end(), [](const clang::CXXMethodDecl* method) {
			return method->getAccess() != clang::AccessSpecifier::AS_public ||
					(method->getTemplatedKind() != clang::FunctionDecl::TemplatedKind::TK_NonTemplate &&
					 method->getTemplatedKind() != clang::FunctionDecl::TemplatedKind::TK_MemberSpecialization);
		}), values.end());
		if (values.isEmpty()) continue;

		// We want to replace the stored methods in cData thus we first remove everything.
		cData.methods_.erase(
			std::remove_if(cData.methods_.begin(), cData.methods_.end(), [key](const ClassMethod& descriptor) {
				return descriptor.name_ == key;
		}), cData.methods_.end());

		QString functionAddress = QString("&%1::%2").arg(cData.methodAddressPrefix(), key);
		for (int i = 0; i < values.size(); ++i)
		{
			auto method = values[i];
			QString overloadName = QString("%1_%2%3").arg(cData.className_, key, QString::number(i+1));
			QString returnType = TypeUtilities::qualTypeToString(method->getReturnType());
			QStringList argumentTypes;
			for (auto arg : method->params())
				argumentTypes.push_back(TypeUtilities::qualTypeToString(arg->getType()));
			QString newFunctionName = QString("*%1").arg(overloadName);
			if (!method->isStatic())
				newFunctionName.prepend("::").prepend(cData.qualifiedName_);
			QString signature = QString("%1 (%2)(%3)").arg(returnType, newFunctionName, argumentTypes.join(", "));
			if (method->isConst()) signature.append(" const");

			cData.overloadAliases_.append({signature, functionAddress});
			if (method->getReturnType().getTypePtr()->isPointerType())
				overloadName = QString("make_function(%1, return_internal_reference<>())").arg(overloadName);
			else if (method->getReturnType().getTypePtr()->isReferenceType())
				overloadName = QString("make_function(%1, return_internal_reference<>())").arg(overloadName);
			cData.methods_.append({key, overloadName});
		}
	}
}

QString EnvisionAstConsumer::functionStringFor(const QString& methodName, const QString& qualifiedClassName,
															  const clang::CXXMethodDecl* method)
{
	QString functionName = QString("&%1::%2").arg(qualifiedClassName, methodName);
	auto returnTypePtr = method->getReturnType().getTypePtr();
	if (returnTypePtr->isReferenceType())
	{
		auto refType = llvm::dyn_cast<clang::ReferenceType>(returnTypePtr);
		auto pointeeType = refType->getPointeeType();
		if (pointeeType.isConstQualified())
		{
			QString pointeeTypeString = TypeUtilities::typePtrToString(pointeeType.getTypePtr());
			QString castString = QString("(const %1& (%2::*)())").arg(pointeeTypeString, qualifiedClassName);
			if (method->isStatic()) castString = "";
			functionName = QString("make_function(%1%2,"
												 " return_value_policy<copy_const_reference>())")
					.arg(castString, functionName);
		}
		else
			functionName = QString("make_function(%1, return_internal_reference<>())").arg(functionName);
	}
	else if (returnTypePtr->isPointerType())
	{
		functionName = QString("make_function(%1, return_internal_reference<>())").arg(functionName);
		checkForTypedList(returnTypePtr);
	}
	return functionName;
}

ClassAttribute EnvisionAstConsumer::attribute(const QString& attributeName, const QString& attributeSetterName,
													const QString& qualifiedClassName, const clang::CXXMethodDecl* method)
{
	QString getter = functionStringFor(attributeName, qualifiedClassName, method);
	QString setter = QString("&%1::%2").arg(qualifiedClassName, attributeSetterName);
	return {attributeName, getter, setter};
}

QStringList EnvisionAstConsumer::baseClasses(clang::CXXRecordDecl* classDecl)
{
	QStringList result {};
	for (auto base : classDecl->bases())
		result << baseClasses(base.getType()->getAsCXXRecordDecl());
	result << QString::fromStdString(classDecl->getQualifiedNameAsString());
	result.removeAll("Core::Reflect");
	return result;
}

ClassData EnvisionAstConsumer::buildClassInfo(clang::CXXRecordDecl* classDecl)
{
	auto className = QString::fromStdString(classDecl->getNameAsString());
	auto qualifiedName = QString::fromStdString(classDecl->getQualifiedNameAsString());

	bool isTemplate = false;
	if (auto templatedDecl = llvm::dyn_cast<clang::ClassTemplateSpecializationDecl>(classDecl))
	{
		auto argDecl = templatedDecl->getTemplateInstantiationArgs()[0].getAsType().getTypePtr()->getAsCXXRecordDecl();
		auto argName = QString::fromStdString(argDecl->getNameAsString());
		auto qualifiedArg = QString::fromStdString(argDecl->getQualifiedNameAsString());
		qualifiedName.append(QString("<%1>").arg(qualifiedArg));
		className.append("Of" + argName);
		isTemplate = true;
	}

	ClassData cData{className, qualifiedName};
	if (isTemplate)
		cData.usingAlias_ = className;

	addBases(cData, classDecl);

	QSet<QString> seenMethods;
	QSet<QString> possibleAttributeSetters;
	for (auto method : classDecl->methods())
	{
		QString methodName = QString::fromStdString(method->getNameAsString());
		if (seenMethods.contains(methodName)) continue;

		auto it = attributes_.find(methodName);
		if (it != attributes_.end())
		{
			cData.attributes_.append(attribute(it.key(), it.value(), cData.qualifiedName_, method));
			seenMethods << it.key() << it.value();
		}
		else if (methodName.size() > 3 && methodName.startsWith("set"))
		{
			possibleAttributeSetters << methodName;
		}
	}
	QMultiHash<QString, clang::CXXMethodDecl*> overloads;
	// check if we have some more attributes which don't have an attribute macro:
	for (auto method : classDecl->methods())
	{
		// Ignore de-/constructors & deleted methods & overloaded operators:
		if (llvm::isa<clang::CXXConstructorDecl>(method) || llvm::isa<clang::CXXDestructorDecl>(method)) continue;
		if (method->isDeleted()) continue;
		if (method->isOverloadedOperator()) continue;

		QString methodName = QString::fromStdString(method->getNameAsString());
		if (method->getAccess() != clang::AccessSpecifier::AS_public ||
			 (method->getTemplatedKind() != clang::FunctionDecl::TemplatedKind::TK_NonTemplate &&
			  method->getTemplatedKind() != clang::FunctionDecl::TemplatedKind::TK_MemberSpecialization) ||
			 // workaround since we only consider cpp files at the moment:
			 methodName == "firstAncestorOfType")
		{
			// For wrapping methods we should still consider non public and template methods for overload resolution.
			overloads.insertMulti(methodName, method);
			continue;
		}
		if (seenMethods.contains(methodName)) continue;

		if (TypeUtilities::typePtrToString(method->getReturnType().getTypePtr()).contains("iterator")) continue;

		if (possibleAttributeSetters.contains(methodName)) continue;
		bool usedAsAttribute = false;
		for (const QString& setterName : possibleAttributeSetters)
		{
			QString possibleGetterName = setterName.mid(4); // drop set and first letter
			possibleGetterName.prepend(setterName[3].toLower());
			if (methodName == possibleGetterName)
			{
				// Found another attribute:
				cData.attributes_.append(attribute(methodName, setterName, cData.qualifiedName_, method));
				seenMethods << setterName << methodName;
				usedAsAttribute = true;
				break;
			}
		}
		if (!usedAsAttribute)
		{
			// found a free standing method to export.
			cData.methods_.append({methodName, functionStringFor(methodName, cData.methodAddressPrefix(), method),
										  method->isStatic()});
			overloads.insertMulti(methodName, method);
		}
	}
	resolveOverloads(cData, overloads);
	return cData;
}

void EnvisionAstConsumer::checkForTypedList(const clang::Type* type)
{
	QString fullName = TypeUtilities::typePtrToString(type);
	if (fullName.contains("TypedList"))
	{
		static QRegularExpression typedListMatcher("([^<]+)<([\\w<>:]+)>");
		auto match = typedListMatcher.match(fullName);
		if (match.hasMatch())
		{
			QString itemType = match.captured(2);
			APIData::instance().insertTypeList(itemType);
		}
	}
}

void EnvisionAstConsumer::addClassEnums(ClassData& cData)
{
	for (const auto& pEnum : processedEnums_)
	{
		QStringList names = pEnum.qualifiedName_.split("::");
		if (names[names.size()-2] == cData.className_)
			cData.enums_ << pEnum;
	}
	processedEnums_.clear();
}

void EnvisionAstConsumer::addBases(ClassData& cData, const clang::CXXRecordDecl* classDecl)
{
	for (auto base : classDecl->bases())
		cData.baseClasses_ << TypeUtilities::typePtrToString(base.getType().getTypePtr());
}
<commit_msg>Do not wrap registerNodeType in PythonWrapperGen<commit_after>/***********************************************************************************************************************
**
** Copyright (c) 2011, 2015 ETH Zurich
** All rights reserved.
**
** Redistribution and use in source and binary forms, with or without modification, are permitted provided that the
** following conditions are met:
**
**    * Redistributions of source code must retain the above copyright notice, this list of conditions and the following
**      disclaimer.
**    * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the
**      following disclaimer in the documentation and/or other materials provided with the distribution.
**    * Neither the name of the ETH Zurich nor the names of its contributors may be used to endorse or promote products
**      derived from this software without specific prior written permission.
**
**
** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
** INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
** DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
** SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
** WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
**
***********************************************************************************************************************/

#include "EnvisionAstConsumer.h"

#include "APIData.h"
#include "EnvisionPPCallbacks.h"
#include "TypeUtilities.h"

EnvisionAstConsumer::EnvisionAstConsumer(clang::CompilerInstance& ci, QString currentFile)
	: compilerInstance_{ci}
{
	currentFile.replace(".cpp", ".h");
	currentFile_ = currentFile.toStdString();
	currentClassName_ = currentFile.split(QDir::separator()).last();
	currentClassName_ = currentClassName_.split(".").first();
}

void EnvisionAstConsumer::Initialize(clang::ASTContext&)
{
	compilerInstance_.getPreprocessor().addPPCallbacks(
				std::make_unique<EnvisionPPCallbacks>(compilerInstance_.getSourceManager(), currentFile_, attributes_));
	compilerInstance_.getPreprocessor().enableIncrementalProcessing();
}

void EnvisionAstConsumer::HandleTagDeclDefinition(clang::TagDecl* tagDecl)
{
	if (auto classDecl = llvm::dyn_cast<clang::CXXRecordDecl>(tagDecl))
		HandleClassDecl(classDecl);
	else if (auto enumDecl = llvm::dyn_cast<clang::EnumDecl>(tagDecl))
		HandleEnumDecl(enumDecl);
	else
		Q_ASSERT(false); // There should not be any other decl here, check clang documentation.
}

void EnvisionAstConsumer::HandleEnumDecl(clang::EnumDecl* enumDecl)
{
	// Only consider enums that are in the current class:
	auto classContext = llvm::dyn_cast<clang::RecordDecl>(enumDecl->getDeclContext());
	if (!classContext ||
		 QString::fromStdString(classContext->getNameAsString()) != currentClassName_)
		return;
	// Ignore private enums:
	if (enumDecl->getAccess() == clang::AccessSpecifier::AS_private) return;

	QString enumName = QString::fromStdString(enumDecl->getNameAsString());
	QString qualifiedName = QString::fromStdString(enumDecl->getQualifiedNameAsString());;
	EnumData eData{enumName, qualifiedName};

	for (auto enumConstant : enumDecl->enumerators())
	{
		QString constantName = QString::fromStdString(enumConstant->getNameAsString());
		eData.values_.append({constantName, QString("%1::%2").arg(eData.qualifiedName_, constantName)});
	}

	processedEnums_ << eData;
}

void EnvisionAstConsumer::HandleClassDecl(clang::CXXRecordDecl* classDecl)
{
	auto context = classDecl->getDeclContext();
	if (context->isNamespace())
	{
		auto namespaceDecl = llvm::dyn_cast<clang::NamespaceDecl>(context);
		auto namespaceName = QString::fromStdString(namespaceDecl->getNameAsString());
		auto className = QString::fromStdString(classDecl->getNameAsString());
		if (namespaceName == APIData::instance().namespaceName_ && className == currentClassName_)
		{
			QStringList bases = baseClasses(classDecl);
			if (!allowedBases_.contains(bases[0])) return; // We only consider classes which have a base that we allow.

			auto cData = buildClassInfo(classDecl);
			auto typedListIt = std::find_if(classDecl->bases_begin(), classDecl->bases_end(),
				[](const clang::CXXBaseSpecifier& base)
				{
					auto baseName = TypeUtilities::typePtrToString(base.getType().getTypePtr());
					return baseName.contains("TypedList");
				});
			if (typedListIt != classDecl->bases_end())
			{
				// Parse TypedList base class as well, this is usually in the form Reclect<TypedList<T>>
				// Therefore we extract the bases first template parameter and assume that it is the TypedList.
				auto baseDecl = typedListIt->getType().getTypePtr()->getAsCXXRecordDecl();
				if (auto templateClass = llvm::dyn_cast<clang::ClassTemplateSpecializationDecl>(baseDecl))
				{
					auto typedListDecl = templateClass->getTemplateInstantiationArgs()[0]
							.getAsType().getTypePtr()->getAsCXXRecordDecl();
					APIData::instance().insertClassData(buildClassInfo(typedListDecl), baseClasses(typedListDecl));
				}
			}

			addClassEnums(cData);
			// Add class to api structure
			APIData::instance().addIncludeFile(QString::fromStdString(currentFile_));
			cData.abstract_ = classDecl->isAbstract();
			APIData::instance().insertClassData(cData, bases);
		}
	}
}

void EnvisionAstConsumer::resolveOverloads(ClassData& cData,
														 const QMultiHash<QString, clang::CXXMethodDecl*>& overloads)
{
	auto keys = overloads.uniqueKeys();
	for (const auto& key : keys)
	{
		auto values = overloads.values(key);
		if (values.size() <= 1) continue;
		// Remove non-public and templated methods.
		values.erase(std::remove_if(values.begin(), values.end(), [](const clang::CXXMethodDecl* method) {
			return method->getAccess() != clang::AccessSpecifier::AS_public ||
					(method->getTemplatedKind() != clang::FunctionDecl::TemplatedKind::TK_NonTemplate &&
					 method->getTemplatedKind() != clang::FunctionDecl::TemplatedKind::TK_MemberSpecialization);
		}), values.end());
		if (values.isEmpty()) continue;

		// We want to replace the stored methods in cData thus we first remove everything.
		cData.methods_.erase(
			std::remove_if(cData.methods_.begin(), cData.methods_.end(), [key](const ClassMethod& descriptor) {
				return descriptor.name_ == key;
		}), cData.methods_.end());

		QString functionAddress = QString("&%1::%2").arg(cData.methodAddressPrefix(), key);
		for (int i = 0; i < values.size(); ++i)
		{
			auto method = values[i];
			QString overloadName = QString("%1_%2%3").arg(cData.className_, key, QString::number(i+1));
			QString returnType = TypeUtilities::qualTypeToString(method->getReturnType());
			QStringList argumentTypes;
			for (auto arg : method->params())
				argumentTypes.push_back(TypeUtilities::qualTypeToString(arg->getType()));
			QString newFunctionName = QString("*%1").arg(overloadName);
			if (!method->isStatic())
				newFunctionName.prepend("::").prepend(cData.qualifiedName_);
			QString signature = QString("%1 (%2)(%3)").arg(returnType, newFunctionName, argumentTypes.join(", "));
			if (method->isConst()) signature.append(" const");

			cData.overloadAliases_.append({signature, functionAddress});
			if (method->getReturnType().getTypePtr()->isPointerType())
				overloadName = QString("make_function(%1, return_internal_reference<>())").arg(overloadName);
			else if (method->getReturnType().getTypePtr()->isReferenceType())
				overloadName = QString("make_function(%1, return_internal_reference<>())").arg(overloadName);
			cData.methods_.append({key, overloadName});
		}
	}
}

QString EnvisionAstConsumer::functionStringFor(const QString& methodName, const QString& qualifiedClassName,
															  const clang::CXXMethodDecl* method)
{
	QString functionName = QString("&%1::%2").arg(qualifiedClassName, methodName);
	auto returnTypePtr = method->getReturnType().getTypePtr();
	if (returnTypePtr->isReferenceType())
	{
		auto refType = llvm::dyn_cast<clang::ReferenceType>(returnTypePtr);
		auto pointeeType = refType->getPointeeType();
		if (pointeeType.isConstQualified())
		{
			QString pointeeTypeString = TypeUtilities::typePtrToString(pointeeType.getTypePtr());
			QString castString = QString("(const %1& (%2::*)())").arg(pointeeTypeString, qualifiedClassName);
			if (method->isStatic()) castString = "";
			functionName = QString("make_function(%1%2,"
												 " return_value_policy<copy_const_reference>())")
					.arg(castString, functionName);
		}
		else
			functionName = QString("make_function(%1, return_internal_reference<>())").arg(functionName);
	}
	else if (returnTypePtr->isPointerType())
	{
		functionName = QString("make_function(%1, return_internal_reference<>())").arg(functionName);
		checkForTypedList(returnTypePtr);
	}
	return functionName;
}

ClassAttribute EnvisionAstConsumer::attribute(const QString& attributeName, const QString& attributeSetterName,
													const QString& qualifiedClassName, const clang::CXXMethodDecl* method)
{
	QString getter = functionStringFor(attributeName, qualifiedClassName, method);
	QString setter = QString("&%1::%2").arg(qualifiedClassName, attributeSetterName);
	return {attributeName, getter, setter};
}

QStringList EnvisionAstConsumer::baseClasses(clang::CXXRecordDecl* classDecl)
{
	QStringList result {};
	for (auto base : classDecl->bases())
		result << baseClasses(base.getType()->getAsCXXRecordDecl());
	result << QString::fromStdString(classDecl->getQualifiedNameAsString());
	result.removeAll("Core::Reflect");
	return result;
}

ClassData EnvisionAstConsumer::buildClassInfo(clang::CXXRecordDecl* classDecl)
{
	auto className = QString::fromStdString(classDecl->getNameAsString());
	auto qualifiedName = QString::fromStdString(classDecl->getQualifiedNameAsString());

	bool isTemplate = false;
	if (auto templatedDecl = llvm::dyn_cast<clang::ClassTemplateSpecializationDecl>(classDecl))
	{
		auto argDecl = templatedDecl->getTemplateInstantiationArgs()[0].getAsType().getTypePtr()->getAsCXXRecordDecl();
		auto argName = QString::fromStdString(argDecl->getNameAsString());
		auto qualifiedArg = QString::fromStdString(argDecl->getQualifiedNameAsString());
		qualifiedName.append(QString("<%1>").arg(qualifiedArg));
		className.append("Of" + argName);
		isTemplate = true;
	}

	ClassData cData{className, qualifiedName};
	if (isTemplate)
		cData.usingAlias_ = className;

	addBases(cData, classDecl);

	QSet<QString> seenMethods;
	QSet<QString> possibleAttributeSetters;
	for (auto method : classDecl->methods())
	{
		QString methodName = QString::fromStdString(method->getNameAsString());
		if (seenMethods.contains(methodName)) continue;

		auto it = attributes_.find(methodName);
		if (it != attributes_.end())
		{
			cData.attributes_.append(attribute(it.key(), it.value(), cData.qualifiedName_, method));
			seenMethods << it.key() << it.value();
		}
		else if (methodName.size() > 3 && methodName.startsWith("set"))
		{
			possibleAttributeSetters << methodName;
		}
	}
	QMultiHash<QString, clang::CXXMethodDecl*> overloads;
	// check if we have some more attributes which don't have an attribute macro:
	for (auto method : classDecl->methods())
	{
		// Ignore de-/constructors & deleted methods & overloaded operators:
		if (llvm::isa<clang::CXXConstructorDecl>(method) || llvm::isa<clang::CXXDestructorDecl>(method)) continue;
		if (method->isDeleted()) continue;
		if (method->isOverloadedOperator()) continue;

		QString methodName = QString::fromStdString(method->getNameAsString());

		// Ignore registerNodeType, it does not make sense to have this function in Python. Furthermore it takes function
		// pointers as arguments which does not make sense in Python.
		// TODO: in general we should exclude functions that take functions pointers and lambdas.
		// A check would have to check all arguments (maybe also return type) for function pointer type.
		// Note that they might be hidden behind a typedef type.
		if (methodName == "registerNodeType") continue;

		if (method->getAccess() != clang::AccessSpecifier::AS_public ||
			 (method->getTemplatedKind() != clang::FunctionDecl::TemplatedKind::TK_NonTemplate &&
			  method->getTemplatedKind() != clang::FunctionDecl::TemplatedKind::TK_MemberSpecialization) ||
			 // workaround since we only consider cpp files at the moment:
			 methodName == "firstAncestorOfType")
		{
			// For wrapping methods we should still consider non public and template methods for overload resolution.
			overloads.insertMulti(methodName, method);
			continue;
		}
		if (seenMethods.contains(methodName)) continue;

		if (TypeUtilities::typePtrToString(method->getReturnType().getTypePtr()).contains("iterator")) continue;

		if (possibleAttributeSetters.contains(methodName)) continue;
		bool usedAsAttribute = false;
		for (const QString& setterName : possibleAttributeSetters)
		{
			QString possibleGetterName = setterName.mid(4); // drop set and first letter
			possibleGetterName.prepend(setterName[3].toLower());
			if (methodName == possibleGetterName)
			{
				// Found another attribute:
				cData.attributes_.append(attribute(methodName, setterName, cData.qualifiedName_, method));
				seenMethods << setterName << methodName;
				usedAsAttribute = true;
				break;
			}
		}
		if (!usedAsAttribute)
		{
			// found a free standing method to export.
			cData.methods_.append({methodName, functionStringFor(methodName, cData.methodAddressPrefix(), method),
										  method->isStatic()});
			overloads.insertMulti(methodName, method);
		}
	}
	resolveOverloads(cData, overloads);
	return cData;
}

void EnvisionAstConsumer::checkForTypedList(const clang::Type* type)
{
	QString fullName = TypeUtilities::typePtrToString(type);
	if (fullName.contains("TypedList"))
	{
		static QRegularExpression typedListMatcher("([^<]+)<([\\w<>:]+)>");
		auto match = typedListMatcher.match(fullName);
		if (match.hasMatch())
		{
			QString itemType = match.captured(2);
			APIData::instance().insertTypeList(itemType);
		}
	}
}

void EnvisionAstConsumer::addClassEnums(ClassData& cData)
{
	for (const auto& pEnum : processedEnums_)
	{
		QStringList names = pEnum.qualifiedName_.split("::");
		if (names[names.size()-2] == cData.className_)
			cData.enums_ << pEnum;
	}
	processedEnums_.clear();
}

void EnvisionAstConsumer::addBases(ClassData& cData, const clang::CXXRecordDecl* classDecl)
{
	for (auto base : classDecl->bases())
		cData.baseClasses_ << TypeUtilities::typePtrToString(base.getType().getTypePtr());
}
<|endoftext|>


<commit_before>//=======================================================================
// Copyright Baptiste Wicht 2011.
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
//  http://www.boost.org/LICENSE_1_0.txt)
//=======================================================================

#include <string>
#include <iostream>

#include "Options.hpp"
#include "Compiler.hpp"
#include "Utils.hpp"
#include "Platform.hpp"

#define BOOST_TEST_DYN_LINK
#define BOOST_TEST_MODULE EddicTestSuites
#include <boost/test/unit_test.hpp>

/* Macros to ease the tests  */

#define TEST_SAMPLE(file)\
BOOST_AUTO_TEST_CASE( samples_##file ){\
    assertCompiles("samples/" #file ".eddi", "--32");\
    assertCompiles("samples/" #file ".eddi", "--64");\
}

#define ASSERT_OUTPUT_32(file, output) assertOutputEquals(file, output, "--32");
#define ASSERT_OUTPUT_64(file, output) assertOutputEquals(file, output, "--64");

#define ASSERT_OUTPUT(file, output)\
    ASSERT_OUTPUT_32(file, output);\
    ASSERT_OUTPUT_64(file, output);

#include <boost/test/detail/unit_test_parameters.hpp>

/* Config Fixture  */

struct ConfigFixture {
    ConfigFixture(){
        //TODO Configure the show progress parameter
    }

    ~ConfigFixture(){
        /* Nothing to teard down */
    }
};

/* Fixture to delete the a.out file after the compilation */

struct DeleteOutFixture {
    DeleteOutFixture(){
        /* Nothing to setup  */
    }

    ~DeleteOutFixture(){
        BOOST_TEST_MESSAGE( "Delete the a.out file" );
        remove("a.out");
    }
};

inline void parse_options(const std::string& file, const std::string& param){
    const char* argv[4];
    argv[0] = "./bin/test";
    argv[1] = param.c_str();
    argv[2] = "--quiet";
    argv[3] = file.c_str();

    BOOST_REQUIRE (eddic::parseOptions(4, argv));
}

void assertCompiles(const std::string& file, const std::string& param){
    parse_options(file, param);

    eddic::Compiler compiler;
    int code = compiler.compile(file);

    BOOST_REQUIRE_EQUAL (code, 0);
}

void assert_compilation_error(const std::string& file, const std::string& param){
    parse_options(file, param);

    eddic::Compiler compiler;
    int code = compiler.compile(file);

    BOOST_REQUIRE_EQUAL (code, 1);
}

void assertOutputEquals(const std::string& file, const std::string& output, const std::string& param){
    assertCompiles("test/cases/" + file, param);

    std::string out = eddic::execCommand("./a.out");

    BOOST_CHECK_EQUAL (output, out);
}

/* Configure a global fixture for the configuration */

BOOST_GLOBAL_FIXTURE ( ConfigFixture  )

/* Compiles all the samples */

BOOST_FIXTURE_TEST_SUITE( SamplesSuite, DeleteOutFixture )

TEST_SAMPLE(arrays)
TEST_SAMPLE(asm)
TEST_SAMPLE(assembly)
TEST_SAMPLE(bool)
TEST_SAMPLE(compound)
TEST_SAMPLE(concat)
TEST_SAMPLE(const)
TEST_SAMPLE(functions)
TEST_SAMPLE(float)
TEST_SAMPLE(casts)
TEST_SAMPLE(inc)
TEST_SAMPLE(includes)
TEST_SAMPLE(optimize)
TEST_SAMPLE(problem)
TEST_SAMPLE(sort)
TEST_SAMPLE(identifiers)
TEST_SAMPLE(structures)

BOOST_AUTO_TEST_SUITE_END()

/* Specific tests */

BOOST_FIXTURE_TEST_SUITE(SpecificSuite, DeleteOutFixture)

BOOST_AUTO_TEST_CASE( array_foreach_local ){
    ASSERT_OUTPUT("array_foreach_local.eddi", "43210");
}

BOOST_AUTO_TEST_CASE( array_foreach_global ){
    ASSERT_OUTPUT("array_foreach_global.eddi", "43210");
}

BOOST_AUTO_TEST_CASE( array_foreach_param_local ){
    ASSERT_OUTPUT("array_foreach_param_local.eddi", "43210");
}

BOOST_AUTO_TEST_CASE( array_foreach_param_global ){
    ASSERT_OUTPUT("array_foreach_param_global.eddi", "43210");
}

BOOST_AUTO_TEST_CASE( array_foreach_param_param ){
    ASSERT_OUTPUT("array_foreach_param_param.eddi", "43210");
}

BOOST_AUTO_TEST_CASE( if_ ){
    ASSERT_OUTPUT("if.eddi", "Cool");
}

BOOST_AUTO_TEST_CASE( while_ ){
    ASSERT_OUTPUT("while.eddi", "01234");
}

BOOST_AUTO_TEST_CASE( do_while_ ){
    ASSERT_OUTPUT("do_while.eddi", "01234");
}

BOOST_AUTO_TEST_CASE( float_ ){
    //TODO Could be better to split this test
    ASSERT_OUTPUT_32("float.eddi", "5.4990|100.0|-100.0|100.0|2.0889|4.1999|3.3299|1.5000|3.0|5.0|4.5000|5.7500|1.5000|-2.0|7.5000|2.2699|7.5590|14.4927|3.0|8.0|3.0910|2.0934|5.1844|1|1|11111|8.0|13.7500|2.5000|5.5000|2.5000|5.5000|2.5000|5.5000|2.5000|5.5000|3.3299|");
    ASSERT_OUTPUT_64("float.eddi", "5.4989|100.0|-100.0|100.0|2.0889|4.2000|3.3300|1.5000|3.0|5.0|4.5000|5.7500|1.5000|-2.0|7.5000|2.2700|7.5590|14.4927|3.0|8.0|3.0910|2.0934|5.1844|1|1|11111|8.0|13.7500|2.5000|5.5000|2.5000|5.5000|2.5000|5.5000|2.5000|5.5000|3.3300|");
}

BOOST_AUTO_TEST_CASE( for_ ){
    ASSERT_OUTPUT("for.eddi", "01234");
}

BOOST_AUTO_TEST_CASE( foreach_ ){
    ASSERT_OUTPUT("foreach.eddi", "012345");
}

BOOST_AUTO_TEST_CASE( globals_ ){
    ASSERT_OUTPUT("globals.eddi", "1000a2000aa");
}

BOOST_AUTO_TEST_CASE( void_functions ){
    ASSERT_OUTPUT("void.eddi", "4445");
}

BOOST_AUTO_TEST_CASE( string_functions ){
    ASSERT_OUTPUT("return_string.eddi", "abcdef");
}

BOOST_AUTO_TEST_CASE( int_functions ){
    ASSERT_OUTPUT("return_int.eddi", "484");
}

BOOST_AUTO_TEST_CASE( recursive_functions ){
    ASSERT_OUTPUT("recursive.eddi", "362880");
}

BOOST_AUTO_TEST_CASE( math ){
    ASSERT_OUTPUT("math.eddi", "333|111|-111|0|24642|2|-2|-1|1|2|0|-111|");
}

BOOST_AUTO_TEST_CASE( builtin ){
    ASSERT_OUTPUT("builtin.eddi", "10|11|12|13|12|13|10|11|4|8|13|8|0|3|");
}

BOOST_AUTO_TEST_CASE( assign_value ){
    ASSERT_OUTPUT("assign_value.eddi", "66779921");
}

BOOST_AUTO_TEST_CASE( concat ){
    ASSERT_OUTPUT("concat.eddi", "asdf1234|1234asdf|asdfasdf|12341234|");
}

BOOST_AUTO_TEST_CASE( prints ){
    ASSERT_OUTPUT_32("prints.eddi", "111|0|-111|0|1|999.9899|1.0089|0.0|-1.0089|-999.9899||-0|asdf|1234asdf|");
    ASSERT_OUTPUT_64("prints.eddi", "111|0|-111|0|1|999.9900|1.0089|0.0|-1.0089|-999.9900||-0|asdf|1234asdf|");
}

BOOST_AUTO_TEST_CASE( structures ){
    ASSERT_OUTPUT_32("structures.eddi", "222|666|3.2300|0|asdf|333|888|4.3299|1|ertz|333|888|4.3299|1|ertz|");
    ASSERT_OUTPUT_64("structures.eddi", "222|666|3.2300|0|asdf|333|888|4.3300|1|ertz|333|888|4.3300|1|ertz|");
}

BOOST_AUTO_TEST_CASE( args ){
    assertCompiles("test/cases/args.eddi", "--32");

    std::string out = eddic::execCommand("./a.out");
    BOOST_CHECK_EQUAL ("./a.out|", out);

    out = eddic::execCommand("./a.out arg1 arg2 arg3");
    BOOST_CHECK_EQUAL ("./a.out|arg1|arg2|arg3|", out);

    assertCompiles("test/cases/args.eddi", "--64");

    out = eddic::execCommand("./a.out");
    BOOST_CHECK_EQUAL ("./a.out|", out);

    out = eddic::execCommand("./a.out arg1 arg2 arg3");
    BOOST_CHECK_EQUAL ("./a.out|arg1|arg2|arg3|", out);
}

BOOST_AUTO_TEST_SUITE_END()

/* Verify that the compilation fails for invalid statements */

BOOST_FIXTURE_TEST_SUITE(CompilationErrorsSuite, DeleteOutFixture)

BOOST_AUTO_TEST_CASE( params_assign ){
    assert_compilation_error("params_assign.eddi", "--32");
    assert_compilation_error("params_assign.eddi", "--64");
}

BOOST_AUTO_TEST_SUITE_END()

/* Standard library test suite  */

BOOST_FIXTURE_TEST_SUITE(StandardLibSuite, DeleteOutFixture)

BOOST_AUTO_TEST_CASE( std_lib_arrays_sum ){
    ASSERT_OUTPUT("stdlib_array_sum.eddi", "100");
}

BOOST_AUTO_TEST_CASE( std_lib_math_min ){
    ASSERT_OUTPUT("stdlib_math_min.eddi", "999|0|0|-1|0|-1");
}

BOOST_AUTO_TEST_CASE( std_lib_math_max ){
    ASSERT_OUTPUT("stdlib_math_max.eddi", "1000|1|1|0|0|0");
}

BOOST_AUTO_TEST_CASE( std_lib_math_factorial ){
    ASSERT_OUTPUT("stdlib_math_factorial.eddi", "1|1|2|362880");
}

BOOST_AUTO_TEST_CASE( std_lib_math_pow ){
    ASSERT_OUTPUT("stdlib_math_pow.eddi", "0|1|10|100|1024|1");
}

BOOST_AUTO_TEST_SUITE_END()

/* Unit test for bug fixes regression */

BOOST_FIXTURE_TEST_SUITE(BugFixesSuite, DeleteOutFixture)

BOOST_AUTO_TEST_CASE( while_bug ){
    ASSERT_OUTPUT("while_bug.eddi", "W1W2W3W4W5");
}

BOOST_AUTO_TEST_SUITE_END()
<commit_msg>cleanup the tests<commit_after>//=======================================================================
// Copyright Baptiste Wicht 2011.
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
//  http://www.boost.org/LICENSE_1_0.txt)
//=======================================================================

#include <string>
#include <iostream>

#include "Options.hpp"
#include "Compiler.hpp"
#include "Utils.hpp"
#include "Platform.hpp"

#define BOOST_TEST_DYN_LINK
#define BOOST_TEST_MODULE EddicTestSuites
#include <boost/test/unit_test.hpp>
#include <boost/test/detail/unit_test_parameters.hpp>

/*
 * \def TEST_SAMPLE(file)
 * Generate a test case that verify that the sample compiles in both 32 and 64 bits mode.
 */
#define TEST_SAMPLE(file)\
BOOST_AUTO_TEST_CASE( samples_##file ){\
    assertCompiles("samples/" #file ".eddi", "--32");\
    assertCompiles("samples/" #file ".eddi", "--64");\
}

/* Config Fixture  */

struct ConfigFixture {
    ConfigFixture(){
        //TODO Configure the show progress parameter
    }

    ~ConfigFixture(){
        /* Nothing to teard down */
    }
};

/* Fixture to delete the a.out file after the compilation */

struct DeleteOutFixture {
    DeleteOutFixture(){
        /* Nothing to setup  */
    }

    ~DeleteOutFixture(){
        BOOST_TEST_MESSAGE( "Delete the a.out file" );
        remove("a.out");
    }
};

inline void parse_options(const std::string& file, const std::string& param){
    const char* argv[4];
    argv[0] = "./bin/test";
    argv[1] = param.c_str();
    argv[2] = "--quiet";
    argv[3] = file.c_str();

    BOOST_REQUIRE (eddic::parseOptions(4, argv));
}

void assertCompiles(const std::string& file, const std::string& param){
    parse_options(file, param);

    eddic::Compiler compiler;
    int code = compiler.compile(file);

    BOOST_REQUIRE_EQUAL (code, 0);
}

void assert_compilation_error(const std::string& file, const std::string& param){
    parse_options(file, param);

    eddic::Compiler compiler;
    int code = compiler.compile(file);

    BOOST_REQUIRE_EQUAL (code, 1);
}

void assertOutputEquals(const std::string& file, const std::string& output, const std::string& param){
    assertCompiles("test/cases/" + file, param);

    std::string out = eddic::execCommand("./a.out");

    BOOST_CHECK_EQUAL (output, out);
}

void assert_output_32(const std::string& file, const std::string& output){
    assertOutputEquals(file, output, "--32");
}

void assert_output_64(const std::string& file, const std::string& output){
    assertOutputEquals(file, output, "--64");
}

void assert_output(const std::string& file, const std::string& output){
    assert_output_32(file, output);
    assert_output_64(file, output);
}

/* Configure a global fixture for the configuration */

BOOST_GLOBAL_FIXTURE ( ConfigFixture  )

/* Compiles all the samples */

BOOST_FIXTURE_TEST_SUITE( SamplesSuite, DeleteOutFixture )

TEST_SAMPLE(arrays)
TEST_SAMPLE(asm)
TEST_SAMPLE(assembly)
TEST_SAMPLE(bool)
TEST_SAMPLE(compound)
TEST_SAMPLE(concat)
TEST_SAMPLE(const)
TEST_SAMPLE(functions)
TEST_SAMPLE(float)
TEST_SAMPLE(casts)
TEST_SAMPLE(inc)
TEST_SAMPLE(includes)
TEST_SAMPLE(optimize)
TEST_SAMPLE(problem)
TEST_SAMPLE(sort)
TEST_SAMPLE(identifiers)
TEST_SAMPLE(structures)

BOOST_AUTO_TEST_SUITE_END()

/* Specific tests */

BOOST_FIXTURE_TEST_SUITE(SpecificSuite, DeleteOutFixture)

BOOST_AUTO_TEST_CASE( array_foreach_local ){
    assert_output("array_foreach_local.eddi", "43210");
}

BOOST_AUTO_TEST_CASE( array_foreach_global ){
    assert_output("array_foreach_global.eddi", "43210");
}

BOOST_AUTO_TEST_CASE( array_foreach_param_local ){
    assert_output("array_foreach_param_local.eddi", "43210");
}

BOOST_AUTO_TEST_CASE( array_foreach_param_global ){
    assert_output("array_foreach_param_global.eddi", "43210");
}

BOOST_AUTO_TEST_CASE( array_foreach_param_param ){
    assert_output("array_foreach_param_param.eddi", "43210");
}

BOOST_AUTO_TEST_CASE( if_ ){
    assert_output("if.eddi", "Cool");
}

BOOST_AUTO_TEST_CASE( while_ ){
    assert_output("while.eddi", "01234");
}

BOOST_AUTO_TEST_CASE( do_while_ ){
    assert_output("do_while.eddi", "01234");
}

BOOST_AUTO_TEST_CASE( float_ ){
    //TODO Could be better to split this test
    assert_output_32("float.eddi", "5.4990|100.0|-100.0|100.0|2.0889|4.1999|3.3299|1.5000|3.0|5.0|4.5000|5.7500|1.5000|-2.0|7.5000|2.2699|7.5590|14.4927|3.0|8.0|3.0910|2.0934|5.1844|1|1|11111|8.0|13.7500|2.5000|5.5000|2.5000|5.5000|2.5000|5.5000|2.5000|5.5000|3.3299|");
    assert_output_64("float.eddi", "5.4989|100.0|-100.0|100.0|2.0889|4.2000|3.3300|1.5000|3.0|5.0|4.5000|5.7500|1.5000|-2.0|7.5000|2.2700|7.5590|14.4927|3.0|8.0|3.0910|2.0934|5.1844|1|1|11111|8.0|13.7500|2.5000|5.5000|2.5000|5.5000|2.5000|5.5000|2.5000|5.5000|3.3300|");
}

BOOST_AUTO_TEST_CASE( for_ ){
    assert_output("for.eddi", "01234");
}

BOOST_AUTO_TEST_CASE( foreach_ ){
    assert_output("foreach.eddi", "012345");
}

BOOST_AUTO_TEST_CASE( globals_ ){
    assert_output("globals.eddi", "1000a2000aa");
}

BOOST_AUTO_TEST_CASE( void_functions ){
    assert_output("void.eddi", "4445");
}

BOOST_AUTO_TEST_CASE( string_functions ){
    assert_output("return_string.eddi", "abcdef");
}

BOOST_AUTO_TEST_CASE( int_functions ){
    assert_output("return_int.eddi", "484");
}

BOOST_AUTO_TEST_CASE( recursive_functions ){
    assert_output("recursive.eddi", "362880");
}

BOOST_AUTO_TEST_CASE( math ){
    assert_output("math.eddi", "333|111|-111|0|24642|2|-2|-1|1|2|0|-111|");
}

BOOST_AUTO_TEST_CASE( builtin ){
    assert_output("builtin.eddi", "10|11|12|13|12|13|10|11|4|8|13|8|0|3|");
}

BOOST_AUTO_TEST_CASE( assign_value ){
    assert_output("assign_value.eddi", "66779921");
}

BOOST_AUTO_TEST_CASE( concat ){
    assert_output("concat.eddi", "asdf1234|1234asdf|asdfasdf|12341234|");
}

BOOST_AUTO_TEST_CASE( prints ){
    assert_output_32("prints.eddi", "111|0|-111|0|1|999.9899|1.0089|0.0|-1.0089|-999.9899||-0|asdf|1234asdf|");
    assert_output_64("prints.eddi", "111|0|-111|0|1|999.9900|1.0089|0.0|-1.0089|-999.9900||-0|asdf|1234asdf|");
}

BOOST_AUTO_TEST_CASE( structures ){
    assert_output_32("structures.eddi", "222|666|3.2300|0|asdf|333|888|4.3299|1|ertz|333|888|4.3299|1|ertz|");
    assert_output_64("structures.eddi", "222|666|3.2300|0|asdf|333|888|4.3300|1|ertz|333|888|4.3300|1|ertz|");
}

BOOST_AUTO_TEST_CASE( args ){
    assertCompiles("test/cases/args.eddi", "--32");

    std::string out = eddic::execCommand("./a.out");
    BOOST_CHECK_EQUAL ("./a.out|", out);

    out = eddic::execCommand("./a.out arg1 arg2 arg3");
    BOOST_CHECK_EQUAL ("./a.out|arg1|arg2|arg3|", out);

    assertCompiles("test/cases/args.eddi", "--64");

    out = eddic::execCommand("./a.out");
    BOOST_CHECK_EQUAL ("./a.out|", out);

    out = eddic::execCommand("./a.out arg1 arg2 arg3");
    BOOST_CHECK_EQUAL ("./a.out|arg1|arg2|arg3|", out);
}

BOOST_AUTO_TEST_SUITE_END()

/* Verify that the compilation fails for invalid statements */

BOOST_FIXTURE_TEST_SUITE(CompilationErrorsSuite, DeleteOutFixture)

BOOST_AUTO_TEST_CASE( params_assign ){
    assert_compilation_error("params_assign.eddi", "--32");
    assert_compilation_error("params_assign.eddi", "--64");
}

BOOST_AUTO_TEST_SUITE_END()

/* Standard library test suite  */

BOOST_FIXTURE_TEST_SUITE(StandardLibSuite, DeleteOutFixture)

BOOST_AUTO_TEST_CASE( std_lib_arrays_sum ){
    assert_output("stdlib_array_sum.eddi", "100");
}

BOOST_AUTO_TEST_CASE( std_lib_math_min ){
    assert_output("stdlib_math_min.eddi", "999|0|0|-1|0|-1");
}

BOOST_AUTO_TEST_CASE( std_lib_math_max ){
    assert_output("stdlib_math_max.eddi", "1000|1|1|0|0|0");
}

BOOST_AUTO_TEST_CASE( std_lib_math_factorial ){
    assert_output("stdlib_math_factorial.eddi", "1|1|2|362880");
}

BOOST_AUTO_TEST_CASE( std_lib_math_pow ){
    assert_output("stdlib_math_pow.eddi", "0|1|10|100|1024|1");
}

BOOST_AUTO_TEST_SUITE_END()

/* Unit test for bug fixes regression */

BOOST_FIXTURE_TEST_SUITE(BugFixesSuite, DeleteOutFixture)

BOOST_AUTO_TEST_CASE( while_bug ){
    assert_output("while_bug.eddi", "W1W2W3W4W5");
}

BOOST_AUTO_TEST_SUITE_END()
<|endoftext|>