<commit_before>/**
 * @file base64url.c
 * @brief Base64url encoding scheme
 *
 * @section License
 *
 * SPDX-License-Identifier: GPL-2.0-or-later
 *
 * Copyright (C) 2010-2019 Oryx Embedded SARL. All rights reserved.
 *
 * This file is part of CycloneCrypto Open.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 *
 * @author Oryx Embedded SARL (www.oryx-embedded.com)
 * @version 1.9.6
 **/

#include "Base64Url.h"

#include <string>

using namespace std;

// Base64url encoding table
static const char base64urlEncTable[64] =
  {
    'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
    'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
    'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
    'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-', '_'
  };

// Base64url decoding table
static const uint8_t base64urlDecTable[128] =
  {
    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x3E, 0xFF, 0xFF,
    0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
    0xFF, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E,
    0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0xFF, 0xFF, 0xFF, 0xFF, 0x3F,
    0xFF, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28,
    0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F, 0x30, 0x31, 0x32, 0x33, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
  };

std::string
Base64Url::encode(const void * input, size_t inputLen) {
  uint8_t a;
  uint8_t b;
  uint8_t c;
  uint8_t d;

  std::string output;

  //Point to the first byte of the input data
  const uint8_t *p = (const uint8_t *) input;

  //Divide the input stream into blocks of 3 bytes
  size_t n = inputLen / 3;

  //A full encoding quantum is always completed at the end of a quantity
  if (inputLen == (n * 3 + 1)) {
    // The final quantum of encoding input is exactly 8 bits
    if (input) {
      output = std::string(n * 4 + 2, ' ');

      // Read input data
      a = (p[n * 3] & 0xFC) >> 2;
      b = (p[n * 3] & 0x03) << 4;

      //The final unit of encoded output will be two characters
      output[n * 4] = base64urlEncTable[a];
      output[n * 4 + 1] = base64urlEncTable[b];
    }
  } else if(inputLen == (n * 3 + 2)) {
    // The final quantum of encoding input is exactly 16 bits
    if (input) {
      output = std::string(n * 4 + 3, ' ');

      // Read input data
      a = (p[n * 3] & 0xFC) >> 2;
      b = ((p[n * 3] & 0x03) << 4) | ((p[n * 3 + 1] & 0xF0) >> 4);
      c = (p[n * 3 + 1] & 0x0F) << 2;

      // The final unit of encoded output will be three characters followed
      // by one "=" padding character
      output[n * 4] = base64urlEncTable[a];
      output[n * 4 + 1] = base64urlEncTable[b];
      output[n * 4 + 2] = base64urlEncTable[c];
    }
  } else {
    // The final quantum of encoding input is an integral multiple of 24 bits
    // The final unit of encoded output will be an integral multiple of 4 characters
    output = std::string(n * 4, ' ');
  }

  if (input) {
    // The input data is processed block by block
    while (n-- > 0) {
      // Read input data
      a = (p[n * 3] & 0xFC) >> 2;
      b = ((p[n * 3] & 0x03) << 4) | ((p[n * 3 + 1] & 0xF0) >> 4);
      c = ((p[n * 3 + 1] & 0x0F) << 2) | ((p[n * 3 + 2] & 0xC0) >> 6);
      d = p[n * 3 + 2] & 0x3F;

      // Map each 3-byte block to 4 printable characters using the Base64url character set
      output[n * 4] = base64urlEncTable[a];
      output[n * 4 + 1] = base64urlEncTable[b];
      output[n * 4 + 2] = base64urlEncTable[c];
      output[n * 4 + 3] = base64urlEncTable[d];
    }
  }

  return output;
}

/**
 * @brief Base64url decoding algorithm
 * @param[in] input Base64url-encoded string
 * @param[in] inputLen Length of the encoded string
 * @param[out] output Resulting decoded data
 * @param[out] outputLen Length of the decoded data
 * @return Error code
 **/

bool
Base64Url::decode(const char *input, size_t inputLen, void *output, size_t *outputLen)
{
  error_t error;
  uint32_t value;
  unsigned int c;
  size_t i;
  size_t n;
  uint8_t *p;

  //Check parameters
  if(input == NULL && inputLen != 0)
    return false;
  if(outputLen == NULL)
    return false;

  //Check the length of the input string
  if((inputLen % 4) == 1)
    return false;

  //Initialize status code
  error = 0;

  //Point to the buffer where to write the decoded data
  p = (uint8_t *) output;

  //Initialize variables
  n = 0;
  value = 0;

  //Process the Base64url-encoded string
  for(i = 0; i < inputLen && !error; i++)
    {
      //Get current character
      c = (unsigned int) input[i];

      //Check the value of the current character
      if(c < 128 && base64urlDecTable[c] < 64)
	{
          //Decode the current character
          value = (value << 6) | base64urlDecTable[c];

          //Divide the input stream into blocks of 4 characters
          if((i % 4) == 3)
	    {
	      //Map each 4-character block to 3 bytes
	      if(p != NULL)
		{
		  p[n] = (value >> 16) & 0xFF;
		  p[n + 1] = (value >> 8) & 0xFF;
		  p[n + 2] = value & 0xFF;
		}

	      //Adjust the length of the decoded data
	      n += 3;
	      //Decode next block
	      value = 0;
	    }
	}
      else
	{
          //Implementations must reject the encoded data if it contains
          //characters outside the base alphabet
          error = 1;
	}
    }

  //Check status code
  if(!error)
    {
      //All trailing pad characters are omitted in Base64url
      if((inputLen % 4) == 2)
	{
          //The last block contains only 1 byte
          if(p != NULL)
	    {
	      //Decode the last byte
	      p[n] = (value >> 4) & 0xFF;
	    }

          //Adjust the length of the decoded data
          n++;
	}
      else if((inputLen % 4) == 3)
	{
          //The last block contains only 2 bytes
          if(p != NULL)
	    {
	      //Decode the last two bytes
	      p[n] = (value >> 10) & 0xFF;
	      p[n + 1] = (value >> 2) & 0xFF;
	    }

          //Adjust the length of the decoded data
          n += 2;
	}
      else
	{
          //No pad characters in this case
	}
    }

  //Total number of bytes that have been written
  *outputLen = n;

  //Return status code
  return !error;
}

long long
Base64Url::decode_id(const std::string & s) {
  unsigned char * bytes = new unsigned char[4 * s.size()];
  size_t len = 0;
  decode(s.c_str(), s.size(), bytes, &len);

  long long id = (long long)bytes[0] + ((long long)bytes[1] << 8) + ((long long)bytes[2] << 16) + ((long long)bytes[3] << 24) + ((long long)bytes[4] << 32) + ((long long)bytes[5] << 40) + ((long long)bytes[6] << 48) + ((long long)bytes[7] << 56);
  delete[] bytes;
  return id;
}

std::string
Base64Url::encode_id(long long id) {
  unsigned char bytes[8];
  bytes[0] = id & 0xff;
  bytes[1] = (id >> 8) & 0xff;
  bytes[2] = (id >> 16) & 0xff;
  bytes[3] = (id >> 24) & 0xff;
  bytes[4] = (id >> 32) & 0xff;
  bytes[5] = (id >> 40) & 0xff;
  bytes[6] = (id >> 48) & 0xff;
  bytes[7] = (id >> 56) & 0xff;

  return encode(bytes, 8);
}

std::string
Base64Url::encode_id_bigendian(long long id) {
  unsigned char bytes[8];
  bytes[7] = id & 0xff;
  bytes[6] = (id >> 8) & 0xff;
  bytes[5] = (id >> 16) & 0xff;
  bytes[4] = (id >> 24) & 0xff;
  bytes[3] = (id >> 32) & 0xff;
  bytes[2] = (id >> 40) & 0xff;
  bytes[1] = (id >> 48) & 0xff;
  bytes[0] = (id >> 56) & 0xff;

  return encode(bytes, 8);
}
<commit_msg>Change type for WIN32<commit_after>/**
 * @file base64url.c
 * @brief Base64url encoding scheme
 *
 * @section License
 *
 * SPDX-License-Identifier: GPL-2.0-or-later
 *
 * Copyright (C) 2010-2019 Oryx Embedded SARL. All rights reserved.
 *
 * This file is part of CycloneCrypto Open.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 *
 * @author Oryx Embedded SARL (www.oryx-embedded.com)
 * @version 1.9.6
 **/

#include "Base64Url.h"

#include <string>

using namespace std;

// Base64url encoding table
static const char base64urlEncTable[64] =
  {
    'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
    'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
    'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
    'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-', '_'
  };

// Base64url decoding table
static const uint8_t base64urlDecTable[128] =
  {
    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x3E, 0xFF, 0xFF,
    0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
    0xFF, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E,
    0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0xFF, 0xFF, 0xFF, 0xFF, 0x3F,
    0xFF, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28,
    0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F, 0x30, 0x31, 0x32, 0x33, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
  };

std::string
Base64Url::encode(const void * input, size_t inputLen) {
  uint8_t a;
  uint8_t b;
  uint8_t c;
  uint8_t d;

  std::string output;

  //Point to the first byte of the input data
  const uint8_t *p = (const uint8_t *) input;

  //Divide the input stream into blocks of 3 bytes
  size_t n = inputLen / 3;

  //A full encoding quantum is always completed at the end of a quantity
  if (inputLen == (n * 3 + 1)) {
    // The final quantum of encoding input is exactly 8 bits
    if (input) {
      output = std::string(n * 4 + 2, ' ');

      // Read input data
      a = (p[n * 3] & 0xFC) >> 2;
      b = (p[n * 3] & 0x03) << 4;

      //The final unit of encoded output will be two characters
      output[n * 4] = base64urlEncTable[a];
      output[n * 4 + 1] = base64urlEncTable[b];
    }
  } else if(inputLen == (n * 3 + 2)) {
    // The final quantum of encoding input is exactly 16 bits
    if (input) {
      output = std::string(n * 4 + 3, ' ');

      // Read input data
      a = (p[n * 3] & 0xFC) >> 2;
      b = ((p[n * 3] & 0x03) << 4) | ((p[n * 3 + 1] & 0xF0) >> 4);
      c = (p[n * 3 + 1] & 0x0F) << 2;

      // The final unit of encoded output will be three characters followed
      // by one "=" padding character
      output[n * 4] = base64urlEncTable[a];
      output[n * 4 + 1] = base64urlEncTable[b];
      output[n * 4 + 2] = base64urlEncTable[c];
    }
  } else {
    // The final quantum of encoding input is an integral multiple of 24 bits
    // The final unit of encoded output will be an integral multiple of 4 characters
    output = std::string(n * 4, ' ');
  }

  if (input) {
    // The input data is processed block by block
    while (n-- > 0) {
      // Read input data
      a = (p[n * 3] & 0xFC) >> 2;
      b = ((p[n * 3] & 0x03) << 4) | ((p[n * 3 + 1] & 0xF0) >> 4);
      c = ((p[n * 3 + 1] & 0x0F) << 2) | ((p[n * 3 + 2] & 0xC0) >> 6);
      d = p[n * 3 + 2] & 0x3F;

      // Map each 3-byte block to 4 printable characters using the Base64url character set
      output[n * 4] = base64urlEncTable[a];
      output[n * 4 + 1] = base64urlEncTable[b];
      output[n * 4 + 2] = base64urlEncTable[c];
      output[n * 4 + 3] = base64urlEncTable[d];
    }
  }

  return output;
}

/**
 * @brief Base64url decoding algorithm
 * @param[in] input Base64url-encoded string
 * @param[in] inputLen Length of the encoded string
 * @param[out] output Resulting decoded data
 * @param[out] outputLen Length of the decoded data
 * @return Error code
 **/

bool
Base64Url::decode(const char *input, size_t inputLen, void *output, size_t *outputLen)
{
  int error;
  uint32_t value;
  unsigned int c;
  size_t i;
  size_t n;
  uint8_t *p;

  //Check parameters
  if(input == NULL && inputLen != 0)
    return false;
  if(outputLen == NULL)
    return false;

  //Check the length of the input string
  if((inputLen % 4) == 1)
    return false;

  //Initialize status code
  error = 0;

  //Point to the buffer where to write the decoded data
  p = (uint8_t *) output;

  //Initialize variables
  n = 0;
  value = 0;

  //Process the Base64url-encoded string
  for(i = 0; i < inputLen && !error; i++)
    {
      //Get current character
      c = (unsigned int) input[i];

      //Check the value of the current character
      if(c < 128 && base64urlDecTable[c] < 64)
	{
          //Decode the current character
          value = (value << 6) | base64urlDecTable[c];

          //Divide the input stream into blocks of 4 characters
          if((i % 4) == 3)
	    {
	      //Map each 4-character block to 3 bytes
	      if(p != NULL)
		{
		  p[n] = (value >> 16) & 0xFF;
		  p[n + 1] = (value >> 8) & 0xFF;
		  p[n + 2] = value & 0xFF;
		}

	      //Adjust the length of the decoded data
	      n += 3;
	      //Decode next block
	      value = 0;
	    }
	}
      else
	{
          //Implementations must reject the encoded data if it contains
          //characters outside the base alphabet
          error = 1;
	}
    }

  //Check status code
  if(!error)
    {
      //All trailing pad characters are omitted in Base64url
      if((inputLen % 4) == 2)
	{
          //The last block contains only 1 byte
          if(p != NULL)
	    {
	      //Decode the last byte
	      p[n] = (value >> 4) & 0xFF;
	    }

          //Adjust the length of the decoded data
          n++;
	}
      else if((inputLen % 4) == 3)
	{
          //The last block contains only 2 bytes
          if(p != NULL)
	    {
	      //Decode the last two bytes
	      p[n] = (value >> 10) & 0xFF;
	      p[n + 1] = (value >> 2) & 0xFF;
	    }

          //Adjust the length of the decoded data
          n += 2;
	}
      else
	{
          //No pad characters in this case
	}
    }

  //Total number of bytes that have been written
  *outputLen = n;

  //Return status code
  return !error;
}

long long
Base64Url::decode_id(const std::string & s) {
  unsigned char * bytes = new unsigned char[4 * s.size()];
  size_t len = 0;
  decode(s.c_str(), s.size(), bytes, &len);

  long long id = (long long)bytes[0] + ((long long)bytes[1] << 8) + ((long long)bytes[2] << 16) + ((long long)bytes[3] << 24) + ((long long)bytes[4] << 32) + ((long long)bytes[5] << 40) + ((long long)bytes[6] << 48) + ((long long)bytes[7] << 56);
  delete[] bytes;
  return id;
}

std::string
Base64Url::encode_id(long long id) {
  unsigned char bytes[8];
  bytes[0] = id & 0xff;
  bytes[1] = (id >> 8) & 0xff;
  bytes[2] = (id >> 16) & 0xff;
  bytes[3] = (id >> 24) & 0xff;
  bytes[4] = (id >> 32) & 0xff;
  bytes[5] = (id >> 40) & 0xff;
  bytes[6] = (id >> 48) & 0xff;
  bytes[7] = (id >> 56) & 0xff;

  return encode(bytes, 8);
}

std::string
Base64Url::encode_id_bigendian(long long id) {
  unsigned char bytes[8];
  bytes[7] = id & 0xff;
  bytes[6] = (id >> 8) & 0xff;
  bytes[5] = (id >> 16) & 0xff;
  bytes[4] = (id >> 24) & 0xff;
  bytes[3] = (id >> 32) & 0xff;
  bytes[2] = (id >> 40) & 0xff;
  bytes[1] = (id >> 48) & 0xff;
  bytes[0] = (id >> 56) & 0xff;

  return encode(bytes, 8);
}
<|endoftext|>


<commit_before>/*
 *  Copyright 2013 The OpenMx Project
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *   Unless required by applicable law or agreed to in writing, software
 *   distributed under the License is distributed on an "AS IS" BASIS,
 *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */

#include "omxDefines.h"
#include "omxState.h"
#include "omxFitFunction.h"
#include "omxNPSOLSpecific.h"
#include "omxExportBackendState.h"
#include "omxCsolnp.h"
#include "nloptcpp.h"
#include "Compute.h"
#include "npsolswitch.h"
#include "glue.h"

enum OptEngine {
	OptEngine_NPSOL,
	OptEngine_CSOLNP,
    OptEngine_NLOPT
};

class ComputeGDBase : public omxCompute {
protected:
	typedef omxCompute super;
	enum OptEngine engine;
	omxMatrix *fitMatrix;
	int verbose;
	double optimalityTolerance;

	virtual void initFromFrontend(omxState *, SEXP rObj);
};

class omxComputeGD : public ComputeGDBase {
	typedef ComputeGDBase super;
	bool useGradient;
	SEXP hessChol;

	int warmStartSize;
	double *warmStart;

public:
	omxComputeGD();
	virtual void initFromFrontend(omxState *, SEXP rObj);
	virtual void computeImpl(FitContext *fc);
	virtual void reportResults(FitContext *fc, MxRList *slots, MxRList *out);
};

class omxCompute *newComputeGradientDescent()
{
	return new omxComputeGD();
}

class ComputeCI : public ComputeGDBase {
	typedef ComputeGDBase super;
	SEXP intervals, intervalCodes;

public:
	ComputeCI();
	virtual void initFromFrontend(omxState *, SEXP rObj);
	virtual void computeImpl(FitContext *fc);
	virtual void reportResults(FitContext *fc, MxRList *slots, MxRList *out);
};

omxCompute *newComputeConfidenceInterval()
{
	return new ComputeCI();
}

omxComputeGD::omxComputeGD()
{
	hessChol = NULL;
	warmStart = NULL;
}

void ComputeGDBase::initFromFrontend(omxState *globalState, SEXP rObj)
{
	super::initFromFrontend(globalState, rObj);

	SEXP slotValue;
	fitMatrix = omxNewMatrixFromSlot(rObj, globalState, "fitfunction");
	setFreeVarGroup(fitMatrix->fitFunction, varGroup);
	omxCompleteFitFunction(fitMatrix);

	ScopedProtect p1(slotValue, R_do_slot(rObj, Rf_install("verbose")));
	verbose = Rf_asInteger(slotValue);

	ScopedProtect p2(slotValue, R_do_slot(rObj, Rf_install("tolerance")));
	optimalityTolerance = Rf_asReal(slotValue);

	ScopedProtect p3(slotValue, R_do_slot(rObj, Rf_install("engine")));
	const char *engine_name = CHAR(Rf_asChar(slotValue));
	if (strEQ(engine_name, "CSOLNP")) {
		engine = OptEngine_CSOLNP;
	} else if (strEQ(engine_name, "NLOPT")) {
#ifdef HAS_NLOPT
		engine = OptEngine_NLOPT;
#else
		Rf_error("NLOPT is not available in this build");
#endif
	} else if (strEQ(engine_name, "NPSOL")) {
#if HAS_NPSOL
		engine = OptEngine_NPSOL;
#else
		Rf_error("NPSOL is not available in this build");
#endif
	} else {
		Rf_error("%s: engine %s unknown", name, engine_name);
	}
}

void omxComputeGD::initFromFrontend(omxState *globalState, SEXP rObj)
{
	super::initFromFrontend(globalState, rObj);

	SEXP slotValue;
	ScopedProtect p1(slotValue, R_do_slot(rObj, Rf_install("useGradient")));
	if (Rf_length(slotValue)) {
		useGradient = Rf_asLogical(slotValue);
	} else {
		useGradient = Global->analyticGradients;
	}

	ScopedProtect p2(slotValue, R_do_slot(rObj, Rf_install("warmStart")));
	if (!Rf_isNull(slotValue)) {
		SEXP matrixDims;
		Rf_protect(matrixDims = Rf_getAttrib(slotValue, R_DimSymbol));
		int *dimList = INTEGER(matrixDims);
		int rows = dimList[0];
		int cols = dimList[1];
		if (rows != cols) Rf_error("%s: warmStart matrix must be square", name);

		warmStartSize = rows;
		warmStart = REAL(slotValue);
	}
}

void omxComputeGD::computeImpl(FitContext *fc)
{
    size_t numParam = varGroup->vars.size();
	if (numParam <= 0) {
		omxRaiseErrorf("%s: model has no free parameters", name);
		return;
	}

	omxFitFunctionCompute(fitMatrix->fitFunction, FF_COMPUTE_PREOPTIMIZE, fc);

	fc->createChildren();

	switch (engine) {
        case OptEngine_NPSOL:{
#if HAS_NPSOL
		if (!hessChol) {
			Rf_protect(hessChol = Rf_allocMatrix(REALSXP, numParam, numParam));
		}
		bool doWarm = false;
		if (warmStart) {
			if (warmStartSize != int(numParam)) {
				Rf_warning("%s: warmStart size %d does not match number of free parameters %d (ignored)",
					   warmStartSize, numParam);
			} else {
				memcpy(REAL(hessChol), warmStart, sizeof(double) * numParam * numParam);
				doWarm = true;
			}
		}
		omxInvokeNPSOL(fitMatrix, fc, &fc->inform, useGradient, varGroup, verbose,
			       REAL(hessChol), optimalityTolerance, doWarm);
		Eigen::Map<Eigen::MatrixXd> hc(REAL(hessChol), numParam, numParam);
		Eigen::MatrixXd hcT = hc.transpose();
		Eigen::Map<Eigen::MatrixXd> dest(fc->getDenseHessUninitialized(), numParam, numParam);
		dest.noalias() = hcT * hc;
#endif
		break;}
        case OptEngine_CSOLNP:
            omxInvokeCSOLNP(fitMatrix, fc, &fc->inform, varGroup, verbose,
			    fc->getDenseHessUninitialized(), optimalityTolerance);
	    break;
#ifdef HAS_NLOPT
        case OptEngine_NLOPT:
            omxInvokeNLOPTorSANN(fitMatrix, fc, &fc->inform, varGroup, verbose,
                fc->getDenseHessUninitialized(), optimalityTolerance);
        break;
#endif
        default: Rf_error("Optimizer %d is not available", engine);
	}
	fc->wanted |= FF_COMPUTE_GRADIENT | FF_COMPUTE_HESSIAN;

	// It seems we cannot avoid this. Both optimizers can terminate
	// with fit not at the optimum.
	ComputeFit(fitMatrix, FF_COMPUTE_FIT, fc);

	if (fitMatrix->rows == 1) {
		if (!std::isfinite(fc->fit) || fc->fit == 1e24) {  // remove magic number 1e24 TODO
			std::string diag = fc->getIterationError();
			omxRaiseErrorf("MxComputeGradientDescent: fitfunction %s evaluated to %f (%s)",
				       fitMatrix->name, fc->fit, diag.c_str());
			return;
		}
	}

	fc->wanted |= FF_COMPUTE_BESTFIT;
    /*printf("fc->hess in computeGD\n");
    printf("%2f", fc->hess[0]); putchar('\n');
    printf("%2f", fc->hess[1]); putchar('\n');
    printf("%2f", fc->hess[2]); putchar('\n');
    */
}

void omxComputeGD::reportResults(FitContext *fc, MxRList *slots, MxRList *out)
{
	omxPopulateFitFunction(fitMatrix, out);

    /*printf("fc->hess in computeGD:report results\n");
    printf("%2f", fc->hess[0]); putchar('\n');
    printf("%2f", fc->hess[1]); putchar('\n');
    printf("%2f", fc->hess[2]); putchar('\n');
*/
	if (engine == OptEngine_NPSOL) {
		out->add("hessianCholesky", hessChol);
	}
}

ComputeCI::ComputeCI()
{
	intervals = 0;
	intervalCodes = 0;
}

void ComputeCI::initFromFrontend(omxState *globalState, SEXP rObj)
{
	super::initFromFrontend(globalState, rObj);
}

void ComputeCI::computeImpl(FitContext *fc)
{
	Global->unpackConfidenceIntervals();

	int numInts = (int) Global->intervalList.size();
	if (verbose >= 1) mxLog("%s: starting work on %d intervals", name, numInts);
	if (!numInts) return;

	// I'm not sure why INFORM_NOT_AT_OPTIMUM is okay, but that's how it was.
	if (fc->inform >= INFORM_LINEAR_CONSTRAINTS_INFEASIBLE && fc->inform != INFORM_NOT_AT_OPTIMUM) {
		// TODO: allow forcing
		Rf_warning("Not calculating confidence intervals because of optimizer status %d", fc->inform);
		return;
	}

	Eigen::ArrayXd mle(fc->numParam);
	memcpy(mle.data(), fc->est, sizeof(double) * fc->numParam);

	Rf_protect(intervals = Rf_allocMatrix(REALSXP, numInts, 3));
	Rf_protect(intervalCodes = Rf_allocMatrix(INTSXP, numInts, 2));

	switch (engine) {
	case OptEngine_NPSOL:
#if HAS_NPSOL
		omxNPSOLConfidenceIntervals(fitMatrix, fc, optimalityTolerance);
#endif
		break;
	case OptEngine_CSOLNP:
		omxCSOLNPConfidenceIntervals(fitMatrix, fc, verbose, optimalityTolerance);
		break;
#ifdef HAS_NLOPT
    case OptEngine_NLOPT:
        omxNLOPTorSANNConfidenceIntervals(fitMatrix, fc, optimalityTolerance);
        break;
#endif
	default:
		Rf_error("huh?");
	}

	if(OMX_DEBUG) { mxLog("Populating CIs for %d fit functions.", numInts); }

	memcpy(fc->est, mle.data(), sizeof(double) * fc->numParam);
	fc->copyParamToModel();

	Eigen::Map< Eigen::ArrayXXd > interval(REAL(intervals), numInts, 3);
	int* intervalCode = INTEGER(intervalCodes);
	for(int j = 0; j < numInts; j++) {
		omxConfidenceInterval *oCI = Global->intervalList[j];
		interval(j, 0) = oCI->min;
		omxRecompute(oCI->matrix, FF_COMPUTE_FIT, fc);
		interval(j, 1) = omxMatrixElement(oCI->matrix, oCI->row, oCI->col);
		interval(j, 2) = oCI->max;
		intervalCode[j] = oCI->lCode;
		intervalCode[j + numInts] = oCI->uCode;
	}
}

void ComputeCI::reportResults(FitContext *fc, MxRList *slots, MxRList *out)
{
	if (!intervals) return;

	int numInt = (int) Global->intervalList.size();

	SEXP dimnames;
	SEXP names;
	Rf_protect(dimnames = Rf_allocVector(VECSXP, 2));
	Rf_protect(names = Rf_allocVector(STRSXP, 3));
	SET_STRING_ELT(names, 0, Rf_mkChar("lbound"));
	SET_STRING_ELT(names, 1, Rf_mkChar("estimate"));
	SET_STRING_ELT(names, 2, Rf_mkChar("ubound"));
	SET_VECTOR_ELT(dimnames, 1, names);

	Rf_protect(names = Rf_allocVector(STRSXP, numInt)); //shared between the two matrices
	for (int nx=0; nx < numInt; ++nx) {
		omxConfidenceInterval *ci = Global->intervalList[nx];
		SET_STRING_ELT(names, nx, Rf_mkChar(ci->name));
	}
	SET_VECTOR_ELT(dimnames, 0, names);

	Rf_setAttrib(intervals, R_DimNamesSymbol, dimnames);

	out->add("confidenceIntervals", intervals);

	Rf_protect(dimnames = Rf_allocVector(VECSXP, 2));
	SET_VECTOR_ELT(dimnames, 0, names);

	Rf_protect(names = Rf_allocVector(STRSXP, 2));
	SET_STRING_ELT(names, 0, Rf_mkChar("lbound"));
	SET_STRING_ELT(names, 1, Rf_mkChar("ubound"));
	SET_VECTOR_ELT(dimnames, 1, names);

	Rf_setAttrib(intervalCodes, R_DimNamesSymbol, dimnames);

	out->add("confidenceIntervalCodes", intervalCodes);
}
<commit_msg>(Re?)enable parallel processing for confidence intervals<commit_after>/*
 *  Copyright 2013 The OpenMx Project
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *   Unless required by applicable law or agreed to in writing, software
 *   distributed under the License is distributed on an "AS IS" BASIS,
 *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */

#include "omxDefines.h"
#include "omxState.h"
#include "omxFitFunction.h"
#include "omxNPSOLSpecific.h"
#include "omxExportBackendState.h"
#include "omxCsolnp.h"
#include "nloptcpp.h"
#include "Compute.h"
#include "npsolswitch.h"
#include "glue.h"

enum OptEngine {
	OptEngine_NPSOL,
	OptEngine_CSOLNP,
    OptEngine_NLOPT
};

class ComputeGDBase : public omxCompute {
protected:
	typedef omxCompute super;
	enum OptEngine engine;
	omxMatrix *fitMatrix;
	int verbose;
	double optimalityTolerance;

	virtual void initFromFrontend(omxState *, SEXP rObj);
};

class omxComputeGD : public ComputeGDBase {
	typedef ComputeGDBase super;
	bool useGradient;
	SEXP hessChol;

	int warmStartSize;
	double *warmStart;

public:
	omxComputeGD();
	virtual void initFromFrontend(omxState *, SEXP rObj);
	virtual void computeImpl(FitContext *fc);
	virtual void reportResults(FitContext *fc, MxRList *slots, MxRList *out);
};

class omxCompute *newComputeGradientDescent()
{
	return new omxComputeGD();
}

class ComputeCI : public ComputeGDBase {
	typedef ComputeGDBase super;
	SEXP intervals, intervalCodes;

public:
	ComputeCI();
	virtual void initFromFrontend(omxState *, SEXP rObj);
	virtual void computeImpl(FitContext *fc);
	virtual void reportResults(FitContext *fc, MxRList *slots, MxRList *out);
};

omxCompute *newComputeConfidenceInterval()
{
	return new ComputeCI();
}

omxComputeGD::omxComputeGD()
{
	hessChol = NULL;
	warmStart = NULL;
}

void ComputeGDBase::initFromFrontend(omxState *globalState, SEXP rObj)
{
	super::initFromFrontend(globalState, rObj);

	SEXP slotValue;
	fitMatrix = omxNewMatrixFromSlot(rObj, globalState, "fitfunction");
	setFreeVarGroup(fitMatrix->fitFunction, varGroup);
	omxCompleteFitFunction(fitMatrix);

	ScopedProtect p1(slotValue, R_do_slot(rObj, Rf_install("verbose")));
	verbose = Rf_asInteger(slotValue);

	ScopedProtect p2(slotValue, R_do_slot(rObj, Rf_install("tolerance")));
	optimalityTolerance = Rf_asReal(slotValue);

	ScopedProtect p3(slotValue, R_do_slot(rObj, Rf_install("engine")));
	const char *engine_name = CHAR(Rf_asChar(slotValue));
	if (strEQ(engine_name, "CSOLNP")) {
		engine = OptEngine_CSOLNP;
	} else if (strEQ(engine_name, "NLOPT")) {
#ifdef HAS_NLOPT
		engine = OptEngine_NLOPT;
#else
		Rf_error("NLOPT is not available in this build");
#endif
	} else if (strEQ(engine_name, "NPSOL")) {
#if HAS_NPSOL
		engine = OptEngine_NPSOL;
#else
		Rf_error("NPSOL is not available in this build");
#endif
	} else {
		Rf_error("%s: engine %s unknown", name, engine_name);
	}
}

void omxComputeGD::initFromFrontend(omxState *globalState, SEXP rObj)
{
	super::initFromFrontend(globalState, rObj);

	SEXP slotValue;
	ScopedProtect p1(slotValue, R_do_slot(rObj, Rf_install("useGradient")));
	if (Rf_length(slotValue)) {
		useGradient = Rf_asLogical(slotValue);
	} else {
		useGradient = Global->analyticGradients;
	}

	ScopedProtect p2(slotValue, R_do_slot(rObj, Rf_install("warmStart")));
	if (!Rf_isNull(slotValue)) {
		SEXP matrixDims;
		Rf_protect(matrixDims = Rf_getAttrib(slotValue, R_DimSymbol));
		int *dimList = INTEGER(matrixDims);
		int rows = dimList[0];
		int cols = dimList[1];
		if (rows != cols) Rf_error("%s: warmStart matrix must be square", name);

		warmStartSize = rows;
		warmStart = REAL(slotValue);
	}
}

void omxComputeGD::computeImpl(FitContext *fc)
{
    size_t numParam = varGroup->vars.size();
	if (numParam <= 0) {
		omxRaiseErrorf("%s: model has no free parameters", name);
		return;
	}

	omxFitFunctionCompute(fitMatrix->fitFunction, FF_COMPUTE_PREOPTIMIZE, fc);

	fc->createChildren();

	switch (engine) {
        case OptEngine_NPSOL:{
#if HAS_NPSOL
		if (!hessChol) {
			Rf_protect(hessChol = Rf_allocMatrix(REALSXP, numParam, numParam));
		}
		bool doWarm = false;
		if (warmStart) {
			if (warmStartSize != int(numParam)) {
				Rf_warning("%s: warmStart size %d does not match number of free parameters %d (ignored)",
					   warmStartSize, numParam);
			} else {
				memcpy(REAL(hessChol), warmStart, sizeof(double) * numParam * numParam);
				doWarm = true;
			}
		}
		omxInvokeNPSOL(fitMatrix, fc, &fc->inform, useGradient, varGroup, verbose,
			       REAL(hessChol), optimalityTolerance, doWarm);
		Eigen::Map<Eigen::MatrixXd> hc(REAL(hessChol), numParam, numParam);
		Eigen::MatrixXd hcT = hc.transpose();
		Eigen::Map<Eigen::MatrixXd> dest(fc->getDenseHessUninitialized(), numParam, numParam);
		dest.noalias() = hcT * hc;
#endif
		break;}
        case OptEngine_CSOLNP:
            omxInvokeCSOLNP(fitMatrix, fc, &fc->inform, varGroup, verbose,
			    fc->getDenseHessUninitialized(), optimalityTolerance);
	    break;
#ifdef HAS_NLOPT
        case OptEngine_NLOPT:
            omxInvokeNLOPTorSANN(fitMatrix, fc, &fc->inform, varGroup, verbose,
                fc->getDenseHessUninitialized(), optimalityTolerance);
        break;
#endif
        default: Rf_error("Optimizer %d is not available", engine);
	}
	fc->wanted |= FF_COMPUTE_GRADIENT | FF_COMPUTE_HESSIAN;

	// It seems we cannot avoid this. Both optimizers can terminate
	// with fit not at the optimum.
	ComputeFit(fitMatrix, FF_COMPUTE_FIT, fc);

	if (fitMatrix->rows == 1) {
		if (!std::isfinite(fc->fit) || fc->fit == 1e24) {  // remove magic number 1e24 TODO
			std::string diag = fc->getIterationError();
			omxRaiseErrorf("MxComputeGradientDescent: fitfunction %s evaluated to %f (%s)",
				       fitMatrix->name, fc->fit, diag.c_str());
			return;
		}
	}

	fc->wanted |= FF_COMPUTE_BESTFIT;
    /*printf("fc->hess in computeGD\n");
    printf("%2f", fc->hess[0]); putchar('\n');
    printf("%2f", fc->hess[1]); putchar('\n');
    printf("%2f", fc->hess[2]); putchar('\n');
    */
}

void omxComputeGD::reportResults(FitContext *fc, MxRList *slots, MxRList *out)
{
	omxPopulateFitFunction(fitMatrix, out);

    /*printf("fc->hess in computeGD:report results\n");
    printf("%2f", fc->hess[0]); putchar('\n');
    printf("%2f", fc->hess[1]); putchar('\n');
    printf("%2f", fc->hess[2]); putchar('\n');
*/
	if (engine == OptEngine_NPSOL) {
		out->add("hessianCholesky", hessChol);
	}
}

ComputeCI::ComputeCI()
{
	intervals = 0;
	intervalCodes = 0;
}

void ComputeCI::initFromFrontend(omxState *globalState, SEXP rObj)
{
	super::initFromFrontend(globalState, rObj);
}

void ComputeCI::computeImpl(FitContext *fc)
{
	Global->unpackConfidenceIntervals();

	int numInts = (int) Global->intervalList.size();
	if (verbose >= 1) mxLog("%s: starting work on %d intervals", name, numInts);
	if (!numInts) return;

	// I'm not sure why INFORM_NOT_AT_OPTIMUM is okay, but that's how it was.
	if (fc->inform >= INFORM_LINEAR_CONSTRAINTS_INFEASIBLE && fc->inform != INFORM_NOT_AT_OPTIMUM) {
		// TODO: allow forcing
		Rf_warning("Not calculating confidence intervals because of optimizer status %d", fc->inform);
		return;
	}

	Eigen::ArrayXd mle(fc->numParam);
	memcpy(mle.data(), fc->est, sizeof(double) * fc->numParam);

	Rf_protect(intervals = Rf_allocMatrix(REALSXP, numInts, 3));
	Rf_protect(intervalCodes = Rf_allocMatrix(INTSXP, numInts, 2));

	fc->createChildren();

	switch (engine) {
	case OptEngine_NPSOL:
#if HAS_NPSOL
		omxNPSOLConfidenceIntervals(fitMatrix, fc, optimalityTolerance);
#endif
		break;
	case OptEngine_CSOLNP:
		omxCSOLNPConfidenceIntervals(fitMatrix, fc, verbose, optimalityTolerance);
		break;
#ifdef HAS_NLOPT
    case OptEngine_NLOPT:
        omxNLOPTorSANNConfidenceIntervals(fitMatrix, fc, optimalityTolerance);
        break;
#endif
	default:
		Rf_error("huh?");
	}

	if(OMX_DEBUG) { mxLog("Populating CIs for %d fit functions.", numInts); }

	memcpy(fc->est, mle.data(), sizeof(double) * fc->numParam);
	fc->copyParamToModel();

	Eigen::Map< Eigen::ArrayXXd > interval(REAL(intervals), numInts, 3);
	int* intervalCode = INTEGER(intervalCodes);
	for(int j = 0; j < numInts; j++) {
		omxConfidenceInterval *oCI = Global->intervalList[j];
		interval(j, 0) = oCI->min;
		omxRecompute(oCI->matrix, FF_COMPUTE_FIT, fc);
		interval(j, 1) = omxMatrixElement(oCI->matrix, oCI->row, oCI->col);
		interval(j, 2) = oCI->max;
		intervalCode[j] = oCI->lCode;
		intervalCode[j + numInts] = oCI->uCode;
	}
}

void ComputeCI::reportResults(FitContext *fc, MxRList *slots, MxRList *out)
{
	if (!intervals) return;

	int numInt = (int) Global->intervalList.size();

	SEXP dimnames;
	SEXP names;
	Rf_protect(dimnames = Rf_allocVector(VECSXP, 2));
	Rf_protect(names = Rf_allocVector(STRSXP, 3));
	SET_STRING_ELT(names, 0, Rf_mkChar("lbound"));
	SET_STRING_ELT(names, 1, Rf_mkChar("estimate"));
	SET_STRING_ELT(names, 2, Rf_mkChar("ubound"));
	SET_VECTOR_ELT(dimnames, 1, names);

	Rf_protect(names = Rf_allocVector(STRSXP, numInt)); //shared between the two matrices
	for (int nx=0; nx < numInt; ++nx) {
		omxConfidenceInterval *ci = Global->intervalList[nx];
		SET_STRING_ELT(names, nx, Rf_mkChar(ci->name));
	}
	SET_VECTOR_ELT(dimnames, 0, names);

	Rf_setAttrib(intervals, R_DimNamesSymbol, dimnames);

	out->add("confidenceIntervals", intervals);

	Rf_protect(dimnames = Rf_allocVector(VECSXP, 2));
	SET_VECTOR_ELT(dimnames, 0, names);

	Rf_protect(names = Rf_allocVector(STRSXP, 2));
	SET_STRING_ELT(names, 0, Rf_mkChar("lbound"));
	SET_STRING_ELT(names, 1, Rf_mkChar("ubound"));
	SET_VECTOR_ELT(dimnames, 1, names);

	Rf_setAttrib(intervalCodes, R_DimNamesSymbol, dimnames);

	out->add("confidenceIntervalCodes", intervalCodes);
}
<|endoftext|>


<commit_before>/************************************************************************/
/*                                                                      */
/*            Copyright 2011-2012 by Ullrich Koethe                     */
/*                                                                      */
/*    This file is part of the VIGRA computer vision library.           */
/*    The VIGRA Website is                                              */
/*        http://hci.iwr.uni-heidelberg.de/vigra/                       */
/*    Please direct questions, bug reports, and contributions to        */
/*        ullrich.koethe@iwr.uni-heidelberg.de    or                    */
/*        vigra@informatik.uni-hamburg.de                               */
/*                                                                      */
/*    Permission is hereby granted, free of charge, to any person       */
/*    obtaining a copy of this software and associated documentation    */
/*    files (the "Software"), to deal in the Software without           */
/*    restriction, including without limitation the rights to use,      */
/*    copy, modify, merge, publish, distribute, sublicense, and/or      */
/*    sell copies of the Software, and to permit persons to whom the    */
/*    Software is furnished to do so, subject to the following          */
/*    conditions:                                                       */
/*                                                                      */
/*    The above copyright notice and this permission notice shall be    */
/*    included in all copies or substantial portions of the             */
/*    Software.                                                         */
/*                                                                      */
/*    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND    */
/*    EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES   */
/*    OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND          */
/*    NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT       */
/*    HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,      */
/*    WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING      */
/*    FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR     */
/*    OTHER DEALINGS IN THE SOFTWARE.                                   */
/*                                                                      */
/************************************************************************/

#define PY_ARRAY_UNIQUE_SYMBOL vigranumpyanalysis_PyArray_API
#define NO_IMPORT_ARRAY

#include <vigra/numpy_array.hxx>
#include <vigra/numpy_array_converters.hxx>
#include <vigra/accumulator.hxx>
#include <vigra/timing.hxx>
#include <map>

namespace python = boost::python;

namespace vigra
{

namespace acc1
{

struct GetTagVisitor
{
    mutable python::object result;

    template <class TAG, class Accu>
    void exec(Accu & a) const
    {
        result = python::object(get<TAG>(a));
    }
};

template <class T>
struct InspectResultType
{
    typedef typename PromoteTraits<T, double>::Promote type;
};

template <class T, int N>
struct InspectResultType<TinyVector<T, N> >
{
    typedef TinyVector<typename PromoteTraits<T, double>::Promote, N> type;
};

template <class T>
struct InspectResultType<Singleband<T> >
{
    typedef typename PromoteTraits<T, double>::Promote type;
};

typedef std::map<std::string, std::string> AliasMap;

AliasMap createAliasMap()
{
    AliasMap res;
    res["DivideByCount<Central<PowerSum<2> > >"] = "Variance";
    res["DivideUnbiased<Central<PowerSum<2> > >"] = "UnbiasedVariance";
    res["DivideByCount<Principal<PowerSum<2> > >"] = "Principal<Variance>";
    res["DivideByCount<FlatScatterMatrix>"] = "Covariance";
    res["DivideByCount<PowerSum<1> >"] = "Mean";
    res["PowerSum<1>"] = "Sum";
    res["PowerSum<0>"] = "Count";
    res["StandardQuantiles<AutoRangeHistogram<100> >"] = "Quantiles";
    return res;
}

static AliasMap const & tagToAlias()
{
    static const AliasMap m = createAliasMap();
    return m;
}

AliasMap createInverseAliasMap()
{
    AliasMap res;
    for(AliasMap::const_iterator k = tagToAlias().begin(); k != tagToAlias().end(); ++k)
        res[normalizeString(k->second)] = normalizeString(k->first);
    return res;
}

static AliasMap const & aliasToTag()
{
    static const AliasMap m = createInverseAliasMap();
    return m;
}

static std::string const & resolveAlias(std::string const & n)
{
    AliasMap::const_iterator k = aliasToTag().find(n);
    if(k == aliasToTag().end())
        return n;
    else
        return k->second;
}

static std::string const & createAlias(std::string const & n)
{
    AliasMap::const_iterator k = tagToAlias().find(n);
    if(k == tagToAlias().end())
        return n;
    else
        return k->second;
}

template <class PixelType, class Accumulators>
struct PythonAccumulator
: public DynamicAccumulatorChain<PixelType, Accumulators>
{
    typedef DynamicAccumulatorChain<PixelType, Accumulators> BaseType;
    typedef typename BaseType::AccumulatorTags AccumulatorTags;

    void activate(std::string tag)
    {
        bool found = detail::ApplyVisitorToTag<AccumulatorTags>::exec((BaseType &)*this,
                                     resolveAlias(normalizeString(tag)), detail::ActivateTagVisitor());
        vigra_precondition(found, std::string("PythonAccumulator::activate(): Tag '") + tag + "' not found.");
    }

    python::list namesImpl(bool activeOnly) const
    {
        ArrayVector<std::string> a = BaseType::namesImpl(activeOnly);
        for(unsigned int k=0; k<a.size(); ++k)
            a[k] = createAlias(a[k]);

        std::sort(a.begin(), a.end());

        python::list result;
        for(unsigned int k=0; k<a.size(); ++k)
            result.append(python::object(a[k]));
        return result;
    }

    python::list activeNames() const
    {
        return namesImpl(true);
    }

    python::list names() const
    {
        return namesImpl(false);
    }

    python::object get(std::string tag)
    {
        GetTagVisitor v;

        bool found = detail::ApplyVisitorToTag<AccumulatorTags>::exec(*this,
                                           resolveAlias(normalizeString(tag)), v);
        vigra_precondition(found, std::string("PythonAccumulator::get(): Tag '") + tag + "' not found.");
        return v.result;
    }

    void mergeImpl(PythonAccumulator const & o)
    {
        this->merge(o);
    }
};

template <class Accumulators, unsigned int ndim, class T>
PythonAccumulator<typename InspectResultType<T>::type, Accumulators> *
pythonInspect(NumpyArray<ndim, T> in, python::object tags)
{
    typedef PythonAccumulator<typename InspectResultType<T>::type, Accumulators> Accu;

    std::auto_ptr<Accu> res(new Accu);

    if(python::len(tags) == 0)
    {
        res->activateAll();
    }
    else if(PyString_Check(tags.ptr()))
    {
        res->activate(python::extract<std::string>(tags)());
    }
    else
    {
        for(int k=0; k<python::len(tags); ++k)
        {
            res->activate(python::extract<std::string>(tags[k])());
        }
    }

    {
        PyAllowThreads _pythread;

        collectStatistics(in.begin(), in.end(), *res);
    }

    return res.release();
}

} // namespace acc1

template <class T, class Accumulators>
void definePythonAccumulator()
{
    using namespace python;

    docstring_options doc_options(true, true, false);

    typedef typename acc1::InspectResultType<T>::type ResultType;

    typedef acc1::PythonAccumulator<ResultType, Accumulators> Accu;
    class_<Accu>("Accumulator", python::no_init)
        .def("__getitem__", &Accu::get)
        .def("activeNames", &Accu::activeNames)
        .def("names", &Accu::names)
        .def("merge", &Accu::mergeImpl)
        ;

    def("extractFeatures", &acc1::pythonInspect<Accumulators, 2, T>,
          (arg("image"), arg("tags") = ""),
          return_value_policy<manage_new_object>());

    def("extractFeatures", &acc1::pythonInspect<Accumulators, 3, T>,
          (arg("volume"), arg("tags") = ""),
          return_value_policy<manage_new_object>());
}

void defineAccumulators()
{
    using namespace python;
    using namespace vigra::acc1;

    docstring_options doc_options(true, true, false);

    definePythonAccumulator<Singleband<float>,
                            Select<Count, Mean, Variance, Skewness, Kurtosis,
                                   UnbiasedVariance, UnbiasedSkewness, UnbiasedKurtosis,
                                   Minimum, Maximum, StandardQuantiles<AutoRangeHistogram<100> > > >();

    typedef Select<Count, Mean, Variance, Skewness, Kurtosis, Minimum, Maximum,
                   Covariance, Principal<Variance>, Principal<Skewness>, Principal<Kurtosis>
                   > VectorAccumulators;
    definePythonAccumulator<TinyVector<float, 2>, VectorAccumulators>();
    definePythonAccumulator<TinyVector<float, 3>, VectorAccumulators>();
    definePythonAccumulator<TinyVector<float, 4>, VectorAccumulators>();
}

} // namespace vigra
<commit_msg>first working Python version of region accumulators<commit_after>/************************************************************************/
/*                                                                      */
/*            Copyright 2011-2012 by Ullrich Koethe                     */
/*                                                                      */
/*    This file is part of the VIGRA computer vision library.           */
/*    The VIGRA Website is                                              */
/*        http://hci.iwr.uni-heidelberg.de/vigra/                       */
/*    Please direct questions, bug reports, and contributions to        */
/*        ullrich.koethe@iwr.uni-heidelberg.de    or                    */
/*        vigra@informatik.uni-hamburg.de                               */
/*                                                                      */
/*    Permission is hereby granted, free of charge, to any person       */
/*    obtaining a copy of this software and associated documentation    */
/*    files (the "Software"), to deal in the Software without           */
/*    restriction, including without limitation the rights to use,      */
/*    copy, modify, merge, publish, distribute, sublicense, and/or      */
/*    sell copies of the Software, and to permit persons to whom the    */
/*    Software is furnished to do so, subject to the following          */
/*    conditions:                                                       */
/*                                                                      */
/*    The above copyright notice and this permission notice shall be    */
/*    included in all copies or substantial portions of the             */
/*    Software.                                                         */
/*                                                                      */
/*    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND    */
/*    EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES   */
/*    OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND          */
/*    NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT       */
/*    HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,      */
/*    WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING      */
/*    FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR     */
/*    OTHER DEALINGS IN THE SOFTWARE.                                   */
/*                                                                      */
/************************************************************************/

#define PY_ARRAY_UNIQUE_SYMBOL vigranumpyanalysis_PyArray_API
#define NO_IMPORT_ARRAY

#ifdef _MSC_VER
#pragma warning (disable: 4503)
#endif

#include <vigra/numpy_array.hxx>
#include <vigra/numpy_array_converters.hxx>
#include <vigra/accumulator.hxx>
#include <vigra/timing.hxx>
#include <map>

namespace python = boost::python;

namespace vigra
{

namespace acc1
{

struct GetTag_Visitor
{
    mutable python::object result;

    template <class T>
    void to_python(T const & t) const
    {
        result = python::object(t);
    }

    template <class T, int N>
    void to_python(TinyVector<T, N> const & t) const
    {
        NumpyArray<1, T> a = NumpyArray<1, T>(Shape1(N));
        for(int k=0; k<N; ++k)
            a(k) = t[k];
        result = python::object(a);
    }

    template <class TAG, class Accu>
    void exec(Accu & a) const
    {
        to_python(get<TAG>(a));
    }
};

struct GetArrayTag_Visitor
: public GetTag_Visitor
{
    template <class TAG, class T, class Accu>
    struct ToPythonArray
    {
        static python::object exec(Accu & a)
        {
            unsigned int n = a.regionCount();
            Shape1 s(n);
            NumpyArray<1, T> res(s);

            for(unsigned int k=0; k<n; ++k)
                res(k) = get<TAG>(a, k);
            return python::object(res);
        }
    };

    template <class TAG, class T, int N, class Accu>
    struct ToPythonArray<TAG, TinyVector<T, N>, Accu>
    {
        static python::object exec(Accu & a)
        {
            unsigned int n = a.regionCount();
            Shape2 s(n, N);
            NumpyArray<2, T> res(s);

            for(unsigned int k=0; k<n; ++k)
                for(int j=0; j<N; ++j)
                    res(k, j) = get<TAG>(a, k)[j];
            return python::object(res);
        }
    };

    template <class TAG, class T, class Accu>
    struct ToPythonArray<TAG, Matrix<T>, Accu>
    {
        static python::object exec(Accu & a)
        {
            unsigned int n = a.regionCount();
            Shape2 m = get<TAG>(a, 0).shape();
            Shape3 s(n, m[0], m[1]);
            NumpyArray<3, T> res(s);

            for(unsigned int k=0; k<n; ++k)
                for(int j=0; j<m[0]; ++j)
                    for(int i=0; i<m[1]; ++i)
                        res(k, j, i) = get<TAG>(a, k)(j,i);
            return python::object(res);
        }
    };

    template <class TAG, class T, class Accu>
    struct ToPythonArray<TAG, Error__Attempt_to_access_inactive_statistic<T>, Accu>
    {
        static python::object exec(Accu & a)
        {
            return python::object();
        }
    };

    template <class TAG, class T, int N, class Accu>
    struct ToPythonArray<TAG, std::pair<TinyVector<T,N>, Matrix<T> >, Accu>
    {
        static python::object exec(Accu & a)
        {
            return python::object();
        }
    };

    template <class TAG, class Accu>
    void exec(Accu & a) const
    {
        exec(a, (TAG *)0);
    }

    template <class Accu, class TAG>
    void exec(Accu & a, TAG *) const
    {
        this->result = ToPythonArray<TAG, typename LookupTag<TAG, Accu>::value_type, Accu>::exec(a);
    }

    template <class Accu, class TAG>
    void exec(Accu & a, Global<TAG> *) const
    {
        to_python(get<Global<TAG> >(a));
    }

};

template <class T>
struct AccumulatorValueTypeTraits
{
    // typedef typename PromoteTraits<T, double>::Promote type;
    typedef T type;
};

// template <class T, int N>
// struct AccumulatorValueTypeTraits<TinyVector<T, N> >
// {
    // typedef TinyVector<typename PromoteTraits<T, double>::Promote, N> type;
// };

template <class T>
struct AccumulatorValueTypeTraits<Singleband<T> >
{
    // typedef typename PromoteTraits<T, double>::Promote type;
    typedef T type;
};

typedef std::map<std::string, std::string> AliasMap;

AliasMap defineAliasMap()
{
    AliasMap res;
    res["DivideByCount<Central<PowerSum<2> > >"] = "Variance";
    res["DivideUnbiased<Central<PowerSum<2> > >"] = "UnbiasedVariance";
    res["DivideByCount<Principal<PowerSum<2> > >"] = "Principal<Variance>";
    res["DivideByCount<FlatScatterMatrix>"] = "Covariance";
    res["DivideByCount<PowerSum<1> >"] = "Mean";
    res["PowerSum<1>"] = "Sum";
    res["PowerSum<0>"] = "Count";
    res["StandardQuantiles<AutoRangeHistogram<100> >"] = "Quantiles";
    res["StandardQuantiles<GlobalRangeHistogram<100> >"] = "Quantiles";
    res["Coord<DivideByCount<PowerSum<1> > >"] = "GeometricCenter";
    res["Coord<RootDivideByCount<Principal<PowerSum<2> > > >"] = "PrincipalRadii";
    res["Coord<Principal<CoordinateSystem> >"] = "PrincipalCoordSystem";
    res["Weighted<Coord<DivideByCount<PowerSum<1> > > >"] = "CenterOfMass";
    res["Weighted<Coord<DivideByCount<Principal<PowerSum<2> > > > >"] = "MomentsOfInertia";
    res["Weighted<Coord<Principal<CoordinateSystem> > >"] = "CoordSystemOfInertia";
    return res;
}

AliasMap createTagToAlias(ArrayVector<std::string> const & names)
{
    static const AliasMap aliases = defineAliasMap();
    AliasMap res;
    for(unsigned int k=0; k<names.size(); ++k)
    {
        AliasMap::const_iterator a = aliases.find(names[k]);
        if(a == aliases.end())
            res[names[k]] = names[k];
        else
            res[names[k]] = a->second;
    }
    return res;
}

AliasMap createAliasToTag(AliasMap const & tagToAlias)
{
    AliasMap res;
    for(AliasMap::const_iterator k = tagToAlias.begin(); k != tagToAlias.end(); ++k)
        res[normalizeString(k->second)] = normalizeString(k->first);
    return res;
}

template <class PixelType, class Accumulators>
struct PythonAccumulator
: public DynamicAccumulatorChain<PixelType, Accumulators>
{
    typedef DynamicAccumulatorChain<PixelType, Accumulators> BaseType;
    typedef typename BaseType::AccumulatorTags AccumulatorTags;

    void activate(std::string tag)
    {
        bool found = detail::ApplyVisitorToTag<AccumulatorTags>::exec((BaseType &)*this,
                                     resolveAlias(tag), detail::ActivateTag_Visitor());
        vigra_precondition(found, std::string("PythonAccumulator::activate(): Tag '") + tag + "' not found.");
    }

    python::list namesImpl(bool activeOnly) const
    {
        ArrayVector<std::string> a = BaseType::namesImpl(activeOnly);
        for(unsigned int k=0; k<a.size(); ++k)
            a[k] = createAlias(a[k]);

        std::sort(a.begin(), a.end());

        python::list result;
        for(unsigned int k=0; k<a.size(); ++k)
            result.append(python::object(a[k]));
        return result;
    }

    python::list activeNames() const
    {
        return namesImpl(true);
    }

    python::list names() const
    {
        return namesImpl(false);
    }

    python::object get(std::string tag)
    {
        GetTag_Visitor v;

        bool found = detail::ApplyVisitorToTag<AccumulatorTags>::exec(*this, resolveAlias(tag), v);
        vigra_precondition(found, std::string("PythonAccumulator::get(): Tag '") + tag + "' not found.");
        return v.result;
    }

    void merge(PythonAccumulator const & o)
    {
        BaseType::merge(o);
    }

    static std::string createAlias(std::string const & n)
    {
        AliasMap::const_iterator k = tagToAlias().find(n);
        if(k == tagToAlias().end())
            return n;
        else
            return k->second;
    }

    static std::string resolveAlias(std::string const & n)
    {
        AliasMap::const_iterator k = aliasToTag().find(normalizeString(n));
        if(k == aliasToTag().end())
            return n;
        else
            return k->second;
    }

    static AliasMap const & tagToAlias()
    {
        static const AliasMap a = createTagToAlias(tagNames());
        return a;
    }

    static AliasMap const & aliasToTag()
    {
        static const AliasMap a = createAliasToTag(tagToAlias());
        return a;
    }
};

template <class Accumulators, unsigned int ndim, class T>
PythonAccumulator<typename AccumulatorValueTypeTraits<T>::type, Accumulators> *
pythonInspect(NumpyArray<ndim, T> in, python::object tags)
{
    typedef PythonAccumulator<typename AccumulatorValueTypeTraits<T>::type, Accumulators> Accu;

    std::auto_ptr<Accu> res(new Accu);

    if(python::len(tags) == 0)
    {
        res->activateAll();
    }
    else if(PyString_Check(tags.ptr()))
    {
        res->activate(python::extract<std::string>(tags)());
    }
    else
    {
        for(int k=0; k<python::len(tags); ++k)
        {
            res->activate(python::extract<std::string>(tags[k])());
        }
    }

    {
        PyAllowThreads _pythread;

        collectStatistics(in.begin(), in.end(), *res);
    }

    return res.release();
}

template <class PixelType, class Accumulators>
struct PythonAccumulatorArray
: public DynamicAccumulatorChainArray<PixelType, Accumulators>
{
    typedef DynamicAccumulatorChainArray<PixelType, Accumulators> BaseType;
    typedef typename BaseType::AccumulatorTags AccumulatorTags;

    void activate(std::string tag)
    {
        bool found = detail::ApplyVisitorToTag<AccumulatorTags>::exec((BaseType &)*this,
                                             resolveAlias(tag), detail::ActivateTag_Visitor());
        vigra_precondition(found, std::string("PythonAccumulatorArray::activate(): Tag '") + tag + "' not found.");
    }

    python::list namesImpl(bool activeOnly) const
    {
        ArrayVector<std::string> a = BaseType::namesImpl(activeOnly);
        for(unsigned int k=0; k<a.size(); ++k)
            a[k] = createAlias(a[k]);

        std::sort(a.begin(), a.end());

        python::list result;
        for(unsigned int k=0; k<a.size(); ++k)
            result.append(python::object(a[k]));
        return result;
    }

    python::list activeNames() const
    {
        return namesImpl(true);
    }

    python::list names() const
    {
        ArrayVector<std::string> a;
        for(AliasMap::const_iterator k = tagToAlias().begin(); k != tagToAlias().end(); ++k)
            a.push_back(k->second);
        std::sort(a.begin(), a.end());

        python::list result;
        for(unsigned int k=0; k<a.size(); ++k)
            result.append(python::object(a[k]));
        return result;
    }

    static std::string createAlias(std::string const & n)
    {
        AliasMap::const_iterator k = tagToAlias().find(n);
        if(k == tagToAlias().end())
            return n;
        else
            return k->second;
    }

    static std::string resolveAlias(std::string const & n)
    {
        AliasMap::const_iterator k = aliasToTag().find(normalizeString(n));
        if(k == aliasToTag().end())
            return n;
        else
            return k->second;
    }

    static AliasMap const & tagToAlias()
    {
        static const AliasMap a = createTagToAlias(tagNames());
        return a;
    }

    static AliasMap const & aliasToTag()
    {
        static const AliasMap a = createAliasToTag(tagToAlias());
        return a;
    }

    python::object get(std::string tag)
    {
        GetArrayTag_Visitor v;

        bool found = detail::ApplyVisitorToTag<AccumulatorTags>::exec((BaseType &)*this, resolveAlias(tag), v);
        vigra_precondition(found, std::string("PythonAccumulatorArray::get(): Tag '") + tag + "' not found.");
        return v.result;
    }

    // void merge(PythonAccumulator const & o)
    // {
        // BaseType::merge(o);
    // }
};

template <class Accumulators, unsigned int ndim, class T>
PythonAccumulatorArray<typename CoupledIteratorType<ndim,
                                      typename AccumulatorValueTypeTraits<T>::type, npy_uint32>::type::value_type, Accumulators> *
pythonRegionInspect(NumpyArray<ndim, T> in,
                    NumpyArray<ndim, Singleband<npy_uint32> > labels,
                    python::object tags)
{
    typedef typename CoupledIteratorType<ndim, typename AccumulatorValueTypeTraits<T>::type, npy_uint32>::type Iterator;
    typedef Iterator::value_type Handle;
    typedef PythonAccumulatorArray<Handle, Accumulators> Accu;

    std::auto_ptr<Accu> res(new Accu);

    if(python::len(tags) == 0)
    {
        res->activateAll();
    }
    else if(PyString_Check(tags.ptr()))
    {
        res->activate(python::extract<std::string>(tags)());
    }
    else
    {
        for(int k=0; k<python::len(tags); ++k)
        {
            res->activate(python::extract<std::string>(tags[k])());
        }
    }

    {
        PyAllowThreads _pythread;

        Iterator i     = createCoupledIterator(in, labels),
                 end   = i.getEndIterator();
        collectStatistics(i, end, *res);
    }

    return res.release();
}

} // namespace acc1

template <class T, class Accumulators>
void definePythonAccumulator()
{
    using namespace python;

    docstring_options doc_options(true, true, false);

    typedef typename acc1::AccumulatorValueTypeTraits<T>::type ResultType;

    typedef acc1::PythonAccumulator<ResultType, Accumulators> Accu;
    class_<Accu>("Accumulator", python::no_init)
        .def("__getitem__", &Accu::get)
        .def("activeNames", &Accu::activeNames)
        .def("names", &Accu::names)
        .def("merge", &Accu::merge)
        ;

    def("extractFeatures", &acc1::pythonInspect<Accumulators, 2, T>,
          (arg("image"), arg("tags") = ""),
          return_value_policy<manage_new_object>());

    def("extractFeatures", &acc1::pythonInspect<Accumulators, 3, T>,
          (arg("volume"), arg("tags") = ""),
          return_value_policy<manage_new_object>());
}

template <class T, class Accumulators>
void definePythonAccumulatorArray()
{
    using namespace python;

    docstring_options doc_options(true, true, false);

    typedef typename acc1::AccumulatorValueTypeTraits<T>::type ResultType;
    typedef typename CoupledIteratorType<2, ResultType, npy_uint32>::type Iterator;
    typedef Iterator::value_type Handle;

    typedef acc1::PythonAccumulatorArray<Handle, Accumulators> Accu;
    class_<Accu>("AccumulatorArray2", python::no_init)
        .def("__getitem__", &Accu::get)
        .def("activeNames", &Accu::activeNames)
        .def("names", &Accu::names)
//        .def("merge", &Accu::merge)
        ;

    def("extractRegionFeatures", &acc1::pythonRegionInspect<Accumulators, 2, T>,
          (arg("image"), arg("labels"), arg("tags") = ""),
          return_value_policy<manage_new_object>());

    // def("extractFeatures", &acc1::pythonInspect<Accumulators, 3, T>,
          // (arg("volume"), arg("tags") = ""),
          // return_value_policy<manage_new_object>());
}

void defineAccumulators()
{
    using namespace python;
    using namespace vigra::acc1;

    docstring_options doc_options(true, true, false);

    NumpyArrayConverter<NumpyArray<1, float> >();
    NumpyArrayConverter<NumpyArray<2, MultiArrayIndex> >();
    NumpyArrayConverter<NumpyArray<3, double> >();

    typedef Select<Count, Mean, Variance, Skewness, Kurtosis,
                   UnbiasedVariance, UnbiasedSkewness, UnbiasedKurtosis,
                   Minimum, Maximum, StandardQuantiles<AutoRangeHistogram<100> >
                   > ScalarAccumulators;
    definePythonAccumulator<Singleband<float>, ScalarAccumulators>();

    typedef Select<Count, Mean, Variance, Skewness, Kurtosis,
                   Covariance, Principal<Variance>, Principal<Skewness>, Principal<Kurtosis>,
                   Minimum, Maximum, Principal<Minimum>, Principal<Maximum>
                   > VectorAccumulators;
    definePythonAccumulator<TinyVector<float, 2>, VectorAccumulators>();
    definePythonAccumulator<TinyVector<float, 3>, VectorAccumulators>();
    definePythonAccumulator<TinyVector<float, 4>, VectorAccumulators>();

    typedef Select<Count, Mean, Variance, Skewness, Kurtosis,
                   Minimum, Maximum, StandardQuantiles<GlobalRangeHistogram<100> >,
                   GeometricCenter, PrincipalRadii, PrincipalCoordSystem,
                   CenterOfMass, MomentsOfInertia, CoordSystemOfInertia,
                   Coord<ArgMinWeight>, Coord<ArgMaxWeight>,
                   DataArg<1>, WeightArg<1>, LabelArg<2>
                   > ScalarRegionAccumulators;
    // typedef Select<Coord<ArgMinWeight>,
                   // DataArg<1>, WeightArg<1>, LabelArg<2>
                   // > ScalarRegionAccumulators;
    definePythonAccumulatorArray<Singleband<float>, ScalarRegionAccumulators>();
}

} // namespace vigra
<|endoftext|>


<commit_before>
#include <boost/thread/tss.hpp>
#include "vtrc-thread.h"

#include <google/protobuf/message.h>
#include <google/protobuf/descriptor.h>

#include "vtrc-atomic.h"
#include "vtrc-mutex.h"
#include "vtrc-mutex-typedefs.h"

#include "vtrc-protocol-layer.h"

#include "vtrc-data-queue.h"
#include "vtrc-hash-iface.h"
#include "vtrc-transformer-iface.h"

#include "vtrc-transport-iface.h"

#include "vtrc-condition-queues.h"
#include "vtrc-exception.h"
#include "vtrc-call-context.h"

#include "proto-helper/message-utilities.h"

#include "protocol/vtrc-rpc-lowlevel.pb.h"
#include "protocol/vtrc-errors.pb.h"

namespace vtrc { namespace common {

    namespace gpb = google::protobuf;

    namespace {

        typedef protocol_layer::message_queue_type message_queue_type;

        void raise_error( unsigned code )
        {
            throw vtrc::common::exception( code );
        }

        void raise_wait_error( wait_result_codes wr )
        {
            switch ( wr ) {
            case WAIT_RESULT_CANCELED:
                raise_error( vtrc_errors::ERR_CANCELED );
                break;
            case  WAIT_RESULT_TIMEOUT:
                raise_error( vtrc_errors::ERR_TIMEOUT );
                break;
            default:
                break;
            }
        }

        static const size_t maximum_message_length = 1024 * 1024;

        struct rpc_unit_index {
            gpb::uint64     id_;
            rpc_unit_index( gpb::uint64 id )
                :id_(id)
            { }
        };

        inline bool operator < ( const rpc_unit_index &lhs,
                                 const rpc_unit_index &rhs )
        {
            return lhs.id_ < rhs.id_;
        }

        typedef vtrc_rpc_lowlevel::lowlevel_unit ll_unit_type;
        typedef vtrc::shared_ptr<ll_unit_type>   ll_unit_sptr;

        typedef condition_queues<rpc_unit_index, ll_unit_sptr> rpc_queue_type;

        typedef boost::thread_specific_ptr<call_context> call_context_ptr;

        typedef std::map <
             const google::protobuf::MethodDescriptor *
            ,vtrc::shared_ptr<vtrc_rpc_lowlevel::options>
        > options_map_type;

    }

    struct protocol_layer::impl {

        transport_iface             *connection_;
        protocol_layer              *parent_;
        hasher_iface_sptr            hash_maker_;
        hasher_iface_sptr            hash_checker_;
        transformer_iface_sptr       transformer_;
        transformer_iface_sptr       reverter_;

        data_queue::queue_base_sptr  queue_;

        rpc_queue_type               rpc_queue_;
        vtrc::atomic<uint64_t>       rpc_index_;

        call_context_ptr             context_;

        options_map_type             options_map_;
        mutable vtrc::shared_mutex   options_map_lock_;

        impl( transport_iface *c )
            :connection_(c)
            ,hash_maker_(common::hash::create_default( ))
            ,hash_checker_(common::hash::create_default( ))
//            ,transformer_(common::transformers::erseefor::create( "1234", 4 ))
//            ,reverter_(common::transformers::erseefor::create( "1234", 4 ))
            ,transformer_(common::transformers::none::create( ))
            ,reverter_(common::transformers::none::create( ))
            ,queue_(data_queue::varint::create_parser(maximum_message_length))
            ,rpc_index_(100)
        {}

        std::string prepare_data( const char *data, size_t length)
        {
            /* here is:
             *  message =
             *  <packed_size(data_length+hash_length)><hash(data)><data>
            */
            std::string result(queue_->pack_size(
                                length + hash_maker_->hash_size( )));

            result.append( hash_maker_->get_data_hash(data, length ));
            result.append( data, data + length );

            /*
             * message = transform( message )
            */

            transformer_->transform(
                            result.empty( ) ? NULL : &result[0],
                                            result.size( ) );
            return result;
        }

        void process_data( const char *data, size_t length )
        {
            if( length > 0 ) {
                std::string next_data(data, data + length);

                const size_t old_size = queue_->messages( ).size( );

                /*
                 * message = revert( message )
                */
                reverter_->transform( &next_data[0], next_data.size( ) );
                queue_->append( &next_data[0], next_data.size( ));

                queue_->process( );

                if( queue_->messages( ).size( ) > old_size ) {
                    parent_->on_data_ready( );
                }
            }

        }

        void drop_first( )
        {
            queue_->messages( ).pop_front( );
        }

        void send_data( const char *data, size_t length )
        {
            connection_->write( data, length );
        }

        void send_message( const gpb::Message &message )
        {
            std::string ser(message.SerializeAsString( ));
            send_data( ser.c_str( ), ser.size( ) );
        }

        uint64_t next_index( )
        {
            return ++rpc_index_;
        }

        // --------------- sett ----------------- //

        message_queue_type &message_queue( )
        {
            return queue_->messages( );
        }

        const message_queue_type &message_queue( ) const
        {
            return queue_->messages( );
        }

        void parse_message( const std::string &mess,
                            google::protobuf::Message &result )
        {
            const size_t hash_length = hash_checker_->hash_size( );
            const size_t diff_len    = mess.size( ) - hash_length;
            result.ParseFromArray( mess.c_str( ) + hash_length, diff_len );
        }

        bool check_message( const std::string &mess )
        {
            const size_t hash_length = hash_checker_->hash_size( );
            const size_t diff_len    = mess.size( ) - hash_length;

            bool result = false;

            if( mess.size( ) >= hash_length ) {
                result = hash_checker_->
                         check_data_hash( mess.c_str( ) + hash_length,
                                          diff_len,
                                          mess.c_str( ) );
            }
            return result;
        }

        call_context *release_call_context( )
        {
            call_context *old = context_.get( );
            context_.release( );
            return old;
        }

        call_context *reset_call_context( call_context *cc )
        {
            context_.reset( cc );
            return cc;
        }

        const call_context *get_call_context( ) const
        {
            return context_.get( );
        }

        call_context *get_call_context( )
        {
            return context_.get( );
        }

        void change_sign_checker( hash_iface *new_signer )
        {
            hash_checker_.reset(new_signer);
        }

        void change_sign_maker( hash_iface *new_signer )
        {
            hash_maker_.reset(new_signer);
        }

        void change_transformer( transformer_iface *new_transformer )
        {
            transformer_.reset(new_transformer);
        }

        void change_reverter( transformer_iface *new_reverter)
        {
            reverter_.reset(new_reverter);
        }

        void pop_message( )
        {
            queue_->messages( ).pop_front( );
        }

        void push_rpc_message(uint64_t slot_id, ll_unit_sptr mess)
        {
            if( rpc_queue_.queue_exists( slot_id ) )
                rpc_queue_.write_queue( slot_id, mess );
        }

        void push_rpc_message_all( ll_unit_sptr mess)
        {
            rpc_queue_.write_all( mess );
        }

        void call_rpc_method( uint64_t slot_id, const ll_unit_type &llu )
        {
            rpc_queue_.add_queue( slot_id );
            send_message( llu );
        }

        void call_rpc_method( const ll_unit_type &llu )
        {
            send_message( llu );
        }

        void wait_call_slot( uint64_t slot_id, uint32_t millisec)
        {
            wait_result_codes qwr = rpc_queue_.wait_queue( slot_id,
                             vtrc::chrono::milliseconds(millisec) );
            raise_wait_error( qwr );
        }

        void wait_call_slot(uint64_t slot_id, ll_unit_sptr &mess,
                                                            uint32_t millisec)
        {
            wait_result_codes qwr =
                    rpc_queue_.read(
                        slot_id, mess,
                        vtrc::chrono::milliseconds(millisec) );
            raise_wait_error( qwr );
        }

        void wait_call_slot(
                    uint64_t slot_id, std::deque<ll_unit_sptr> &data_list,
                                                       uint32_t millisec )
        {
            wait_result_codes qwr = rpc_queue_.read_queue(
                        slot_id, data_list,
                        vtrc::chrono::milliseconds(millisec)) ;
            raise_wait_error( qwr );
        }

        void close_slot( uint64_t slot_id )
        {
            rpc_queue_.erase_queue( slot_id );
        }

        void cancel_slot( uint64_t slot_id )
        {
            rpc_queue_.cancel( slot_id );
        }

        const vtrc_rpc_lowlevel::options &get_method_options(
                              const gpb::MethodDescriptor *method)
        {
            upgradable_lock lck(options_map_lock_);

            options_map_type::const_iterator f(options_map_.find(method));

            vtrc::shared_ptr<vtrc_rpc_lowlevel::options> result;

            if( f == options_map_.end( ) ) {

                const vtrc_rpc_lowlevel::rpc_options_type &serv (
                    method->service( )->options( )
                        .GetExtension( vtrc_rpc_lowlevel::service_options ));

                const vtrc_rpc_lowlevel::rpc_options_type &meth (
                    method->options( )
                        .GetExtension( vtrc_rpc_lowlevel::method_options));

                result = vtrc::make_shared<vtrc_rpc_lowlevel::options>
                                                                (serv.opt( ));
                if( meth.has_opt( ) )
                    utilities::merge_messages( *result, meth.opt( ) );

                upgrade_to_unique ulck( lck );
                options_map_.insert( std::make_pair( method, result ) );

            } else {
                result = f->second;
            }

            return *result;
        }

        void cancel_all_slots( bool erase )
        {
            if( erase )
                rpc_queue_.erase_all( );
            else
                rpc_queue_.cancel_all( );
        }

    };

    protocol_layer::protocol_layer( transport_iface *connection )
        :impl_(new impl(connection))
    {
        impl_->parent_ = this;
    }

    protocol_layer::~protocol_layer( )
    {
        delete impl_;
    }

    void protocol_layer::process_data( const char *data, size_t length )
    {
        impl_->process_data( data, length );
    }

    std::string protocol_layer::prepare_data( const char *data, size_t length)
    {
        return impl_->prepare_data( data, length );
    }

    void protocol_layer::send_message(const google::protobuf::Message &message)
    {
        impl_->send_message( message );
    }

    call_context *protocol_layer::reset_call_context(call_context *cc)
    {
        return impl_->reset_call_context( cc );
    }

    call_context *protocol_layer::release_call_context( )
    {
        return impl_->release_call_context( );
    }

    const vtrc_rpc_lowlevel::options &protocol_layer::get_method_options(
                              const gpb::MethodDescriptor *method)
    {
        return impl_->get_method_options( method );
    }

    const call_context *protocol_layer::get_call_context( ) const
    {
        return impl_->get_call_context( );
    }

    call_context *protocol_layer::get_call_context( )
    {
        return impl_->get_call_context( );
    }

    bool protocol_layer::check_message( const std::string &mess )
    {
        return impl_->check_message( mess );
    }

    void protocol_layer::parse_message( const std::string &mess,
                                        google::protobuf::Message &result )
    {
        impl_->parse_message(mess, result);
    }

    message_queue_type &protocol_layer::message_queue( )
    {
        return impl_->message_queue( );
    }

    const message_queue_type &protocol_layer::message_queue( ) const
    {
        return impl_->message_queue( );
    }

    void protocol_layer::change_hash_maker( hash_iface *new_hasher )
    {
        impl_->change_sign_maker( new_hasher );
    }

    void protocol_layer::change_hash_checker( hash_iface *new_hasher )
    {
        impl_->change_sign_checker( new_hasher );
    }

    void protocol_layer::change_transformer( transformer_iface *new_transformer)
    {
        impl_->change_transformer( new_transformer );
    }

    void protocol_layer::change_reverter( transformer_iface *new_reverter)
    {
        impl_->change_reverter( new_reverter );
    }

    void protocol_layer::pop_message( )
    {
        impl_->pop_message( );
    }

    uint64_t protocol_layer::next_index( )
    {
        return impl_->next_index( );
    }

    void protocol_layer::call_rpc_method( const ll_unit_type &llu )
    {
        impl_->call_rpc_method( llu );
    }

    void protocol_layer::call_rpc_method( uint64_t slot_id,
                                          const ll_unit_type &llu )
    {
        impl_->call_rpc_method( slot_id, llu );
    }

    void protocol_layer::wait_call_slot( uint64_t slot_id, uint32_t millisec)
    {
        impl_->wait_call_slot( slot_id, millisec);
    }

    void protocol_layer::wait_call_slot(uint64_t slot_id, ll_unit_sptr &mess,
                                                            uint32_t millisec)
    {
        impl_->wait_call_slot( slot_id, mess, millisec);
    }

    void protocol_layer::wait_call_slot( uint64_t slot_id,
                         std::deque<ll_unit_sptr> &data_list,
                         uint32_t millisec )
    {
        impl_->wait_call_slot( slot_id, data_list, millisec);
    }

    void protocol_layer::close_slot(uint64_t slot_id)
    {
        impl_->close_slot( slot_id );
    }

    void protocol_layer::cancel_slot(uint64_t slot_id)
    {
        impl_->cancel_slot( slot_id );
    }

    void protocol_layer::cancel_all_slots( bool erase  )
    {
        impl_->cancel_all_slots( erase );
    }

    void protocol_layer::push_rpc_message(uint64_t slot_id, ll_unit_sptr mess)
    {
        impl_->push_rpc_message(slot_id, mess);
    }

    void protocol_layer::push_rpc_message_all( ll_unit_sptr mess)
    {
        impl_->push_rpc_message_all( mess );
    }

    void protocol_layer::on_write_error(const boost::system::error_code & /*e*/)
    {

    }

    void protocol_layer::on_read_error(const boost::system::error_code & /*e*/)
    {

    }

}}
<commit_msg>protocol<commit_after>
#include <boost/thread/tss.hpp>
#include "vtrc-thread.h"

#include <google/protobuf/message.h>
#include <google/protobuf/descriptor.h>

#include "vtrc-atomic.h"
#include "vtrc-mutex.h"
#include "vtrc-mutex-typedefs.h"

#include "vtrc-protocol-layer.h"

#include "vtrc-data-queue.h"
#include "vtrc-hash-iface.h"
#include "vtrc-transformer-iface.h"

#include "vtrc-transport-iface.h"

#include "vtrc-condition-queues.h"
#include "vtrc-exception.h"
#include "vtrc-call-context.h"

#include "proto-helper/message-utilities.h"

#include "protocol/vtrc-rpc-lowlevel.pb.h"
#include "protocol/vtrc-errors.pb.h"

namespace vtrc { namespace common {

    namespace gpb = google::protobuf;

    namespace {

        typedef protocol_layer::message_queue_type message_queue_type;

        void raise_error( unsigned code )
        {
            throw vtrc::common::exception( code );
        }

        void raise_wait_error( wait_result_codes wr )
        {
            switch ( wr ) {
            case WAIT_RESULT_CANCELED:
                raise_error( vtrc_errors::ERR_CANCELED );
                break;
            case  WAIT_RESULT_TIMEOUT:
                raise_error( vtrc_errors::ERR_TIMEOUT );
                break;
            default:
                break;
            }
        }

        static const size_t maximum_message_length = 1024 * 1024;

        struct rpc_unit_index {
            gpb::uint64     id_;
            rpc_unit_index( gpb::uint64 id )
                :id_(id)
            { }
        };

        inline bool operator < ( const rpc_unit_index &lhs,
                                 const rpc_unit_index &rhs )
        {
            return lhs.id_ < rhs.id_;
        }

        typedef vtrc_rpc_lowlevel::lowlevel_unit ll_unit_type;
        typedef vtrc::shared_ptr<ll_unit_type>   ll_unit_sptr;

        typedef condition_queues<rpc_unit_index, ll_unit_sptr> rpc_queue_type;

        typedef boost::thread_specific_ptr<call_context> call_context_ptr;

        typedef std::map <
             const google::protobuf::MethodDescriptor *
            ,vtrc::shared_ptr<vtrc_rpc_lowlevel::options>
        > options_map_type;

    }

    struct protocol_layer::impl {

        transport_iface             *connection_;
        protocol_layer              *parent_;
        hasher_iface_sptr            hash_maker_;
        hasher_iface_sptr            hash_checker_;
        transformer_iface_sptr       transformer_;
        transformer_iface_sptr       reverter_;

        data_queue::queue_base_sptr  queue_;

        rpc_queue_type               rpc_queue_;
        vtrc::atomic<uint64_t>       rpc_index_;

        call_context_ptr             context_;

        options_map_type             options_map_;
        mutable vtrc::shared_mutex   options_map_lock_;

        impl( transport_iface *c )
            :connection_(c)
            ,hash_maker_(common::hash::create_default( ))
            ,hash_checker_(common::hash::create_default( ))
//            ,transformer_(common::transformers::erseefor::create( "1234", 4 ))
//            ,reverter_(common::transformers::erseefor::create( "1234", 4 ))
            ,transformer_(common::transformers::none::create( ))
            ,reverter_(common::transformers::none::create( ))
            ,queue_(data_queue::varint::create_parser(maximum_message_length))
            ,rpc_index_(100)
        {}

        std::string prepare_data( const char *data, size_t length)
        {
            /* here is:
             *  message =
             *  <packed_size(data_length+hash_length)><hash(data)><data>
            */
            std::string result(queue_->pack_size(
                                length + hash_maker_->hash_size( )));

            result.append( hash_maker_->get_data_hash(data, length ));
            result.append( data, data + length );

            /*
             * message = transform( message )
            */

            transformer_->transform(
                            result.empty( ) ? NULL : &result[0],
                                            result.size( ) );
            return result;
        }

        void process_data( const char *data, size_t length )
        {
            if( length > 0 ) {
                std::string next_data(data, data + length);

                const size_t old_size = queue_->messages( ).size( );

                /*
                 * message = revert( message )
                */
                reverter_->transform( &next_data[0], next_data.size( ) );
                queue_->append( &next_data[0], next_data.size( ));

                queue_->process( );

                if( queue_->messages( ).size( ) > old_size ) {
                    parent_->on_data_ready( );
                }
            }

        }

        void drop_first( )
        {
            queue_->messages( ).pop_front( );
        }

        void send_data( const char *data, size_t length )
        {
            connection_->write( data, length );
        }

        void send_message( const gpb::Message &message )
        {
            std::string ser(message.SerializeAsString( ));
            send_data( ser.c_str( ), ser.size( ) );
        }

        uint64_t next_index( )
        {
            return ++rpc_index_;
        }

        // --------------- sett ----------------- //

        message_queue_type &message_queue( )
        {
            return queue_->messages( );
        }

        const message_queue_type &message_queue( ) const
        {
            return queue_->messages( );
        }

        void parse_message( const std::string &mess,
                            google::protobuf::Message &result )
        {
            const size_t hash_length = hash_checker_->hash_size( );
            const size_t diff_len    = mess.size( ) - hash_length;
            result.ParseFromArray( mess.c_str( ) + hash_length, diff_len );
        }

        bool check_message( const std::string &mess )
        {
            const size_t hash_length = hash_checker_->hash_size( );
            const size_t diff_len    = mess.size( ) - hash_length;

            bool result = false;

            if( mess.size( ) >= hash_length ) {
                result = hash_checker_->
                         check_data_hash( mess.c_str( ) + hash_length,
                                          diff_len,
                                          mess.c_str( ) );
            }
            return result;
        }

        call_context *release_call_context( )
        {
            call_context *old = context_.get( );
            context_.release( );
            return old;
        }

        call_context *reset_call_context( call_context *cc )
        {
            context_.reset( cc );
            return cc;
        }

        const call_context *get_call_context( ) const
        {
            return context_.get( );
        }

        call_context *get_call_context( )
        {
            return context_.get( );
        }

        void change_sign_checker( hash_iface *new_signer )
        {
            hash_checker_.reset(new_signer);
        }

        void change_sign_maker( hash_iface *new_signer )
        {
            hash_maker_.reset(new_signer);
        }

        void change_transformer( transformer_iface *new_transformer )
        {
            transformer_.reset(new_transformer);
        }

        void change_reverter( transformer_iface *new_reverter)
        {
            reverter_.reset(new_reverter);
        }

        void pop_message( )
        {
            queue_->messages( ).pop_front( );
        }

        void push_rpc_message(uint64_t slot_id, ll_unit_sptr mess)
        {
            if( rpc_queue_.queue_exists( slot_id ) )
                rpc_queue_.write_queue( slot_id, mess );
        }

        void push_rpc_message_all( ll_unit_sptr mess)
        {
            rpc_queue_.write_all( mess );
        }

        void call_rpc_method( uint64_t slot_id, const ll_unit_type &llu )
        {
            rpc_queue_.add_queue( slot_id );
            send_message( llu );
        }

        void call_rpc_method( const ll_unit_type &llu )
        {
            send_message( llu );
        }

        void wait_call_slot( uint64_t slot_id, uint32_t millisec)
        {
            wait_result_codes qwr = rpc_queue_.wait_queue( slot_id,
                             vtrc::chrono::milliseconds(millisec) );
            raise_wait_error( qwr );
        }

        void wait_call_slot(uint64_t slot_id, ll_unit_sptr &mess,
                                                            uint32_t millisec)
        {
            wait_result_codes qwr =
                    rpc_queue_.read(
                        slot_id, mess,
                        vtrc::chrono::milliseconds(millisec) );
            raise_wait_error( qwr );
        }

        void wait_call_slot(uint64_t slot_id,
                        std::deque<ll_unit_sptr> &data_list, uint32_t millisec )
        {
            wait_result_codes qwr = rpc_queue_.read_queue(
                        slot_id, data_list,
                        vtrc::chrono::milliseconds(millisec)) ;
            raise_wait_error( qwr );
        }

        void close_slot( uint64_t slot_id )
        {
            rpc_queue_.erase_queue( slot_id );
        }

        void cancel_slot( uint64_t slot_id )
        {
            rpc_queue_.cancel( slot_id );
        }

        const vtrc_rpc_lowlevel::options &get_method_options(
                              const gpb::MethodDescriptor *method)
        {
            upgradable_lock lck(options_map_lock_);

            options_map_type::const_iterator f(options_map_.find(method));

            vtrc::shared_ptr<vtrc_rpc_lowlevel::options> result;

            if( f == options_map_.end( ) ) {

                const vtrc_rpc_lowlevel::rpc_options_type &serv (
                    method->service( )->options( )
                        .GetExtension( vtrc_rpc_lowlevel::service_options ));

                const vtrc_rpc_lowlevel::rpc_options_type &meth (
                    method->options( )
                        .GetExtension( vtrc_rpc_lowlevel::method_options));

                result = vtrc::make_shared<vtrc_rpc_lowlevel::options>
                                                                (serv.opt( ));
                if( meth.has_opt( ) )
                    utilities::merge_messages( *result, meth.opt( ) );

                upgrade_to_unique ulck( lck );
                options_map_.insert( std::make_pair( method, result ) );

            } else {
                result = f->second;
            }

            return *result;
        }

        void cancel_all_slots( bool erase )
        {
            if( erase )
                rpc_queue_.erase_all( );
            else
                rpc_queue_.cancel_all( );
        }

    };

    protocol_layer::protocol_layer( transport_iface *connection )
        :impl_(new impl(connection))
    {
        impl_->parent_ = this;
    }

    protocol_layer::~protocol_layer( )
    {
        delete impl_;
    }

    void protocol_layer::process_data( const char *data, size_t length )
    {
        impl_->process_data( data, length );
    }

    std::string protocol_layer::prepare_data( const char *data, size_t length)
    {
        return impl_->prepare_data( data, length );
    }

    void protocol_layer::send_message(const google::protobuf::Message &message)
    {
        impl_->send_message( message );
    }

    call_context *protocol_layer::reset_call_context(call_context *cc)
    {
        return impl_->reset_call_context( cc );
    }

    call_context *protocol_layer::release_call_context( )
    {
        return impl_->release_call_context( );
    }

    const vtrc_rpc_lowlevel::options &protocol_layer::get_method_options(
                              const gpb::MethodDescriptor *method)
    {
        return impl_->get_method_options( method );
    }

    const call_context *protocol_layer::get_call_context( ) const
    {
        return impl_->get_call_context( );
    }

    call_context *protocol_layer::get_call_context( )
    {
        return impl_->get_call_context( );
    }

    bool protocol_layer::check_message( const std::string &mess )
    {
        return impl_->check_message( mess );
    }

    void protocol_layer::parse_message( const std::string &mess,
                                        google::protobuf::Message &result )
    {
        impl_->parse_message(mess, result);
    }

    message_queue_type &protocol_layer::message_queue( )
    {
        return impl_->message_queue( );
    }

    const message_queue_type &protocol_layer::message_queue( ) const
    {
        return impl_->message_queue( );
    }

    void protocol_layer::change_hash_maker( hash_iface *new_hasher )
    {
        impl_->change_sign_maker( new_hasher );
    }

    void protocol_layer::change_hash_checker( hash_iface *new_hasher )
    {
        impl_->change_sign_checker( new_hasher );
    }

    void protocol_layer::change_transformer( transformer_iface *new_transformer)
    {
        impl_->change_transformer( new_transformer );
    }

    void protocol_layer::change_reverter( transformer_iface *new_reverter)
    {
        impl_->change_reverter( new_reverter );
    }

    void protocol_layer::pop_message( )
    {
        impl_->pop_message( );
    }

    uint64_t protocol_layer::next_index( )
    {
        return impl_->next_index( );
    }

    void protocol_layer::call_rpc_method( const ll_unit_type &llu )
    {
        impl_->call_rpc_method( llu );
    }

    void protocol_layer::call_rpc_method( uint64_t slot_id,
                                          const ll_unit_type &llu )
    {
        impl_->call_rpc_method( slot_id, llu );
    }

    void protocol_layer::wait_call_slot( uint64_t slot_id, uint32_t millisec)
    {
        impl_->wait_call_slot( slot_id, millisec);
    }

    void protocol_layer::wait_call_slot(uint64_t slot_id, ll_unit_sptr &mess,
                                                            uint32_t millisec)
    {
        impl_->wait_call_slot( slot_id, mess, millisec);
    }

    void protocol_layer::wait_call_slot( uint64_t slot_id,
                         std::deque<ll_unit_sptr> &data_list,
                         uint32_t millisec )
    {
        impl_->wait_call_slot( slot_id, data_list, millisec);
    }

    void protocol_layer::close_slot(uint64_t slot_id)
    {
        impl_->close_slot( slot_id );
    }

    void protocol_layer::cancel_slot(uint64_t slot_id)
    {
        impl_->cancel_slot( slot_id );
    }

    void protocol_layer::cancel_all_slots( bool erase  )
    {
        impl_->cancel_all_slots( erase );
    }

    void protocol_layer::push_rpc_message(uint64_t slot_id, ll_unit_sptr mess)
    {
        impl_->push_rpc_message(slot_id, mess);
    }

    void protocol_layer::push_rpc_message_all( ll_unit_sptr mess)
    {
        impl_->push_rpc_message_all( mess );
    }

    void protocol_layer::on_write_error(const boost::system::error_code & /*e*/)
    {

    }

    void protocol_layer::on_read_error(const boost::system::error_code & /*e*/)
    {

    }

}}
<|endoftext|>


<commit_before>

/**
 * delays
 * Copyright (c) 2015 Daniele Olmisani
 *
 * see LICENSE file
 *
 * Compile using: gcc -Wall delays.cpp -o delays
 */

#include <cstdio>
#include <ctime>

#include <unistd.h>

void delay(int seconds)
{
	struct timespec current;
	clock_gettime(CLOCK_MONOTONIC, &current);

	long threshold;
	threshold = current.tv_sec + seconds;

	while (current.tv_sec < threshold) {

		printf("\rdelays: %5lds", threshold-current.tv_sec);

		sleep(1);
		clock_gettime(CLOCK_MONOTONIC, &current);
	}
	printf("\n");
}

int main(int argc, char** argv)
{

	setbuf(stdout, NULL);

	delay(10);

	return 0;
}
<commit_msg>fixed behavior<commit_after>

/**
 * delays
 * Copyright (c) 2015 Daniele Olmisani
 *
 * see LICENSE file
 *
 * Compile using: gcc -Wall delays.cpp -o delays
 */

#include <cstdio>
#include <ctime>
#include <cstdlib>

#include <unistd.h>

void delay(int seconds)
{
	struct timespec current;
	clock_gettime(CLOCK_MONOTONIC, &current);

	long threshold;
	threshold = current.tv_sec + seconds;

	while (current.tv_sec < threshold) {

		printf("\rdelays: %7lds", threshold-current.tv_sec);

		sleep(1);
		clock_gettime(CLOCK_MONOTONIC, &current);
	}
	printf("\n");
}

int main(int argc, char** argv)
{

	setbuf(stdout, NULL);

	if (argc != 2) {
		printf("usage: %s <secs>\n", argv[0]);
	}

	int seconds;
	seconds = strtol(argv[1], 0, 0);

	delay(seconds);

	return 0;
}
<|endoftext|>