<commit_before>/*
 * The Apache Software License, Version 1.1
 *
 * Copyright (c) 1999-2003 The Apache Software Foundation.  All rights
 * reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 *
 * 3. The end-user documentation included with the redistribution,
 *    if any, must include the following acknowledgment:
 *       "This product includes software developed by the
 *        Apache Software Foundation (http://www.apache.org/)."
 *    Alternately, this acknowledgment may appear in the software itself,
 *    if and wherever such third-party acknowledgments normally appear.
 *
 * 4. The names "Xerces" and "Apache Software Foundation" must
 *    not be used to endorse or promote products derived from this
 *    software without prior written permission. For written
 *    permission, please contact apache\@apache.org.
 *
 * 5. Products derived from this software may not be called "Apache",
 *    nor may "Apache" appear in their name, without prior written
 *    permission of the Apache Software Foundation.
 *
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 * ====================================================================
 *
 * This software consists of voluntary contributions made by many
 * individuals on behalf of the Apache Software Foundation, and was
 * originally based on software copyright (c) 1999, International
 * Business Machines, Inc., http://www.ibm.com .  For more information
 * on the Apache Software Foundation, please see
 * <http://www.apache.org/>.
 */

/*
 * $Log$
 * Revision 1.4  2003/09/16 18:30:54  neilg
 * make Grammar pool be responsible for creating and owning URI string pools.  This is one more step towards having grammars be independent of the parsers involved in their creation
 *
 * Revision 1.3  2003/09/02 08:59:02  gareth
 * Added API to get enumerator of grammars.
 *
 * Revision 1.2  2003/07/31 17:02:42  peiyongz
 * Grammar embed GrammarDescription
 *
 * Revision 1.1  2003/06/20 18:37:39  peiyongz
 * Stateless Grammar Pool :: Part I
 *
 * $Id$
 *
 */

#if !defined(XMLGRAMMARPOOL_HPP)
#define XMLGRAMMARPOOL_HPP

#include <xercesc/util/PlatformUtils.hpp>
#include <xercesc/util/RefHashTableOf.hpp>
#include <xercesc/util/XMemory.hpp>

XERCES_CPP_NAMESPACE_BEGIN

class Grammar;
class XMLGrammarDescription;
class DTDGrammar;
class SchemaGrammar;
class XMLDTDDescription;
class XMLSchemaDescription;
class XMLStringPool;

class XMLPARSER_EXPORT XMLGrammarPool : public XMemory
{
public :
    // -----------------------------------------------------------------------
    /** @name Virtual destructor for derived classes */
    // -----------------------------------------------------------------------
    //@{

    /**
      * virtual destructor
      *
      */
    virtual ~XMLGrammarPool(){};
    //@}

    // -----------------------------------------------------------------------
    /** @name The Grammar Pool Interface */
    // -----------------------------------------------------------------------
    //@{

    /**
      * cacheGrammar
      *
      * @param gramToCache: the Grammar to be cached in the grammar pool
	  *
      */
    virtual void           cacheGrammar(Grammar* const               gramToCache) = 0;

    /**
      * retrieveGrammar
      *
      * @param gramDesc: the Grammar Description used to search for grammar
	  *                  cached in the grammar pool
	  *
      */
    virtual Grammar*       retrieveGrammar(XMLGrammarDescription* const gramDesc) = 0;

    /**
      * orphanGrammar
      *
	  * grammar removed from the grammar pool and owned by the caller
      *
      * @param nameSpaceKey: Key sed to search for grammar in the grammar pool
	  *
      */
    virtual Grammar*       orphanGrammar(const XMLCh* const nameSpaceKey) = 0;

    /**
     * Get an enumeration of the cached Grammars in the Grammar pool
     *
     * @return enumeration of the cached Grammars in Grammar pool
     */
    virtual RefHashTableOfEnumerator<Grammar> getGrammarEnumerator() const = 0;

    /**
      * clear
      *
	  * all grammars are removed from the grammar pool and deleted.
      *
      */
    virtual void           clear() = 0;

    /**
      * lockPool
      *
	  * When this method is called by the application, the
      * grammar pool should stop adding new grammars to the cache.
      *
      */
    virtual void           lockPool() = 0;

    /**
      * unlockPool
      *
	  * After this method has been called, the grammar pool implementation
      * should return to its default behaviour when cacheGrammars(...) is called.
      *
      */
    virtual void           unlockPool() = 0;
    //@}

    // -----------------------------------------------------------------------
    /** @name  Factory interface */
    // -----------------------------------------------------------------------
    //@{

    /**
      * createDTDGrammar
      *
      */
    virtual DTDGrammar*            createDTDGrammar() = 0;

    /**
      * createSchemaGrammar
      *
      */
    virtual SchemaGrammar*         createSchemaGrammar() = 0;

    /**
      * createDTDDescription
      *
      */
    virtual XMLDTDDescription*     createDTDDescription(const XMLCh* const rootName) = 0;
    /**
      * createSchemaDescription
      *
      */
    virtual XMLSchemaDescription*  createSchemaDescription(const XMLCh* const targetNamespace) = 0;

    //@}

    // -----------------------------------------------------------------------
    /** @name  Getter */
    // -----------------------------------------------------------------------
    //@{

    /**
      * getMemoryManager
      *
      */
	inline MemoryManager* const    getMemoryManager()
    {
        return fMemMgr;
    }

    /**
      * Return an XMLStringPool for use by validation routines.
      * Implementations should not create a string pool on each call to this
      * method, but should maintain one string pool for all grammars
      * for which this pool is responsible.
      */
    virtual XMLStringPool *getURIStringPool() = 0;
    //@}

protected :
    // -----------------------------------------------------------------------
    /**  Hidden Constructors */
    // -----------------------------------------------------------------------
    //@{
    XMLGrammarPool(MemoryManager* const memMgr = XMLPlatformUtils::fgMemoryManager)
    :fMemMgr(memMgr)
    {
    };
    //@}

private :
    // -----------------------------------------------------------------------
    /** name  Unimplemented copy constructor and operator= */
    // -----------------------------------------------------------------------
    //@{
    XMLGrammarPool(const XMLGrammarPool& );
    XMLGrammarPool& operator=(const XMLGrammarPool& );
    //@}

    // -----------------------------------------------------------------------
    //
    // fMemMgr: plugged-in (or defaulted-in) memory manager
    //          not owned
    //          no reset after initialization
    //
    // -----------------------------------------------------------------------

    MemoryManager* const  fMemMgr;
};

XERCES_CPP_NAMESPACE_END

#endif
<commit_msg>update XMLGrammarPool interface to make expected behaviour of locked pools better specified, and to add the capability to generate XSModels<commit_after>/*
 * The Apache Software License, Version 1.1
 *
 * Copyright (c) 1999-2003 The Apache Software Foundation.  All rights
 * reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 *
 * 3. The end-user documentation included with the redistribution,
 *    if any, must include the following acknowledgment:
 *       "This product includes software developed by the
 *        Apache Software Foundation (http://www.apache.org/)."
 *    Alternately, this acknowledgment may appear in the software itself,
 *    if and wherever such third-party acknowledgments normally appear.
 *
 * 4. The names "Xerces" and "Apache Software Foundation" must
 *    not be used to endorse or promote products derived from this
 *    software without prior written permission. For written
 *    permission, please contact apache\@apache.org.
 *
 * 5. Products derived from this software may not be called "Apache",
 *    nor may "Apache" appear in their name, without prior written
 *    permission of the Apache Software Foundation.
 *
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 * ====================================================================
 *
 * This software consists of voluntary contributions made by many
 * individuals on behalf of the Apache Software Foundation, and was
 * originally based on software copyright (c) 1999, International
 * Business Machines, Inc., http://www.ibm.com .  For more information
 * on the Apache Software Foundation, please see
 * <http://www.apache.org/>.
 */

/*
 * $Log$
 * Revision 1.5  2003/10/10 18:36:03  neilg
 * update XMLGrammarPool interface to make expected behaviour of locked pools better specified, and to add the capability to generate XSModels
 *
 * Revision 1.4  2003/09/16 18:30:54  neilg
 * make Grammar pool be responsible for creating and owning URI string pools.  This is one more step towards having grammars be independent of the parsers involved in their creation
 *
 * Revision 1.3  2003/09/02 08:59:02  gareth
 * Added API to get enumerator of grammars.
 *
 * Revision 1.2  2003/07/31 17:02:42  peiyongz
 * Grammar embed GrammarDescription
 *
 * Revision 1.1  2003/06/20 18:37:39  peiyongz
 * Stateless Grammar Pool :: Part I
 *
 * $Id$
 *
 */

#if !defined(XMLGRAMMARPOOL_HPP)
#define XMLGRAMMARPOOL_HPP

#include <xercesc/util/PlatformUtils.hpp>
#include <xercesc/util/RefHashTableOf.hpp>
#include <xercesc/util/XMemory.hpp>
#include <xercesc/framework/psvi/XSModel.hpp>

XERCES_CPP_NAMESPACE_BEGIN

class Grammar;
class XMLGrammarDescription;
class DTDGrammar;
class SchemaGrammar;
class XMLDTDDescription;
class XMLSchemaDescription;
class XMLStringPool;

class XMLPARSER_EXPORT XMLGrammarPool : public XMemory
{
public :
    // -----------------------------------------------------------------------
    /** @name Virtual destructor for derived classes */
    // -----------------------------------------------------------------------
    //@{

    /**
      * virtual destructor
      *
      */
    virtual ~XMLGrammarPool(){};
    //@}

    // -----------------------------------------------------------------------
    /** @name The Grammar Pool Interface */
    // -----------------------------------------------------------------------
    //@{

    /**
      * cacheGrammar
      *
      * @param gramToCache: the Grammar to be cached in the grammar pool
	  *
      */
    virtual void           cacheGrammar(Grammar* const               gramToCache) = 0;

    /**
      * retrieveGrammar
      *
      * @param gramDesc: the Grammar Description used to search for grammar
	  *                  cached in the grammar pool
	  *
      */
    virtual Grammar*       retrieveGrammar(XMLGrammarDescription* const gramDesc) = 0;

    /**
      * orphanGrammar
      *
	  * grammar removed from the grammar pool and owned by the caller
      *
      * @param nameSpaceKey: Key sed to search for grammar in the grammar pool
	  *
      */
    virtual Grammar*       orphanGrammar(const XMLCh* const nameSpaceKey) = 0;

    /**
     * Get an enumeration of the cached Grammars in the Grammar pool
     *
     * @return enumeration of the cached Grammars in Grammar pool
     */
    virtual RefHashTableOfEnumerator<Grammar> getGrammarEnumerator() const = 0;

    /**
      * clear
      *
	  * all grammars are removed from the grammar pool and deleted.
      *
      */
    virtual void           clear() = 0;

    /**
      * lockPool
      *
	  * When this method is called by the application, the
      * grammar pool should stop adding new grammars to the cache.
      * This should result in the grammar pool being sharable
      * among parsers operating in different threads.
      *
      */
    virtual void           lockPool() = 0;

    /**
      * unlockPool
      *
	  * After this method has been called, the grammar pool implementation
      * should return to its default behaviour when cacheGrammars(...) is called.
      *
      */
    virtual void           unlockPool() = 0;
    //@}

    // -----------------------------------------------------------------------
    /** @name  Factory interface */
    // -----------------------------------------------------------------------
    //@{

    /**
      * createDTDGrammar
      *
      */
    virtual DTDGrammar*            createDTDGrammar() = 0;

    /**
      * createSchemaGrammar
      *
      */
    virtual SchemaGrammar*         createSchemaGrammar() = 0;

    /**
      * createDTDDescription
      *
      */
    virtual XMLDTDDescription*     createDTDDescription(const XMLCh* const rootName) = 0;
    /**
      * createSchemaDescription
      *
      */
    virtual XMLSchemaDescription*  createSchemaDescription(const XMLCh* const targetNamespace) = 0;

    //@}

    // -----------------------------------------------------------------------
    /** @name  schema component model support
    // -----------------------------------------------------------------------
    //@{

    /***
      * If the grammar pool has been locked, this method returns
      * an XSModel corresponding to the schema components represented
      * by the objects stored in the pool.  If the pool has not been
      * locked, this must return null.  If the pool is unlocked at
      * any point, the underlying XSModel will be destroyed;
      * applications must take care that, if they wish to unlock
      * a pool, no further access is made to the XSModel
      * it produced.  The pool's XSModel will not be serialized,
      * but, if a locked pool is deserialized, its XSModel
      * will be recreated.
      */
    virtual XSModel *getXSModel() const = 0;

    // @}

    // -----------------------------------------------------------------------
    /** @name  Getter */
    // -----------------------------------------------------------------------
    //@{

    /**
      * getMemoryManager
      *
      */
	inline MemoryManager* const    getMemoryManager()
    {
        return fMemMgr;
    }

    /**
      * Return an XMLStringPool for use by validation routines.
      * Implementations should not create a string pool on each call to this
      * method, but should maintain one string pool for all grammars
      * for which this pool is responsible.
      */
    virtual XMLStringPool *getURIStringPool() = 0;
    //@}

protected :
    // -----------------------------------------------------------------------
    /**  Hidden Constructors */
    // -----------------------------------------------------------------------
    //@{
    XMLGrammarPool(MemoryManager* const memMgr = XMLPlatformUtils::fgMemoryManager)
    :fMemMgr(memMgr)
    {
    };
    //@}

private :
    // -----------------------------------------------------------------------
    /** name  Unimplemented copy constructor and operator= */
    // -----------------------------------------------------------------------
    //@{
    XMLGrammarPool(const XMLGrammarPool& );
    XMLGrammarPool& operator=(const XMLGrammarPool& );
    //@}

    // -----------------------------------------------------------------------
    //
    // fMemMgr: plugged-in (or defaulted-in) memory manager
    //          not owned
    //          no reset after initialization
    //
    // -----------------------------------------------------------------------

    MemoryManager* const  fMemMgr;
};

XERCES_CPP_NAMESPACE_END

#endif
<|endoftext|>


<commit_before>#ifndef STAN_MATH_PRIM_SCAL_PROB_NORMAL_LCDF_HPP
#define STAN_MATH_PRIM_SCAL_PROB_NORMAL_LCDF_HPP

#include <stan/math/prim/meta.hpp>
#include <stan/math/prim/scal/err/check_consistent_sizes.hpp>
#include <stan/math/prim/scal/err/check_finite.hpp>
#include <stan/math/prim/scal/err/check_not_nan.hpp>
#include <stan/math/prim/scal/err/check_positive.hpp>
#include <stan/math/prim/scal/fun/size_zero.hpp>
#include <stan/math/prim/scal/fun/constants.hpp>
#include <stan/math/prim/scal/fun/value_of.hpp>
#include <stan/math/prim/scal/fun/square.hpp>
#include <cmath>
#include <limits>

namespace stan {
namespace math {

template <typename T_y, typename T_loc, typename T_scale>
inline return_type_t<T_y, T_loc, T_scale> normal_lcdf(const T_y& y,
                                                      const T_loc& mu,
                                                      const T_scale& sigma) {
  static const char* function = "normal_lcdf";
  using T_partials_return = partials_return_t<T_y, T_loc, T_scale>;
  using std::exp;
  using std::log;
  using std::log1p;
  using std::pow;

  T_partials_return cdf_log(0.0);
  if (size_zero(y, mu, sigma)) {
    return cdf_log;
  }

  check_not_nan(function, "Random variable", y);
  check_finite(function, "Location parameter", mu);
  check_not_nan(function, "Scale parameter", sigma);
  check_positive(function, "Scale parameter", sigma);
  check_consistent_sizes(function, "Random variable", y, "Location parameter",
                         mu, "Scale parameter", sigma);

  operands_and_partials<T_y, T_loc, T_scale> ops_partials(y, mu, sigma);

  scalar_seq_view<T_y> y_vec(y);
  scalar_seq_view<T_loc> mu_vec(mu);
  scalar_seq_view<T_scale> sigma_vec(sigma);
  size_t N = max_size(y, mu, sigma);

  const double SQRT_TWO_OVER_PI = std::sqrt(2.0 / pi());
  for (size_t n = 0; n < N; n++) {
    const T_partials_return y_dbl = value_of(y_vec[n]);
    const T_partials_return mu_dbl = value_of(mu_vec[n]);
    const T_partials_return sigma_dbl = value_of(sigma_vec[n]);

    const T_partials_return scaled_diff
        = (y_dbl - mu_dbl) / (sigma_dbl * SQRT_2);

    const T_partials_return sigma_sqrt2 = sigma_dbl * SQRT_2;
    const T_partials_return x2 = square(scaled_diff);

    // use erfc() instead of erf() in order to retain precision
    // since for x>0 erfc()->0
    if (scaled_diff > 0.0) {
      // CDF(x) = 1/2 + 1/2erf(x) = 1 - 1/2erfc(x)
      cdf_log += log1p(-0.5 * erfc(scaled_diff));
      if (!is_not_nan(cdf_log)) {
        cdf_log = 0;
      }
    } else if (scaled_diff > -20.0) {
      // CDF(x) = 1/2 - 1/2erf(-x) = 1/2erfc(-x)
      cdf_log += log(erfc(-scaled_diff)) + LOG_HALF;
    } else if (10.0 * log(fabs(scaled_diff))
               < log(std::numeric_limits<T_partials_return>::max())) {
      // entering territory where erfc(-x)~0
      // need to use direct numerical approximation of cdf_log instead
      // the following based on W. J. Cody, Math. Comp. 23(107):631-638 (1969)
      // CDF(x) = 1/2erfc(-x)
      const T_partials_return x4 = pow(scaled_diff, 4);
      const T_partials_return x6 = pow(scaled_diff, 6);
      const T_partials_return x8 = pow(scaled_diff, 8);
      const T_partials_return x10 = pow(scaled_diff, 10);
      const T_partials_return temp_p
          = 0.000658749161529837803157 + 0.0160837851487422766278 / x2
            + 0.125781726111229246204 / x4 + 0.360344899949804439429 / x6
            + 0.305326634961232344035 / x8 + 0.0163153871373020978498 / x10;
      const T_partials_return temp_q
          = -0.00233520497626869185443 - 0.0605183413124413191178 / x2
            - 0.527905102951428412248 / x4 - 1.87295284992346047209 / x6
            - 2.56852019228982242072 / x8 - 1.0 / x10;
      cdf_log += LOG_HALF + log(1.0 / SQRT_PI + (temp_p / temp_q) / x2)
                 - log(-scaled_diff) - x2;
    } else {
      // scaled_diff^10 term will overflow
      cdf_log = stan::math::negative_infinity();
    }

    if (!is_constant_all<T_y, T_loc, T_scale>::value) {
      // compute partial derivatives
      // based on analytic form given by:
      // dln(CDF)/dx = exp(-x^2)/(sqrt(pi)*(1/2+erf(x)/2)
      T_partials_return dncdf_log = 0.0;
      T_partials_return t = 0.0;
      T_partials_return t2 = 0.0;
      T_partials_return t4 = 0.0;

      // calculate using piecewise funciton
      // (due to instability / inaccuracy in the various approximations)
      if (scaled_diff > 2.9) {
        // approximation derived from Abramowitx and Stegun (1964) 7.1.26
        t = 1.0 / (1.0 + 0.3275911 * scaled_diff);
        t2 = square(t);
        t4 = pow(t, 4);
        dncdf_log
            = 1.0
              / (SQRT_PI
                 * (exp(x2) - 0.254829592 + 0.284496736 * t - 1.421413741 * t2
                    + 1.453152027 * t2 * t - 1.061405429 * t4));
      } else if (scaled_diff > 2.5) {
        // in the trouble area where all of the standard numerical
        // approximations are unstable - bridge the gap using Taylor
        // expansions of the analytic function
        // use Taylor expansion centred around x=2.7
        t = scaled_diff - 2.7;
        t2 = square(t);
        t4 = pow(t, 4);
        dncdf_log = 0.0003849882382 - 0.002079084702 * t + 0.005229340880 * t2
                    - 0.008029540137 * t2 * t + 0.008232190507 * t4
                    - 0.005692364250 * t4 * t + 0.002399496363 * pow(t, 6);
      } else if (scaled_diff > 2.1) {
        // use Taylor expansion centred around x=2.3
        t = scaled_diff - 2.3;
        t2 = square(t);
        t4 = pow(t, 4);
        dncdf_log = 0.002846135439 - 0.01310032351 * t + 0.02732189391 * t2
                    - 0.03326906904 * t2 * t + 0.02482478940 * t4
                    - 0.009883071924 * t4 * t - 0.0002771362254 * pow(t, 6);
      } else if (scaled_diff > 1.5) {
        // use Taylor expansion centred around x=1.85
        t = scaled_diff - 1.85;
        t2 = square(t);
        t4 = pow(t, 4);
        dncdf_log = 0.01849212058 - 0.06876280470 * t + 0.1099906382 * t2
                    - 0.09274533184 * t2 * t + 0.03543327418 * t4
                    + 0.005644855518 * t4 * t - 0.01111434424 * pow(t, 6);
      } else if (scaled_diff > 0.8) {
        // use Taylor expansion centred around x=1.15
        t = scaled_diff - 1.15;
        t2 = square(t);
        t4 = pow(t, 4);
        dncdf_log = 0.1585747034 - 0.3898677543 * t + 0.3515963775 * t2
                    - 0.09748053605 * t2 * t - 0.04347986191 * t4
                    + 0.02182506378 * t4 * t + 0.01074751427 * pow(t, 6);
      } else if (scaled_diff > 0.1) {
        // use Taylor expansion centred around x=0.45
        t = scaled_diff - 0.45;
        t2 = square(t);
        t4 = pow(t, 4);
        dncdf_log = 0.6245634904 - 0.9521866949 * t + 0.3986215682 * t2
                    + 0.04700850676 * t2 * t - 0.03478651979 * t4
                    - 0.01772675404 * t4 * t + 0.0006577254811 * pow(t, 6);
      } else if (10.0 * log(fabs(scaled_diff))
                 < log(std::numeric_limits<T_partials_return>::max())) {
        // approximation derived from Abramowitx and Stegun (1964) 7.1.26
        // use fact that erf(x)=-erf(-x)
        // Abramowitx and Stegun define this for -inf<x<0 but seems to be
        // accurate for -inf<x<0.1
        t = 1.0 / (1.0 - 0.3275911 * scaled_diff);
        t2 = square(t);
        t4 = pow(t, 4);
        dncdf_log
            = 2.0
              / (SQRT_PI
                 * (0.254829592 * t - 0.284496736 * t2 + 1.421413741 * t2 * t
                    - 1.453152027 * t4 + 1.061405429 * t4 * t));
        // check if we need to add a correction term
        // (from cubic fit of residuals)
        if (scaled_diff < -29.0) {
          dncdf_log += 0.0015065154280332 * x2
                       - 0.3993154819705530 * scaled_diff - 4.2919418242931700;
        } else if (scaled_diff < -17.0) {
          dncdf_log += 0.0001263257217272 * x2 * scaled_diff
                       + 0.0123586859488623 * x2
                       - 0.0860505264736028 * scaled_diff - 1.252783383752970;
        } else if (scaled_diff < -7.0) {
          dncdf_log += 0.000471585349920831 * x2 * scaled_diff
                       + 0.0296839305424034 * x2
                       + 0.207402143352332 * scaled_diff + 0.425316974683324;
        } else if (scaled_diff < -3.9) {
          dncdf_log += -0.0006972280656443 * x2 * scaled_diff
                       + 0.0068218494628567 * x2
                       + 0.0585761964460277 * scaled_diff + 0.1034397670201370;
        } else if (scaled_diff < -2.1) {
          dncdf_log += -0.0018742199480885 * x2 * scaled_diff
                       - 0.0097119598291202 * x2
                       - 0.0170137970924080 * scaled_diff - 0.0100428567412041;
        }
      } else {
        dncdf_log = stan::math::positive_infinity();
      }

      if (!is_constant_all<T_y>::value) {
        ops_partials.edge1_.partials_[n] += dncdf_log / sigma_sqrt2;
      }
      if (!is_constant_all<T_loc>::value) {
        ops_partials.edge2_.partials_[n] -= dncdf_log / sigma_sqrt2;
      }
      if (!is_constant_all<T_scale>::value) {
        ops_partials.edge3_.partials_[n] -= dncdf_log * scaled_diff / sigma_dbl;
      }
    }
  }
  return ops_partials.build(cdf_log);
}

}  // namespace math
}  // namespace stan
#endif
<commit_msg>Added brief comment explaining reasoning behind the extensive numerical approximations used.<commit_after>#ifndef STAN_MATH_PRIM_SCAL_PROB_NORMAL_LCDF_HPP
#define STAN_MATH_PRIM_SCAL_PROB_NORMAL_LCDF_HPP

#include <stan/math/prim/meta.hpp>
#include <stan/math/prim/scal/err/check_consistent_sizes.hpp>
#include <stan/math/prim/scal/err/check_finite.hpp>
#include <stan/math/prim/scal/err/check_not_nan.hpp>
#include <stan/math/prim/scal/err/check_positive.hpp>
#include <stan/math/prim/scal/fun/size_zero.hpp>
#include <stan/math/prim/scal/fun/constants.hpp>
#include <stan/math/prim/scal/fun/value_of.hpp>
#include <stan/math/prim/scal/fun/square.hpp>
#include <cmath>
#include <limits>

namespace stan {
namespace math {

template <typename T_y, typename T_loc, typename T_scale>
inline return_type_t<T_y, T_loc, T_scale> normal_lcdf(const T_y& y,
                                                      const T_loc& mu,
                                                      const T_scale& sigma) {
  static const char* function = "normal_lcdf";
  using T_partials_return = partials_return_t<T_y, T_loc, T_scale>;
  using std::exp;
  using std::log;
  using std::log1p;
  using std::pow;

  T_partials_return cdf_log(0.0);
  if (size_zero(y, mu, sigma)) {
    return cdf_log;
  }

  check_not_nan(function, "Random variable", y);
  check_finite(function, "Location parameter", mu);
  check_not_nan(function, "Scale parameter", sigma);
  check_positive(function, "Scale parameter", sigma);
  check_consistent_sizes(function, "Random variable", y, "Location parameter",
                         mu, "Scale parameter", sigma);

  operands_and_partials<T_y, T_loc, T_scale> ops_partials(y, mu, sigma);

  scalar_seq_view<T_y> y_vec(y);
  scalar_seq_view<T_loc> mu_vec(mu);
  scalar_seq_view<T_scale> sigma_vec(sigma);
  size_t N = max_size(y, mu, sigma);

  const double SQRT_TWO_OVER_PI = std::sqrt(2.0 / pi());
  for (size_t n = 0; n < N; n++) {
    const T_partials_return y_dbl = value_of(y_vec[n]);
    const T_partials_return mu_dbl = value_of(mu_vec[n]);
    const T_partials_return sigma_dbl = value_of(sigma_vec[n]);

    const T_partials_return scaled_diff
        = (y_dbl - mu_dbl) / (sigma_dbl * SQRT_2);

    const T_partials_return sigma_sqrt2 = sigma_dbl * SQRT_2;
    const T_partials_return x2 = square(scaled_diff);

    // Rigorous numerical approximations are applied here to deal with values
    // of |scaled_diff|>>0. This is needed to deal with rare base-rate
    // logistic regression problems where it is useful to use an alternative
    // link function instead.
    //
    // use erfc() instead of erf() in order to retain precision
    // since for x>0 erfc()->0
    if (scaled_diff > 0.0) {
      // CDF(x) = 1/2 + 1/2erf(x) = 1 - 1/2erfc(x)
      cdf_log += log1p(-0.5 * erfc(scaled_diff));
      if (!is_not_nan(cdf_log)) {
        cdf_log = 0;
      }
    } else if (scaled_diff > -20.0) {
      // CDF(x) = 1/2 - 1/2erf(-x) = 1/2erfc(-x)
      cdf_log += log(erfc(-scaled_diff)) + LOG_HALF;
    } else if (10.0 * log(fabs(scaled_diff))
               < log(std::numeric_limits<T_partials_return>::max())) {
      // entering territory where erfc(-x)~0
      // need to use direct numerical approximation of cdf_log instead
      // the following based on W. J. Cody, Math. Comp. 23(107):631-638 (1969)
      // CDF(x) = 1/2erfc(-x)
      const T_partials_return x4 = pow(scaled_diff, 4);
      const T_partials_return x6 = pow(scaled_diff, 6);
      const T_partials_return x8 = pow(scaled_diff, 8);
      const T_partials_return x10 = pow(scaled_diff, 10);
      const T_partials_return temp_p
          = 0.000658749161529837803157 + 0.0160837851487422766278 / x2
            + 0.125781726111229246204 / x4 + 0.360344899949804439429 / x6
            + 0.305326634961232344035 / x8 + 0.0163153871373020978498 / x10;
      const T_partials_return temp_q
          = -0.00233520497626869185443 - 0.0605183413124413191178 / x2
            - 0.527905102951428412248 / x4 - 1.87295284992346047209 / x6
            - 2.56852019228982242072 / x8 - 1.0 / x10;
      cdf_log += LOG_HALF + log(1.0 / SQRT_PI + (temp_p / temp_q) / x2)
                 - log(-scaled_diff) - x2;
    } else {
      // scaled_diff^10 term will overflow
      cdf_log = stan::math::negative_infinity();
    }

    if (!is_constant_all<T_y, T_loc, T_scale>::value) {
      // compute partial derivatives
      // based on analytic form given by:
      // dln(CDF)/dx = exp(-x^2)/(sqrt(pi)*(1/2+erf(x)/2)
      T_partials_return dncdf_log = 0.0;
      T_partials_return t = 0.0;
      T_partials_return t2 = 0.0;
      T_partials_return t4 = 0.0;

      // calculate using piecewise funciton
      // (due to instability / inaccuracy in the various approximations)
      if (scaled_diff > 2.9) {
        // approximation derived from Abramowitx and Stegun (1964) 7.1.26
        t = 1.0 / (1.0 + 0.3275911 * scaled_diff);
        t2 = square(t);
        t4 = pow(t, 4);
        dncdf_log
            = 1.0
              / (SQRT_PI
                 * (exp(x2) - 0.254829592 + 0.284496736 * t - 1.421413741 * t2
                    + 1.453152027 * t2 * t - 1.061405429 * t4));
      } else if (scaled_diff > 2.5) {
        // in the trouble area where all of the standard numerical
        // approximations are unstable - bridge the gap using Taylor
        // expansions of the analytic function
        // use Taylor expansion centred around x=2.7
        t = scaled_diff - 2.7;
        t2 = square(t);
        t4 = pow(t, 4);
        dncdf_log = 0.0003849882382 - 0.002079084702 * t + 0.005229340880 * t2
                    - 0.008029540137 * t2 * t + 0.008232190507 * t4
                    - 0.005692364250 * t4 * t + 0.002399496363 * pow(t, 6);
      } else if (scaled_diff > 2.1) {
        // use Taylor expansion centred around x=2.3
        t = scaled_diff - 2.3;
        t2 = square(t);
        t4 = pow(t, 4);
        dncdf_log = 0.002846135439 - 0.01310032351 * t + 0.02732189391 * t2
                    - 0.03326906904 * t2 * t + 0.02482478940 * t4
                    - 0.009883071924 * t4 * t - 0.0002771362254 * pow(t, 6);
      } else if (scaled_diff > 1.5) {
        // use Taylor expansion centred around x=1.85
        t = scaled_diff - 1.85;
        t2 = square(t);
        t4 = pow(t, 4);
        dncdf_log = 0.01849212058 - 0.06876280470 * t + 0.1099906382 * t2
                    - 0.09274533184 * t2 * t + 0.03543327418 * t4
                    + 0.005644855518 * t4 * t - 0.01111434424 * pow(t, 6);
      } else if (scaled_diff > 0.8) {
        // use Taylor expansion centred around x=1.15
        t = scaled_diff - 1.15;
        t2 = square(t);
        t4 = pow(t, 4);
        dncdf_log = 0.1585747034 - 0.3898677543 * t + 0.3515963775 * t2
                    - 0.09748053605 * t2 * t - 0.04347986191 * t4
                    + 0.02182506378 * t4 * t + 0.01074751427 * pow(t, 6);
      } else if (scaled_diff > 0.1) {
        // use Taylor expansion centred around x=0.45
        t = scaled_diff - 0.45;
        t2 = square(t);
        t4 = pow(t, 4);
        dncdf_log = 0.6245634904 - 0.9521866949 * t + 0.3986215682 * t2
                    + 0.04700850676 * t2 * t - 0.03478651979 * t4
                    - 0.01772675404 * t4 * t + 0.0006577254811 * pow(t, 6);
      } else if (10.0 * log(fabs(scaled_diff))
                 < log(std::numeric_limits<T_partials_return>::max())) {
        // approximation derived from Abramowitx and Stegun (1964) 7.1.26
        // use fact that erf(x)=-erf(-x)
        // Abramowitx and Stegun define this for -inf<x<0 but seems to be
        // accurate for -inf<x<0.1
        t = 1.0 / (1.0 - 0.3275911 * scaled_diff);
        t2 = square(t);
        t4 = pow(t, 4);
        dncdf_log
            = 2.0
              / (SQRT_PI
                 * (0.254829592 * t - 0.284496736 * t2 + 1.421413741 * t2 * t
                    - 1.453152027 * t4 + 1.061405429 * t4 * t));
        // check if we need to add a correction term
        // (from cubic fit of residuals)
        if (scaled_diff < -29.0) {
          dncdf_log += 0.0015065154280332 * x2
                       - 0.3993154819705530 * scaled_diff - 4.2919418242931700;
        } else if (scaled_diff < -17.0) {
          dncdf_log += 0.0001263257217272 * x2 * scaled_diff
                       + 0.0123586859488623 * x2
                       - 0.0860505264736028 * scaled_diff - 1.252783383752970;
        } else if (scaled_diff < -7.0) {
          dncdf_log += 0.000471585349920831 * x2 * scaled_diff
                       + 0.0296839305424034 * x2
                       + 0.207402143352332 * scaled_diff + 0.425316974683324;
        } else if (scaled_diff < -3.9) {
          dncdf_log += -0.0006972280656443 * x2 * scaled_diff
                       + 0.0068218494628567 * x2
                       + 0.0585761964460277 * scaled_diff + 0.1034397670201370;
        } else if (scaled_diff < -2.1) {
          dncdf_log += -0.0018742199480885 * x2 * scaled_diff
                       - 0.0097119598291202 * x2
                       - 0.0170137970924080 * scaled_diff - 0.0100428567412041;
        }
      } else {
        dncdf_log = stan::math::positive_infinity();
      }

      if (!is_constant_all<T_y>::value) {
        ops_partials.edge1_.partials_[n] += dncdf_log / sigma_sqrt2;
      }
      if (!is_constant_all<T_loc>::value) {
        ops_partials.edge2_.partials_[n] -= dncdf_log / sigma_sqrt2;
      }
      if (!is_constant_all<T_scale>::value) {
        ops_partials.edge3_.partials_[n] -= dncdf_log * scaled_diff / sigma_dbl;
      }
    }
  }
  return ops_partials.build(cdf_log);
}

}  // namespace math
}  // namespace stan
#endif
<|endoftext|>


<commit_before>/*
Licensed Materials - Property of IBM
DB2 Storage Engine Enablement
Copyright IBM Corporation 2007,2008
All rights reserved

Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
 (a) Redistributions of source code must retain this list of conditions, the
     copyright notice in section {d} below, and the disclaimer following this
     list of conditions.
 (b) Redistributions in binary form must reproduce this list of conditions, the
     copyright notice in section (d) below, and the disclaimer following this
     list of conditions, in the documentation and/or other materials provided
     with the distribution.
 (c) The name of IBM may not be used to endorse or promote products derived from
     this software without specific prior written permission.
 (d) The text of the required copyright notice is:
       Licensed Materials - Property of IBM
       DB2 Storage Engine Enablement
       Copyright IBM Corporation 2007,2008
       All rights reserved

THIS SOFTWARE IS PROVIDED BY IBM CORPORATION "AS IS" AND ANY EXPRESS OR IMPLIED
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
SHALL IBM CORPORATION BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
OF SUCH DAMAGE.
*/

#include "db2i_collationSupport.h"
#include "db2i_errors.h"

/*
  The following arrays define a mapping between MySQL collation names and
  corresponding IBM i sort sequences. The mapping is a 1-to-1 correlation
  between corresponding array slots but is incomplete without case-sensitivity
  markers dynamically added to the mySqlSortSequence names.
*/
#define MAX_COLLATION 89
static const char* mySQLCollation[MAX_COLLATION] =
{
    {"ascii_general"},
    {"ascii"},
    {"big5_chinese"},
    {"big5"},
    {"cp1250_croatian"},
    {"cp1250_czech"},
    {"cp1250_general"},
    {"cp1250_polish"},
    {"cp1250"},
    {"cp1251_bulgarian"},
    {"cp1251_general"},
    {"cp1251"},
    {"cp1256_general"},
    {"cp1256"},
    {"cp850_general"},
    {"cp850"},
    {"cp852_general"},
    {"cp852"},
    {"cp932_japanese"},
    {"cp932"},
    {"euckr_korean"},
    {"euckr"},
    {"gb2312_chinese"},
    {"gb2312"},
    {"gbk_chinese"},
    {"gbk"},
    {"greek_general"},
    {"greek"},
    {"hebrew_general"},
    {"hebrew"},
    {"latin1_danish"},
    {"latin1_general"},
    {"latin1_german1"},
    {"latin1_spanish"},
    {"latin1_swedish"},
    {"latin1"},
    {"latin2_croatian"},
    {"latin2_czech"},
    {"latin2_general"},
    {"latin2_hungarian"},
    {"latin2"},
    {"latin5_turkish"},
    {"latin5"},
    {"macce_general"},
    {"macce"},
    {"sjis_japanese"},
    {"sjis"},
    {"tis620_thai"},
    {"tis620"},
    {"ucs2_czech"},
    {"ucs2_danish"},
    {"ucs2_esperanto"},
    {"ucs2_estonian"},
    {"ucs2_general"},
    {"ucs2_hungarian"},
    {"ucs2_icelandic"},
    {"ucs2_latvian"},
    {"ucs2_lithuanian"},
    {"ucs2_persian"},
    {"ucs2_polish"},
    {"ucs2_romanian"},
    {"ucs2_slovak"},
    {"ucs2_slovenian"},
    {"ucs2_spanish"},
    {"ucs2_spanish2"},
    {"ucs2_turkish"},
    {"ucs2_unicode"},
    {"ucs2"},
    {"ujis_japanese"},
    {"ujis"},
    {"utf8_czech"},
    {"utf8_danish"},
    {"utf8_esperanto"},
    {"utf8_estonian"},
    {"utf8_general"},
    {"utf8_hungarian"},
    {"utf8_icelandic"},
    {"utf8_latvian"},
    {"utf8_lithuanian"},
    {"utf8_persian"},
    {"utf8_polish"},
    {"utf8_romanian"},
    {"utf8_slovak"},
    {"utf8_slovenian"},
    {"utf8_spanish"},
    {"utf8_spanish2"},
    {"utf8_turkish"},
    {"utf8_unicode"},
    {"utf8"}
};

static const char* mySqlSortSequence[MAX_COLLATION] =
{
    {"QALA101F4"},
    {"QBLA101F4"},
    {"QACHT04B0"},
    {"QBCHT04B0"},
    {"QALA20481"},
    {"QBLA20481"},
    {"QCLA20481"},
    {"QDLA20481"},
    {"QELA20481"},
    {"QACYR0401"},
    {"QBCYR0401"},
    {"QCCYR0401"},
    {"QAARA01A4"},
    {"QBARA01A4"},
    {"QCLA101F4"},
    {"QDLA101F4"},
    {"QALA20366"},
    {"QBLA20366"},
    {"QAJPN04B0"},
    {"QBJPN04B0"},
    {"QAKOR04B0"},
    {"QBKOR04B0"},
    {"QACHS04B0"},
    {"QBCHS04B0"},
    {"QCCHS04B0"},
    {"QDCHS04B0"},
    {"QAELL036B"},
    {"QBELL036B"},
    {"QAHEB01A8"},
    {"QBHEB01A8"},
    {"QALA1047C"},
    {"QBLA1047C"},
    {"QCLA1047C"},
    {"QDLA1047C"},
    {"QELA1047C"},
    {"QFLA1047C"},
    {"QCLA20366"},
    {"QDLA20366"},
    {"QELA20366"},
    {"QFLA20366"},
    {"QGLA20366"},
    {"QATRK0402"},
    {"QBTRK0402"},
    {"QHLA20366"},
    {"QILA20366"},
    {"QCJPN04B0"},
    {"QDJPN04B0"},
    {"QATHA0346"},
    {"QBTHA0346"},
    {"ACS"},
    {"ADA"},
    {"AEO"},
    {"AET"},
    {"QAUCS04B0"},
    {"AHU"},
    {"AIS"},
    {"ALV"},
    {"ALT"},
    {"AFA"},
    {"APL"},
    {"ARO"},
    {"ASK"},
    {"ASL"},
    {"AES"},
    {"AES__TRADIT"},
    {"ATR"},
    {"AEN"},
    {"*HEX"},
    {"QEJPN04B0"},
    {"QFJPN04B0"},
    {"ACS"},
    {"ADA"},
    {"AEO"},
    {"AET"},
    {"QAUCS04B0"},
    {"AHU"},
    {"AIS"},
    {"ALV"},
    {"ALT"},
    {"AFA"},
    {"APL"},
    {"ARO"},
    {"ASK"},
    {"ASL"},
    {"AES"},
    {"AES__TRADIT"},
    {"ATR"},
    {"AEN"},
    {"*HEX"}
};

/**
  Get the IBM i sort sequence that corresponds to the given MySQL collation.

  @param fieldCharSet  The collated character set
  @param[out] rtnSortSequence  The corresponding sort sequence

  @return  0 if successful. Failure otherwise
*/
static int32 getAssociatedSortSequence(const CHARSET_INFO *fieldCharSet, const char** rtnSortSequence)
{
  DBUG_ENTER("ha_ibmdb2i::getAssociatedSortSequence");

  if (strcmp(fieldCharSet->csname,"binary") != 0)
  {
    int collationSearchLen = strlen(fieldCharSet->name);
    if (fieldCharSet->state & MY_CS_BINSORT)
      collationSearchLen -= 4;
    else
      collationSearchLen -= 3;

    uint16 loopCnt = 0;
    for (loopCnt; loopCnt < MAX_COLLATION; ++loopCnt)
    {
      if ((strlen(mySQLCollation[loopCnt]) == collationSearchLen) &&
          (strncmp((char*)mySQLCollation[loopCnt], fieldCharSet->name, collationSearchLen) == 0))
        break;
    }
    if (loopCnt == MAX_COLLATION)  // Did not find associated sort sequence
    {
      getErrTxt(DB2I_ERR_SRTSEQ);
      DBUG_RETURN(DB2I_ERR_SRTSEQ);
    }
    *rtnSortSequence = mySqlSortSequence[loopCnt];
  }

  DBUG_RETURN(0);
}

/**
  Update sort sequence information for a key.

  This function accumulates information about a key as it is called for each
  field composing the key. The caller should invoke the function for each field
  and (with the exception of the charset parm) preserve the values for the
  parms across invocations, until a particular key has been evaluated. Once
  the last field in the key has been evaluated, the fileSortSequence and
  fileSortSequenceLibrary parms will contain the correct information for
  creating the corresponding DB2 key.

  @param charset  The character set under consideration
  @param[in, out] fileSortSequenceType  The type of the current key's sort seq
  @param[in, out] fileSortSequence  The IBM i identifier for the DB2 sort sequence
                                    that corresponds

  @return  0 if successful. Failure otherwise
*/
int32 updateAssociatedSortSequence(const CHARSET_INFO* charset,
                                   char* fileSortSequenceType,
                                   char* fileSortSequence,
                                   char* fileSortSequenceLibrary)
{
  DBUG_ENTER("ha_ibmdb2i::updateAssociatedSortSequence");
  DBUG_ASSERT(charset);
  if (strcmp(charset->csname,"binary") != 0)
  {
    char newSortSequence[11] = "";
    char newSortSequenceType = ' ';
    const char* foundSortSequence;
    int rc = getAssociatedSortSequence(charset, &foundSortSequence);
    if (rc) DBUG_RETURN (rc);
    switch(foundSortSequence[0])
    {
      case '*': // Binary
        strcat(newSortSequence,foundSortSequence);
        newSortSequenceType = 'B';
        break;
      case 'Q': // Non-ICU sort sequence
        strcat(newSortSequence,foundSortSequence);
        if ((charset->state & MY_CS_BINSORT) != 0)
        {
          strcat(newSortSequence,"U");
        }
        else if ((charset->state & MY_CS_CSSORT) != 0)
        {
          strcat(newSortSequence,"U");
        }
        else
        {
          strcat(newSortSequence,"S");
        }
        newSortSequenceType = 'N';
        break;
      default: // ICU sort sequence
	{
          if ((charset->state & MY_CS_CSSORT) == 0)
          {
            if (osVersion.v >= 6)
              strcat(newSortSequence,"I34"); // ICU 3.4
            else
              strcat(newSortSequence,"I26"); // ICU 2.6.1
          }
          strcat(newSortSequence,foundSortSequence);
          newSortSequenceType = 'I';
        }
        break;
    }
    if (*fileSortSequenceType == ' ') // If no sort sequence has been set yet
    {
      // Set associated sort sequence
      strcpy(fileSortSequence,newSortSequence);
      strcpy(fileSortSequenceLibrary,"QSYS");
      *fileSortSequenceType = newSortSequenceType;
    }
    else if (strcmp(fileSortSequence,newSortSequence) != 0)
    {
      // Only one sort sequence/collation is supported for each DB2 index.
      getErrTxt(DB2I_ERR_MIXED_COLLATIONS);
      DBUG_RETURN(DB2I_ERR_MIXED_COLLATIONS);
    }
  }

  DBUG_RETURN(0);
}
<commit_msg>Bug#44020 Unicode Swedish collations not working with IBMDB2I<commit_after>/*
Licensed Materials - Property of IBM
DB2 Storage Engine Enablement
Copyright IBM Corporation 2007,2008
All rights reserved

Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
 (a) Redistributions of source code must retain this list of conditions, the
     copyright notice in section {d} below, and the disclaimer following this
     list of conditions.
 (b) Redistributions in binary form must reproduce this list of conditions, the
     copyright notice in section (d) below, and the disclaimer following this
     list of conditions, in the documentation and/or other materials provided
     with the distribution.
 (c) The name of IBM may not be used to endorse or promote products derived from
     this software without specific prior written permission.
 (d) The text of the required copyright notice is:
       Licensed Materials - Property of IBM
       DB2 Storage Engine Enablement
       Copyright IBM Corporation 2007,2008
       All rights reserved

THIS SOFTWARE IS PROVIDED BY IBM CORPORATION "AS IS" AND ANY EXPRESS OR IMPLIED
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
SHALL IBM CORPORATION BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
OF SUCH DAMAGE.
*/

#include "db2i_collationSupport.h"
#include "db2i_errors.h"

/*
  The following arrays define a mapping between MySQL collation names and
  corresponding IBM i sort sequences. The mapping is a 1-to-1 correlation
  between corresponding array slots but is incomplete without case-sensitivity
  markers dynamically added to the mySqlSortSequence names.
*/
#define MAX_COLLATION 89
static const char* mySQLCollation[MAX_COLLATION] =
{
    {"ascii_general"},
    {"ascii"},
    {"big5_chinese"},
    {"big5"},
    {"cp1250_croatian"},
    {"cp1250_czech"},
    {"cp1250_general"},
    {"cp1250_polish"},
    {"cp1250"},
    {"cp1251_bulgarian"},
    {"cp1251_general"},
    {"cp1251"},
    {"cp1256_general"},
    {"cp1256"},
    {"cp850_general"},
    {"cp850"},
    {"cp852_general"},
    {"cp852"},
    {"cp932_japanese"},
    {"cp932"},
    {"euckr_korean"},
    {"euckr"},
    {"gb2312_chinese"},
    {"gb2312"},
    {"gbk_chinese"},
    {"gbk"},
    {"greek_general"},
    {"greek"},
    {"hebrew_general"},
    {"hebrew"},
    {"latin1_danish"},
    {"latin1_general"},
    {"latin1_german1"},
    {"latin1_spanish"},
    {"latin1_swedish"},
    {"latin1"},
    {"latin2_croatian"},
    {"latin2_czech"},
    {"latin2_general"},
    {"latin2_hungarian"},
    {"latin2"},
    {"latin5_turkish"},
    {"latin5"},
    {"macce_general"},
    {"macce"},
    {"sjis_japanese"},
    {"sjis"},
    {"tis620_thai"},
    {"tis620"},
    {"ucs2_czech"},
    {"ucs2_danish"},
    {"ucs2_esperanto"},
    {"ucs2_estonian"},
    {"ucs2_general"},
    {"ucs2_hungarian"},
    {"ucs2_icelandic"},
    {"ucs2_latvian"},
    {"ucs2_lithuanian"},
    {"ucs2_persian"},
    {"ucs2_polish"},
    {"ucs2_romanian"},
    {"ucs2_slovak"},
    {"ucs2_slovenian"},
    {"ucs2_spanish"},
    {"ucs2_swedish"},
    {"ucs2_turkish"},
    {"ucs2_unicode"},
    {"ucs2"},
    {"ujis_japanese"},
    {"ujis"},
    {"utf8_czech"},
    {"utf8_danish"},
    {"utf8_esperanto"},
    {"utf8_estonian"},
    {"utf8_general"},
    {"utf8_hungarian"},
    {"utf8_icelandic"},
    {"utf8_latvian"},
    {"utf8_lithuanian"},
    {"utf8_persian"},
    {"utf8_polish"},
    {"utf8_romanian"},
    {"utf8_slovak"},
    {"utf8_slovenian"},
    {"utf8_spanish"},
    {"utf8_swedish"},
    {"utf8_turkish"},
    {"utf8_unicode"},
    {"utf8"}
};

static const char* mySqlSortSequence[MAX_COLLATION] =
{
    {"QALA101F4"},
    {"QBLA101F4"},
    {"QACHT04B0"},
    {"QBCHT04B0"},
    {"QALA20481"},
    {"QBLA20481"},
    {"QCLA20481"},
    {"QDLA20481"},
    {"QELA20481"},
    {"QACYR0401"},
    {"QBCYR0401"},
    {"QCCYR0401"},
    {"QAARA01A4"},
    {"QBARA01A4"},
    {"QCLA101F4"},
    {"QDLA101F4"},
    {"QALA20366"},
    {"QBLA20366"},
    {"QAJPN04B0"},
    {"QBJPN04B0"},
    {"QAKOR04B0"},
    {"QBKOR04B0"},
    {"QACHS04B0"},
    {"QBCHS04B0"},
    {"QCCHS04B0"},
    {"QDCHS04B0"},
    {"QAELL036B"},
    {"QBELL036B"},
    {"QAHEB01A8"},
    {"QBHEB01A8"},
    {"QALA1047C"},
    {"QBLA1047C"},
    {"QCLA1047C"},
    {"QDLA1047C"},
    {"QELA1047C"},
    {"QFLA1047C"},
    {"QCLA20366"},
    {"QDLA20366"},
    {"QELA20366"},
    {"QFLA20366"},
    {"QGLA20366"},
    {"QATRK0402"},
    {"QBTRK0402"},
    {"QHLA20366"},
    {"QILA20366"},
    {"QCJPN04B0"},
    {"QDJPN04B0"},
    {"QATHA0346"},
    {"QBTHA0346"},
    {"ACS"},
    {"ADA"},
    {"AEO"},
    {"AET"},
    {"QAUCS04B0"},
    {"AHU"},
    {"AIS"},
    {"ALV"},
    {"ALT"},
    {"AFA"},
    {"APL"},
    {"ARO"},
    {"ASK"},
    {"ASL"},
    {"AES"},
    {"ASW"},
    {"ATR"},
    {"AEN"},
    {"*HEX"},
    {"QEJPN04B0"},
    {"QFJPN04B0"},
    {"ACS"},
    {"ADA"},
    {"AEO"},
    {"AET"},
    {"QAUCS04B0"},
    {"AHU"},
    {"AIS"},
    {"ALV"},
    {"ALT"},
    {"AFA"},
    {"APL"},
    {"ARO"},
    {"ASK"},
    {"ASL"},
    {"AES"},
    {"ASW"},
    {"ATR"},
    {"AEN"},
    {"*HEX"}
};

/**
  Get the IBM i sort sequence that corresponds to the given MySQL collation.

  @param fieldCharSet  The collated character set
  @param[out] rtnSortSequence  The corresponding sort sequence

  @return  0 if successful. Failure otherwise
*/
static int32 getAssociatedSortSequence(const CHARSET_INFO *fieldCharSet, const char** rtnSortSequence)
{
  DBUG_ENTER("ha_ibmdb2i::getAssociatedSortSequence");

  if (strcmp(fieldCharSet->csname,"binary") != 0)
  {
    int collationSearchLen = strlen(fieldCharSet->name);
    if (fieldCharSet->state & MY_CS_BINSORT)
      collationSearchLen -= 4;
    else
      collationSearchLen -= 3;

    uint16 loopCnt = 0;
    for (loopCnt; loopCnt < MAX_COLLATION; ++loopCnt)
    {
      if ((strlen(mySQLCollation[loopCnt]) == collationSearchLen) &&
          (strncmp((char*)mySQLCollation[loopCnt], fieldCharSet->name, collationSearchLen) == 0))
        break;
    }
    if (loopCnt == MAX_COLLATION)  // Did not find associated sort sequence
    {
      getErrTxt(DB2I_ERR_SRTSEQ);
      DBUG_RETURN(DB2I_ERR_SRTSEQ);
    }
    *rtnSortSequence = mySqlSortSequence[loopCnt];
  }

  DBUG_RETURN(0);
}

/**
  Update sort sequence information for a key.

  This function accumulates information about a key as it is called for each
  field composing the key. The caller should invoke the function for each field
  and (with the exception of the charset parm) preserve the values for the
  parms across invocations, until a particular key has been evaluated. Once
  the last field in the key has been evaluated, the fileSortSequence and
  fileSortSequenceLibrary parms will contain the correct information for
  creating the corresponding DB2 key.

  @param charset  The character set under consideration
  @param[in, out] fileSortSequenceType  The type of the current key's sort seq
  @param[in, out] fileSortSequence  The IBM i identifier for the DB2 sort sequence
                                    that corresponds

  @return  0 if successful. Failure otherwise
*/
int32 updateAssociatedSortSequence(const CHARSET_INFO* charset,
                                   char* fileSortSequenceType,
                                   char* fileSortSequence,
                                   char* fileSortSequenceLibrary)
{
  DBUG_ENTER("ha_ibmdb2i::updateAssociatedSortSequence");
  DBUG_ASSERT(charset);
  if (strcmp(charset->csname,"binary") != 0)
  {
    char newSortSequence[11] = "";
    char newSortSequenceType = ' ';
    const char* foundSortSequence;
    int rc = getAssociatedSortSequence(charset, &foundSortSequence);
    if (rc) DBUG_RETURN (rc);
    switch(foundSortSequence[0])
    {
      case '*': // Binary
        strcat(newSortSequence,foundSortSequence);
        newSortSequenceType = 'B';
        break;
      case 'Q': // Non-ICU sort sequence
        strcat(newSortSequence,foundSortSequence);
        if ((charset->state & MY_CS_BINSORT) != 0)
        {
          strcat(newSortSequence,"U");
        }
        else if ((charset->state & MY_CS_CSSORT) != 0)
        {
          strcat(newSortSequence,"U");
        }
        else
        {
          strcat(newSortSequence,"S");
        }
        newSortSequenceType = 'N';
        break;
      default: // ICU sort sequence
	{
          if ((charset->state & MY_CS_CSSORT) == 0)
          {
            if (osVersion.v >= 6)
              strcat(newSortSequence,"I34"); // ICU 3.4
            else
              strcat(newSortSequence,"I26"); // ICU 2.6.1
          }
          strcat(newSortSequence,foundSortSequence);
          newSortSequenceType = 'I';
        }
        break;
    }
    if (*fileSortSequenceType == ' ') // If no sort sequence has been set yet
    {
      // Set associated sort sequence
      strcpy(fileSortSequence,newSortSequence);
      strcpy(fileSortSequenceLibrary,"QSYS");
      *fileSortSequenceType = newSortSequenceType;
    }
    else if (strcmp(fileSortSequence,newSortSequence) != 0)
    {
      // Only one sort sequence/collation is supported for each DB2 index.
      getErrTxt(DB2I_ERR_MIXED_COLLATIONS);
      DBUG_RETURN(DB2I_ERR_MIXED_COLLATIONS);
    }
  }

  DBUG_RETURN(0);
}
<|endoftext|>


<commit_before>/*************************************************************************
 *
 *  $RCSfile: helper.hxx,v $
 *
 *  $Revision: 1.4 $
 *
 *  last change: $Author: pb $ $Date: 2001-07-14 12:35:31 $
 *
 *  The Contents of this file are made available subject to the terms of
 *  either of the following licenses
 *
 *         - GNU Lesser General Public License Version 2.1
 *         - Sun Industry Standards Source License Version 1.1
 *
 *  Sun Microsystems Inc., October, 2000
 *
 *  GNU Lesser General Public License Version 2.1
 *  =============================================
 *  Copyright 2000 by Sun Microsystems, Inc.
 *  901 San Antonio Road, Palo Alto, CA 94303, USA
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public
 *  License version 2.1, as published by the Free Software Foundation.
 *
 *  This library is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this library; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 *  MA  02111-1307  USA
 *
 *
 *  Sun Industry Standards Source License Version 1.1
 *  =================================================
 *  The contents of this file are subject to the Sun Industry Standards
 *  Source License Version 1.1 (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.openoffice.org/license.html.
 *
 *  Software provided under this License is provided on an "AS IS" basis,
 *  WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING,
 *  WITHOUT LIMITATION, WARRANTIES THAT THE SOFTWARE IS FREE OF DEFECTS,
 *  MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE, OR NON-INFRINGING.
 *  See the License for the specific provisions governing your rights and
 *  obligations concerning the Software.
 *
 *  The Initial Developer of the Original Code is: Sun Microsystems, Inc.
 *
 *  Copyright: 2000 by Sun Microsystems, Inc.
 *
 *  All Rights Reserved.
 *
 *  Contributor(s): _______________________________________
 *
 *
 ************************************************************************/
#ifndef _SFX_HELPER_HXX
#define _SFX_HELPER_HXX

// include ---------------------------------------------------------------

#ifndef _COM_SUN_STAR_UNO_SEQUENCE_HXX_
#include <com/sun/star/uno/Sequence.hxx>
#endif
#ifndef  _COM_SUN_STAR_UCB_NAMECLASH_HPP_
#include <com/sun/star/ucb/NameClash.hpp>
#endif

#include <tools/string.hxx>
#include <tools/errcode.hxx>
#include <tools/bigint.hxx>

// class SfxContentHelper ------------------------------------------------

class SfxContentHelper
{
private:
    static sal_Bool             Transfer_Impl( const String& rSource, const String& rDest, sal_Bool bMoveData,
                                                    sal_Int32 nNameClash );

public:
    static sal_Bool             IsDocument( const String& rContent );
    static sal_Bool             IsFolder( const String& rContent );
    static sal_Bool             GetTitle( const String& rContent, String& rTitle );
    static sal_Bool             Kill( const String& rContent );

    static ::com::sun::star::uno::Sequence< ::rtl::OUString >
                                GetFolderContents( const String& rFolder, sal_Bool bFolder, sal_Bool bSorted = sal_False );
    static ::com::sun::star::uno::Sequence< ::rtl::OUString >
                                GetFolderContentProperties( const String& rFolder, sal_Bool bFolder );
    static ::com::sun::star::uno::Sequence< ::rtl::OUString >
                                GetResultSet( const String& rURL );
    static ::com::sun::star::uno::Sequence< ::rtl::OUString >
                                GetHelpTreeViewContents( const String& rURL );

    static sal_Bool             CopyTo( const String& rSource, const String& rDest );
    static sal_Bool             MoveTo( const String& rSource, const String& rDest, sal_Int32 nNameClash = com::sun::star::ucb::NameClash::ERROR );

    static sal_Bool             MakeFolder( const String& rFolder );
    static ErrCode              QueryDiskSpace( const String& rPath, sal_Int64& rFreeBytes );
    static ULONG                GetSize( const String& rContent );
    static sal_Bool             IsYounger( const String& rIsYoung, const String& rIsOlder );

    // please don't use this!
    static sal_Bool             Exists( const String& rContent );
    static sal_Bool             Find( const String& rFolder, const String& rName, String& rFile );
};

#endif // #ifndef _SFX_HELPER_HXX

<commit_msg>fix: #89507# #88139# Get<commit_after>/*************************************************************************
 *
 *  $RCSfile: helper.hxx,v $
 *
 *  $Revision: 1.5 $
 *
 *  last change: $Author: pb $ $Date: 2001-08-10 08:49:49 $
 *
 *  The Contents of this file are made available subject to the terms of
 *  either of the following licenses
 *
 *         - GNU Lesser General Public License Version 2.1
 *         - Sun Industry Standards Source License Version 1.1
 *
 *  Sun Microsystems Inc., October, 2000
 *
 *  GNU Lesser General Public License Version 2.1
 *  =============================================
 *  Copyright 2000 by Sun Microsystems, Inc.
 *  901 San Antonio Road, Palo Alto, CA 94303, USA
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public
 *  License version 2.1, as published by the Free Software Foundation.
 *
 *  This library is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this library; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 *  MA  02111-1307  USA
 *
 *
 *  Sun Industry Standards Source License Version 1.1
 *  =================================================
 *  The contents of this file are subject to the Sun Industry Standards
 *  Source License Version 1.1 (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.openoffice.org/license.html.
 *
 *  Software provided under this License is provided on an "AS IS" basis,
 *  WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING,
 *  WITHOUT LIMITATION, WARRANTIES THAT THE SOFTWARE IS FREE OF DEFECTS,
 *  MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE, OR NON-INFRINGING.
 *  See the License for the specific provisions governing your rights and
 *  obligations concerning the Software.
 *
 *  The Initial Developer of the Original Code is: Sun Microsystems, Inc.
 *
 *  Copyright: 2000 by Sun Microsystems, Inc.
 *
 *  All Rights Reserved.
 *
 *  Contributor(s): _______________________________________
 *
 *
 ************************************************************************/
#ifndef _SFX_HELPER_HXX
#define _SFX_HELPER_HXX

// include ---------------------------------------------------------------

#ifndef _COM_SUN_STAR_UNO_SEQUENCE_HXX_
#include <com/sun/star/uno/Sequence.hxx>
#endif
#ifndef  _COM_SUN_STAR_UCB_NAMECLASH_HPP_
#include <com/sun/star/ucb/NameClash.hpp>
#endif

#include <tools/string.hxx>
#include <tools/errcode.hxx>
#include <tools/bigint.hxx>

// class SfxContentHelper ------------------------------------------------

class SfxContentHelper
{
private:
    static sal_Bool             Transfer_Impl( const String& rSource, const String& rDest, sal_Bool bMoveData,
                                                    sal_Int32 nNameClash );

public:
    static sal_Bool             IsDocument( const String& rContent );
    static sal_Bool             IsFolder( const String& rContent );
    static sal_Bool             GetTitle( const String& rContent, String& rTitle );
    static sal_Bool             Kill( const String& rContent );

    static ::com::sun::star::uno::Sequence< ::rtl::OUString >
                                GetFolderContents( const String& rFolder, sal_Bool bFolder, sal_Bool bSorted = sal_False );
    static ::com::sun::star::uno::Sequence< ::rtl::OUString >
                                GetFolderContentProperties( const String& rFolder, sal_Bool bFolder );
    static ::com::sun::star::uno::Sequence< ::rtl::OUString >
                                GetResultSet( const String& rURL );
    static ::com::sun::star::uno::Sequence< ::rtl::OUString >
                                GetHelpTreeViewContents( const String& rURL );
    static String               GetActiveHelpString( const String& rURL );

    static sal_Bool             CopyTo( const String& rSource, const String& rDest );
    static sal_Bool             MoveTo( const String& rSource, const String& rDest, sal_Int32 nNameClash = com::sun::star::ucb::NameClash::ERROR );

    static sal_Bool             MakeFolder( const String& rFolder );
    static ErrCode              QueryDiskSpace( const String& rPath, sal_Int64& rFreeBytes );
    static ULONG                GetSize( const String& rContent );
    static sal_Bool             IsYounger( const String& rIsYoung, const String& rIsOlder );

    // please don't use this!
    static sal_Bool             Exists( const String& rContent );
    static sal_Bool             Find( const String& rFolder, const String& rName, String& rFile );
};

#endif // #ifndef _SFX_HELPER_HXX

<|endoftext|>


<commit_before>/**
 * This file is part of the CernVM File System.
 */

#include <gtest/gtest.h>

#include <utility>

#include "json_document.h"
#include "json_document_write.h"
#include "util/pointer.h"

TEST(T_Json, Empty) {
  UniquePtr<JsonDocument> json(JsonDocument::Create("{}"));
  EXPECT_TRUE(json.IsValid());
  EXPECT_EQ("{}", json->PrintCanonical());
  UniquePtr<JsonDocument> json2(JsonDocument::Create(""));
  EXPECT_FALSE(json2.IsValid());
}

TEST(T_Json, Complex) {
  UniquePtr<JsonDocument> json(JsonDocument::Create(
      "{\"string\": \"a string with spaces\",\n"
      " \"number\": 42,\n"
      " \"float\": 0.1,\n"
      " \"switch\": true,\n"
      " \"void\": null,\n"
      " \"vector\": [true, false, null, 0.0, 7, \"foo\", {1, 2}, {}, []],\n"
      " \"compound\": {\"a\": 2, \"b\": [1, 2, 3], \"c\": {}}}"));
  EXPECT_TRUE(json.IsValid());
  EXPECT_EQ(
      "{\"string\":\"a string with spaces\","
      "\"number\":42,"
      "\"float\":0.100,"
      "\"switch\":true,"
      "\"void\":null,"
      "\"vector\":[true,false,null,0.000,7,\"foo\",{1,2},{},[]],"
      "\"compound\":{\"a\":2,\"b\":[1,2,3],\"c\":{}}}",
      json->PrintCanonical());
}

TEST(T_Json, StringEscape) {
  UniquePtr<JsonDocument> json(JsonDocument::Create(
      "{\"string\": \"a \\\"string\\\" with special chars\"}"));
  ASSERT_TRUE(json.IsValid());
  EXPECT_EQ("{\"string\":\"a \\\"string\\\" with special chars\"}",
            json->PrintCanonical());
}

TEST(T_Json, SearchInObject) {
  UniquePtr<JsonDocument> json(JsonDocument::Create(
      "{\"string\": \"a \\\"string\\\" with special chars\"}"));
  ASSERT_TRUE(json.IsValid());
  JSON *result = json->SearchInObject(json->root(), "string", JSON_STRING);
  EXPECT_TRUE(result != NULL);
  result = json->SearchInObject(json->root(), "string", JSON_INT);
  EXPECT_EQ(NULL, result);
  result = json->SearchInObject(json->root(), "xyz", JSON_INT);
  EXPECT_EQ(NULL, result);
  result = json->SearchInObject(NULL, "string", JSON_STRING);
  EXPECT_EQ(NULL, result);
  result =
      json->SearchInObject(json->root()->first_child, "string", JSON_STRING);
  EXPECT_EQ(NULL, result);
}

TEST(T_Json, GenerateValidJsonString) {
  JsonStringGenerator input;
  input.AddQuoted("f1", "v1");
  input.AddQuoted("f2", "v2");
  input.AddQuoted("f3", "v3");
  input.AddUnquoted("interger", "12");

  std::string output = input.GenerateString();

  ASSERT_EQ("{\"f1\":\"v1\",\"f2\":\"v2\",\"f3\":\"v3\",\"integer\":12}", output);

  UniquePtr<JsonDocument> json(JsonDocument::Create(output));
  ASSERT_TRUE(json.IsValid());
}
<commit_msg>[LINT] length of string<commit_after>/**
 * This file is part of the CernVM File System.
 */

#include <gtest/gtest.h>

#include <utility>

#include "json_document.h"
#include "json_document_write.h"
#include "util/pointer.h"

TEST(T_Json, Empty) {
  UniquePtr<JsonDocument> json(JsonDocument::Create("{}"));
  EXPECT_TRUE(json.IsValid());
  EXPECT_EQ("{}", json->PrintCanonical());
  UniquePtr<JsonDocument> json2(JsonDocument::Create(""));
  EXPECT_FALSE(json2.IsValid());
}

TEST(T_Json, Complex) {
  UniquePtr<JsonDocument> json(JsonDocument::Create(
      "{\"string\": \"a string with spaces\",\n"
      " \"number\": 42,\n"
      " \"float\": 0.1,\n"
      " \"switch\": true,\n"
      " \"void\": null,\n"
      " \"vector\": [true, false, null, 0.0, 7, \"foo\", {1, 2}, {}, []],\n"
      " \"compound\": {\"a\": 2, \"b\": [1, 2, 3], \"c\": {}}}"));
  EXPECT_TRUE(json.IsValid());
  EXPECT_EQ(
      "{\"string\":\"a string with spaces\","
      "\"number\":42,"
      "\"float\":0.100,"
      "\"switch\":true,"
      "\"void\":null,"
      "\"vector\":[true,false,null,0.000,7,\"foo\",{1,2},{},[]],"
      "\"compound\":{\"a\":2,\"b\":[1,2,3],\"c\":{}}}",
      json->PrintCanonical());
}

TEST(T_Json, StringEscape) {
  UniquePtr<JsonDocument> json(JsonDocument::Create(
      "{\"string\": \"a \\\"string\\\" with special chars\"}"));
  ASSERT_TRUE(json.IsValid());
  EXPECT_EQ("{\"string\":\"a \\\"string\\\" with special chars\"}",
            json->PrintCanonical());
}

TEST(T_Json, SearchInObject) {
  UniquePtr<JsonDocument> json(JsonDocument::Create(
      "{\"string\": \"a \\\"string\\\" with special chars\"}"));
  ASSERT_TRUE(json.IsValid());
  JSON *result = json->SearchInObject(json->root(), "string", JSON_STRING);
  EXPECT_TRUE(result != NULL);
  result = json->SearchInObject(json->root(), "string", JSON_INT);
  EXPECT_EQ(NULL, result);
  result = json->SearchInObject(json->root(), "xyz", JSON_INT);
  EXPECT_EQ(NULL, result);
  result = json->SearchInObject(NULL, "string", JSON_STRING);
  EXPECT_EQ(NULL, result);
  result =
      json->SearchInObject(json->root()->first_child, "string", JSON_STRING);
  EXPECT_EQ(NULL, result);
}

TEST(T_Json, GenerateValidJsonString) {
  JsonStringGenerator input;
  input.AddQuoted("f1", "v1");
  input.AddQuoted("f2", "v2");
  input.AddQuoted("f3", "v3");
  input.AddUnquoted("interger", "12");

  std::string output = input.GenerateString();

  ASSERT_EQ("{\"f1\":\"v1\",\"f2\":\"v2\",\"f3\":\"v3\",\"integer\":12}",
            output);

  UniquePtr<JsonDocument> json(JsonDocument::Create(output));
  ASSERT_TRUE(json.IsValid());
}
<|endoftext|>


<commit_before>// (c) COPYRIGHT URI/MIT 1994-1999
// Please read the full copyright statement in the file COPYRIGHT.
//
// Authors:
//      pwest       Patrick West (pwest@ucar.edu)

// pwest 12/02/2002

#ifdef __GNUG__
#pragma implementation
#endif

#include "LongIterAdapter.h"

LongIterAdapter::
LongIterAdapter( std::vector<long> &vec ) :
    _vec( vec )
{
}

LongIterAdapter::
~LongIterAdapter(void)
{
}

void LongIterAdapter::
first(void)
{
    _i = _vec.begin() ;
}

void LongIterAdapter::
next(void)
{
    _i++ ;
}

LongIterAdapter::
operator bool(void)
{
    return ( _i != _vec.end() ) ;
}

bool LongIterAdapter::
operator==( const IteratorAdapter &i )
{
    return _i == ((const LongIterAdapter &)i)._i ;
}

long LongIterAdapter::
entry(void)
{
    return *_i ;
}

LongIterAdapter::iter &LongIterAdapter::
getIterator(void)
{
    return _i ;
}

<commit_msg>reformatted source file and replaced (void) with ()<commit_after>// (c) COPYRIGHT URI/MIT 1994-1999
// Please read the full copyright statement in the file COPYRIGHT.
//
// Authors:
//      pwest       Patrick West (pwest@ucar.edu)

// pwest 12/02/2002

#ifdef __GNUG__
#pragma implementation
#endif

#include "LongIterAdapter.h"

LongIterAdapter::LongIterAdapter( std::vector<long> &vec ) :
    _vec( vec )
{
}

LongIterAdapter::~LongIterAdapter( )
{
}

void
LongIterAdapter::first( )
{
    _i = _vec.begin() ;
}

void
LongIterAdapter::next( )
{
    _i++ ;
}

LongIterAdapter::operator bool( )
{
    return ( _i != _vec.end() ) ;
}

bool
LongIterAdapter::operator==( const IteratorAdapter &i )
{
    return _i == ((const LongIterAdapter &)i)._i ;
}

long
LongIterAdapter::entry( )
{
    return *_i ;
}

LongIterAdapter::iter &
LongIterAdapter::getIterator( )
{
    return _i ;
}

<|endoftext|>


<commit_before>/** @file
 *
 * Client/server program for the drive test.
 */
#include <cstdio>
#include "Chassis_Control/DriveData.h"

/* Manually disable client mode on ARM machines (e.g. the Raspberry Pi, which
   gives us enough grief anyway... */
#if defined(__arm__) && ! defined(DISABLE_CLIENT)
# define DISABLE_CLIENT 1
#endif

/* ****************************************************************
 * Help text stuff.
 */
#if ! defined(DISABLE_CLIENT)
#  define PRINT_USAGE(stream) fprintf(stream, "Usage: %s [OPTION]... ADDRESS PORT [EVDEV]\n", argv[0])

#  define HELP_TEXT "\
Simple controls-test program.\n\
\n\
Options:\n\
  -h	Show this help.\n\
\n\
Client mode is assumed when EVDEV argument is present; otherwise server mode is\n\
enabled.\n"
#else
#  define PRINT_USAGE(stream) fprintf(stream, "Usage: %s [OPTION]... ADDRESS PORT\n", argv[0])
#  define HELP_TEXT "\
Simple controls-test program: server-only build.\n\
\n\
Options:\n\
  -h	Show this help.\n\
\n"
#endif  /* ! defined(DISABLE_CLIENT) */
/* **************************************************************** */

#include <boost/asio/connect.hpp>
#include <boost/asio/ip/tcp.hpp>
#include <boost/asio/signal_set.hpp>
#include <crisp/comms/BasicNode.hh>
#include <crisp/comms/NodeServer.hh>

/* Define some aliases for our communication-types for convenience. */
using namespace crisp::comms;
typedef BasicNode<boost::asio::ip::tcp> Node;
typedef typename Node::Protocol Protocol;
typedef typename Protocol::endpoint Endpoint;

#include "gnublin_i2c.hpp"

#define I2C_BUS "/dev/i2c-0"
#define I2C_SLAVE_ADDRESS 5

static int
run_server(boost::asio::io_service& service,
           const Endpoint& target_endpoint)

{
  crisp::comms::NodeServer<Node> server ( service, target_endpoint );

  gnublin_i2c i2c ( I2C_BUS, I2C_SLAVE_ADDRESS );

  /* I don't trust this "Gnublin" code (it's terribly written and I had to fix
     it to even get it to compile).   We'll be replacing it eventually. */
  i2c.setDevicefile(I2C_BUS);
  i2c.setAddress(I2C_SLAVE_ADDRESS);

  if ( i2c.fail() )             /* Won't happen because the Gnublin stuff opens
                                   the device on-demand.  /Le sigh.../ */
    return 1;

  DriveData data;
  unsigned char* data_pointer = reinterpret_cast<unsigned char*>(&data);
  memset(data_pointer, 0, sizeof(data));

  using namespace crisp::comms::keywords;

  /* Declare our interface configuration. */
  server.configuration.add_module( "LED", 2)
    .add_input<int8_t>({ "left",  { _neutral = 0, _minimum = -127, _maximum = 128 } })
    .add_input<int8_t>({ "right", { _neutral = 0, _minimum = -127, _maximum = 128 } });

  /* Override the handler run when we receive a module-control packet. */
  server.dispatcher.module_control.received.connect(
    [&](Node& node, const crisp::comms::ModuleControl& mc)
    {
      fprintf(stderr, "[0x%0x] \033[1;33mModule-control received\033[0m\n", THREAD_ID);

      const crisp::comms::DataValue<>* dv ( nullptr );

      /* Update our input-value array. */
      if ( (dv = mc.value_for("left")) != nullptr )
          data.left_motor = dv->get<int8_t>();
      if ( (dv = mc.value_for("right")) != nullptr )
          data.right_motor = dv->get<int8_t>();

      /* This call here is just so EW EW EW EW EW.  There's a reason for it, of
         course, but that reason is irrelevant with the way we're using I2C. */
      i2c.send(data_pointer, sizeof(data));

      if ( i2c.fail() )
        {
          perror("write");
        }
    });

  server.run();
  return 0;
}

/* Disable client functionality on the Raspberry Pi.

   _With_ this code (and using the old controller code based on the
   terribly-bloated Boost.Signals2 library), I had trouble getting the program
   to compile on the Pi without running out of memory.

   We don't need to support client mode on the Pi, so we might as well keep it
   disabled anyway.
 */
#if ! defined(DISABLE_CLIENT)

#include <crisp/input/EvDevController.hh>
#include <float.h>

/** Update a ModuleControl instance to reflect the input coordinates.
 *
 * @param mc ModuleControl to update.
 *
 * @param left Value of left stick y axis.
 *
 * @param right Value of right stick y axis.
 *
 */
void
update_control(crisp::comms::ModuleControl& mc,
               float left, float right)
{
  fprintf(stderr, "left %f, right %f:", left, right);

  mc.set<int8_t>("left", static_cast<int8_t>(left * 127));
  mc.set<int8_t>("right", static_cast<int8_t>(right * 127));
}

/** Run the client code, connecting to the specified endpoint and taking input
 *  from the specified Linux `evdev` input device.
 */
static int
run_client(boost::asio::io_service& service,
           const Endpoint& target_endpoint,
           const char* evdev)
{
  /* Create a socket, and try to connect it to the specified endpoint. */
  boost::system::error_code ec;
  typename Protocol::socket socket ( service );
  typename Protocol::resolver resolver ( service );

  boost::asio::connect(socket, resolver.resolve(target_endpoint), ec);

  if ( ec )
    { fprintf(stderr, "connect: %s\n", strerror(ec.value()));
      return 1; }
  else
    {
      /* We're going to be modifying the ModuleControl instance on-demand
         (whenever the input controller changes state), but sending it at a
         fixed rate.  Since this will be happening in different threads and
         there's no easy way to coordinate the memory access and modification
         otherwise,  we need a mutex to keep everything in sync.

         (If we didn't do this, we could e.g., get halfway through updating the
         module control instance, send a control packet based on that [likely
         invalid] data, and then finish updating.)
      */
      std::mutex mutex;
      ModuleControl mc;
      bool have_config ( false );
      float left, right;

      /* Create the input device. */
      using namespace crisp::input;
      EvDevController controller ( evdev );
      assert(controller.axes.size() >= 4);

      /* Make sure the axes we'll be using are in absolute mode. */
      for ( size_t i ( 0 ); i < 2; ++i )
        {
          controller.axes[i].set_coefficients({1, 0, 0, 0});
          if ( controller.axes[i].type != Axis::Type::ABSOLUTE )
            {                     /* Set up emulation for the axis. */
              controller.axes[i].mode = Axis::Type::ABSOLUTE;

              /* We need to manually initialize the absolute-axis raw-value
                 configuration when an axis isn't actually absolute... */
              controller.axes[i].raw.minimum =
                - (controller.axes[i].raw.maximum = 256);
              controller.axes[i].raw.neutral = 0;

              /* set cubic mapping: value = 1 * x^3 + 0 * x^2 + 0 * x + 0.  */
            }
        }

      /* Add event handlers for each of the axes we're interested in. */
      controller.axes[1].hook([&](const Axis& axis, Axis::State state)
                              {
                                if ( have_config )
                                  {
                                    std::unique_lock<std::mutex> lock ( mutex );
                                    left = state.value;
                                    update_control(mc, left, right);
                                  }
                              });
      controller.axes[3].hook([&](const Axis& axis, Axis::State state)
                              {
                                if ( have_config )
                                  {
                                    std::unique_lock<std::mutex> lock ( mutex );
                                    right = state.value;
                                    update_control(mc, left, right);
                                  }
                              });

      /* Instantiate the network node. */
      Node node ( std::move(socket), NodeRole::MASTER );

      /* Override the default configuration-response-recieved handler with
         this:

         - First, make sure that the received configuration is at least similar to what
           we expect; shut down the node if it isn't.

         - Store the received configuration.

         - Set up the module-control object to
      */
      node.dispatcher.configuration_response.received.connect(
        [&](Node& _node, const Configuration& configuration)
        {
          have_config = true;
          node.configuration = configuration;
          mc.reset(&(node.configuration.modules[0]));

          using namespace crisp::util::literals;

          /* Set up the control packet to be sent at 25 Hz. */
          fprintf(stderr, "Setting up send action... ");
          node.scheduler.schedule(10_Hz,
                                  [&](crisp::util::PeriodicAction&) {
                                    std::unique_lock<std::mutex> lock ( mutex );
                                    node.send(mc);
                                    return;
                                  });
          fprintf(stderr, "done.");
        });

      /* clear the module-control-sent handler -- it's just a lot of spam. */
      node.dispatcher.module_control.sent.clear();

      std::atomic<bool> controller_run_flag ( true );

      /* On user interrupt (Ctrl+C), shut down the node and stop the
         controller's main loop.

         If the program doesn't stop, wiggle the controller a bit. (Current
         EvDevController implementation uses a blocking read, so it won't notice
         that the run flag has changed until it gets another event.)
      */
      boost::asio::signal_set ss ( service, SIGINT );
      ss.async_wait([&](const boost::system::error_code& error, int sig)
                    {
                      if ( ! error )
                        {
                          node.halt();
                          controller_run_flag = false;
                        }
                    });

      /* Queue a configuration-query message send. */
      node.send(MessageType::CONFIGURATION_QUERY);

      /* Start the controller and the network node. */
      std::thread controller_thread([&]() { controller.run(controller_run_flag); });
      node.run();
    }

  return 0;
}
#endif  /* ! defined(DISABLE_CLIENT) */

int
main(int argc, char* argv[])
{
  /* Parse user options. */
  int c;
  while ( (c = getopt(argc, argv, "h")) != -1 )
    switch ( c )
      {
      case 'h':
	PRINT_USAGE(stdout);
	fprintf(stdout, HELP_TEXT);
	return 0;

      default:
	abort();
      }

  if ( argc - optind < 2 )
    {
      PRINT_USAGE(stderr);
      return 1;
    }

  /* Parse the address and port passed via the command line, and inform the user
     if they're doin' it wrong. */
  char* tail;
  boost::system::error_code pec;

  namespace ip = boost::asio::ip;

  ip::address address ( ip::address::from_string(argv[optind], pec) );

  int port ( strtoul(argv[optind + 1], &tail, 0) );

  if ( pec )
    { fprintf(stderr, "%s: %s (failed to parse IP address).\n", argv[optind], pec.message().c_str());
      return 1; }

  if ( *tail != '\0' )
    { fprintf(stderr, "%s: Need integer port number.\n", argv[optind + 1]);
      return 1; }

  /* Construct the target endpoint (IP address + port number) object. */
  using Protocol = Node::Protocol;
  typename Protocol::endpoint target_endpoint(address, port);

  /* Asio's io_service manages all the low-level details of parceling out work
     to IO worker threads in a thread-safe manner.  It's required for pretty
     much any network-related function, so we'll create one here.  */
  boost::asio::io_service service;

#if ! defined(DISABLE_CLIENT)
  return ( argc - optind < 3 )
    ? run_server(service, target_endpoint)
    : run_client(service, target_endpoint, argv[optind+2]);
#else
  return run_server(service, target_endpoint);
#endif
}

<commit_msg>drive-test: improve usefulness of output for diagnosing problems<commit_after>/** @file
 *
 * Client/server program for the drive test.
 */
#include <cstdio>
#include "Chassis_Control/DriveData.h"

/* Manually disable client mode on ARM machines (e.g. the Raspberry Pi, which
   gives us enough grief anyway... */
#if defined(__arm__) && ! defined(DISABLE_CLIENT)
# define DISABLE_CLIENT 1
#endif

/* ****************************************************************
 * Help text stuff.
 */
#if ! defined(DISABLE_CLIENT)
#  define PRINT_USAGE(stream) fprintf(stream, "Usage: %s [OPTION]... ADDRESS PORT [EVDEV]\n", argv[0])

#  define HELP_TEXT "\
Simple controls-test program.\n\
\n\
Options:\n\
  -h	Show this help.\n\
\n\
Client mode is assumed when EVDEV argument is present; otherwise server mode is\n\
enabled.\n"
#else
#  define PRINT_USAGE(stream) fprintf(stream, "Usage: %s [OPTION]... ADDRESS PORT\n", argv[0])
#  define HELP_TEXT "\
Simple controls-test program: server-only build.\n\
\n\
Options:\n\
  -h	Show this help.\n\
\n"
#endif  /* ! defined(DISABLE_CLIENT) */
/* **************************************************************** */

#include <boost/asio/connect.hpp>
#include <boost/asio/ip/tcp.hpp>
#include <boost/asio/signal_set.hpp>
#include <crisp/comms/BasicNode.hh>
#include <crisp/comms/NodeServer.hh>

/* Define some aliases for our communication-types for convenience. */
using namespace crisp::comms;
typedef BasicNode<boost::asio::ip::tcp> Node;
typedef typename Node::Protocol Protocol;
typedef typename Protocol::endpoint Endpoint;

#include "gnublin_i2c.hpp"

#define I2C_BUS "/dev/i2c-0"
#define I2C_SLAVE_ADDRESS 5

static int
run_server(boost::asio::io_service& service,
           const Endpoint& target_endpoint)

{
  crisp::comms::NodeServer<Node> server ( service, target_endpoint );

  gnublin_i2c i2c ( I2C_BUS, I2C_SLAVE_ADDRESS );

  /* I don't trust this "Gnublin" code (it's terribly written and I had to fix
     it to even get it to compile).   We'll be replacing it eventually. */
  i2c.setDevicefile(I2C_BUS);
  i2c.setAddress(I2C_SLAVE_ADDRESS);

  if ( i2c.fail() )             /* Won't happen because the Gnublin stuff opens
                                   the device on-demand.  /Le sigh.../ */
    return 1;

  DriveData data;
  unsigned char* data_pointer = reinterpret_cast<unsigned char*>(&data);
  memset(data_pointer, 0, sizeof(data));

  using namespace crisp::comms::keywords;

  /* Declare our interface configuration. */
  server.configuration.add_module( "LED", 2)
    .add_input<int8_t>({ "left",  { _neutral = 0, _minimum = -127, _maximum = 128 } })
    .add_input<int8_t>({ "right", { _neutral = 0, _minimum = -127, _maximum = 128 } });

  /* Override the handler run when we receive a module-control packet. */
  server.dispatcher.module_control.received.clear();
  server.dispatcher.module_control.received.connect(
    [&](Node& node, const crisp::comms::ModuleControl& mc)
    {
      fprintf(stderr, "[0x%0x] \033[1;33mModule-control received\033[0m:", THREAD_ID);

      const crisp::comms::DataValue<> *dvl ( nullptr ), *dvr ( nullptr );

      /* Update our input-value array. */
      if ( (dvl = mc.value_for("left")) != nullptr )
        {
          data.left_motor = dvl->get<int8_t>();
          fprintf(stderr, "%d", data.left_motor);
        }
      dvr = mc.value_for("right");

      if ( dvl && dvr )
        fputs(", ", stderr);

      if ( dvr )
        {
          data.right_motor = dvr->get<int8_t>();
          fprintf(stderr, "%d\n", data.right_motor);
        }

      /* This call here is just so EW EW EW EW EW.  There's a reason for it, of
         course, but that reason is irrelevant with the way we're using I2C. */
      i2c.send(data_pointer, sizeof(data));

      if ( i2c.fail() )
        {
          perror("write");
        }
    });

  server.run();
  return 0;
}

/* Disable client functionality on the Raspberry Pi.

   _With_ this code (and using the old controller code based on the
   terribly-bloated Boost.Signals2 library), I had trouble getting the program
   to compile on the Pi without running out of memory.

   We don't need to support client mode on the Pi, so we might as well keep it
   disabled anyway.
 */
#if ! defined(DISABLE_CLIENT)

#include <crisp/input/EvDevController.hh>
#include <float.h>

/** Update a ModuleControl instance to reflect the input coordinates.
 *
 * @param mc ModuleControl to update.
 *
 * @param left Value of left stick y axis.
 *
 * @param right Value of right stick y axis.
 *
 */
void
update_control(crisp::comms::ModuleControl& mc,
               float left, float right)
{
  mc.set<int8_t>("left", static_cast<int8_t>(left * 127));
  mc.set<int8_t>("right", static_cast<int8_t>(right * 127));
}

/** Run the client code, connecting to the specified endpoint and taking input
 *  from the specified Linux `evdev` input device.
 */
static int
run_client(boost::asio::io_service& service,
           const Endpoint& target_endpoint,
           const char* evdev)
{
  /* Create a socket, and try to connect it to the specified endpoint. */
  boost::system::error_code ec;
  typename Protocol::socket socket ( service );
  typename Protocol::resolver resolver ( service );

  boost::asio::connect(socket, resolver.resolve(target_endpoint), ec);

  if ( ec )
    { fprintf(stderr, "connect: %s\n", strerror(ec.value()));
      return 1; }
  else
    {
      /* We're going to be modifying the ModuleControl instance on-demand
         (whenever the input controller changes state), but sending it at a
         fixed rate.  Since this will be happening in different threads and
         there's no easy way to coordinate the memory access and modification
         otherwise,  we need a mutex to keep everything in sync.

         (If we didn't do this, we could e.g., get halfway through updating the
         module control instance, send a control packet based on that [likely
         invalid] data, and then finish updating.)
      */
      std::mutex mutex;
      ModuleControl mc;
      bool have_config ( false );
      float left, right;

      /* Create the input device. */
      using namespace crisp::input;
      EvDevController controller ( evdev );
      assert(controller.axes.size() >= 4);

      /* Make sure the axes we'll be using are in absolute mode. */
      for ( size_t i ( 0 ); i < 2; ++i )
        {
          controller.axes[i].set_coefficients({1, 0, 0, 0});
          if ( controller.axes[i].type != Axis::Type::ABSOLUTE )
            {                     /* Set up emulation for the axis. */
              controller.axes[i].mode = Axis::Type::ABSOLUTE;

              /* We need to manually initialize the absolute-axis raw-value
                 configuration when an axis isn't actually absolute... */
              controller.axes[i].raw.minimum =
                - (controller.axes[i].raw.maximum = 256);
              controller.axes[i].raw.neutral = 0;

              /* set cubic mapping: value = 1 * x^3 + 0 * x^2 + 0 * x + 0.  */
            }
        }

      /* Add event handlers for each of the axes we're interested in. */
      controller.axes[1].hook([&](const Axis& axis, Axis::State state)
                              {
                                if ( have_config )
                                  {
                                    std::unique_lock<std::mutex> lock ( mutex );
                                    left = state.value;
                                    update_control(mc, left, right);
                                  }
                              });
      controller.axes[3].hook([&](const Axis& axis, Axis::State state)
                              {
                                if ( have_config )
                                  {
                                    std::unique_lock<std::mutex> lock ( mutex );
                                    right = state.value;
                                    update_control(mc, left, right);
                                  }
                              });

      /* Instantiate the network node. */
      Node node ( std::move(socket), NodeRole::MASTER );

      /* Override the default configuration-response-recieved handler with
         this:

         - First, make sure that the received configuration is at least similar to what
           we expect; shut down the node if it isn't.

         - Store the received configuration.

         - Set up the module-control object to
      */
      node.dispatcher.configuration_response.received.connect(
        [&](Node& _node, const Configuration& configuration)
        {
          have_config = true;
          node.configuration = configuration;
          mc.reset(&(node.configuration.modules[0]));

          using namespace crisp::util::literals;

          /* Set up the control packet to be sent at 25 Hz. */
          fprintf(stderr, "Setting up send action... ");
          node.scheduler.schedule(10_Hz,
                                  [&](crisp::util::PeriodicAction&) {
                                    std::unique_lock<std::mutex> lock ( mutex );
                                    node.send(mc);
                                    return;
                                  });
          fprintf(stderr, "done.");
        });

      /* clear the module-control-sent handler -- it's just a lot of spam. */
      node.dispatcher.module_control.sent.clear();

      std::atomic<bool> controller_run_flag ( true );

      /* On user interrupt (Ctrl+C), shut down the node and stop the
         controller's main loop.

         If the program doesn't stop, wiggle the controller a bit. (Current
         EvDevController implementation uses a blocking read, so it won't notice
         that the run flag has changed until it gets another event.)
      */
      boost::asio::signal_set ss ( service, SIGINT );
      ss.async_wait([&](const boost::system::error_code& error, int sig)
                    {
                      if ( ! error )
                        {
                          node.halt();
                          controller_run_flag = false;
                        }
                    });

      /* Queue a configuration-query message send. */
      node.send(MessageType::CONFIGURATION_QUERY);

      /* Start the controller and the network node. */
      std::thread controller_thread([&]() { controller.run(controller_run_flag); });
      node.run();
    }

  return 0;
}
#endif  /* ! defined(DISABLE_CLIENT) */

int
main(int argc, char* argv[])
{
  /* Parse user options. */
  int c;
  while ( (c = getopt(argc, argv, "h")) != -1 )
    switch ( c )
      {
      case 'h':
	PRINT_USAGE(stdout);
	fprintf(stdout, HELP_TEXT);
	return 0;

      default:
	abort();
      }

  if ( argc - optind < 2 )
    {
      PRINT_USAGE(stderr);
      return 1;
    }

  /* Parse the address and port passed via the command line, and inform the user
     if they're doin' it wrong. */
  char* tail;
  boost::system::error_code pec;

  namespace ip = boost::asio::ip;

  ip::address address ( ip::address::from_string(argv[optind], pec) );

  int port ( strtoul(argv[optind + 1], &tail, 0) );

  if ( pec )
    { fprintf(stderr, "%s: %s (failed to parse IP address).\n", argv[optind], pec.message().c_str());
      return 1; }

  if ( *tail != '\0' )
    { fprintf(stderr, "%s: Need integer port number.\n", argv[optind + 1]);
      return 1; }

  /* Construct the target endpoint (IP address + port number) object. */
  using Protocol = Node::Protocol;
  typename Protocol::endpoint target_endpoint(address, port);

  /* Asio's io_service manages all the low-level details of parceling out work
     to IO worker threads in a thread-safe manner.  It's required for pretty
     much any network-related function, so we'll create one here.  */
  boost::asio::io_service service;

#if ! defined(DISABLE_CLIENT)
  return ( argc - optind < 3 )
    ? run_server(service, target_endpoint)
    : run_client(service, target_endpoint, argv[optind+2]);
#else
  return run_server(service, target_endpoint);
#endif
}

<|endoftext|>