/******************************************************************************
 * Copyright (c) 2000-2021 Ericsson Telecom AB
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v2.0
 * which accompanies this distribution, and is available at
 * https://www.eclipse.org/org/documents/epl-2.0/EPL-2.0.html
 *
 * Contributors:
 *   >
 *   Baji, Laszlo
 *   Balasko, Jeno
 *   Baranyi, Botond
 *   Beres, Szabolcs
 *   Delic, Adam
 *   Forstner, Matyas
 *   Kovacs, Ferenc
 *   Raduly, Csaba
 *   Szabados, Kristof
 *   Szabo, Bence Janos
 *   Szabo, Janos Zoltan – initial implementation
 *   Szalai, Gabor
 *   Tatarka, Gabor
 *
 ******************************************************************************/
#include <string.h>
#include <math.h>
#include <float.h>
#include <locale.h>

#include "../common/memory.h"
#include "Float.hh"
#include "Types.h"
#include "Param_Types.hh"
#include "Optional.hh"
#include "Logger.hh"
#include "Error.hh"
#include "Encdec.hh"
#include "RAW.hh"
#include "BER.hh"
#include "OER.hh"
#include "Charstring.hh"
#include "Addfunc.hh"
#include "XmlReader.hh"

#include "../common/dbgnew.hh"


#ifndef INFINITY
#define INFINITY (DBL_MAX*DBL_MAX)
#endif
const FLOAT PLUS_INFINITY(INFINITY);
const FLOAT MINUS_INFINITY(-INFINITY);

#ifdef NAN
const FLOAT NOT_A_NUMBER(NAN);
#else
const FLOAT NOT_A_NUMBER((double)PLUS_INFINITY+(double)MINUS_INFINITY);
// The casts ensure that FLOAT::operator+ is not called
#endif


static inline void log_float(double float_val)
{
  if(float_val==INFINITY)
    TTCN_Logger::log_event_str("infinity");
  else if(float_val==-INFINITY)
    TTCN_Logger::log_event_str("-infinity");
  else if(float_val!=float_val)
    TTCN_Logger::log_event_str("not_a_number");
  else {
    const char* loc = setlocale(LC_ALL, NULL);
    setlocale(LC_NUMERIC, "C"); // use default locale for displaying numbers
    if(TTCN_Logger::get_log_format() == TTCN_Logger::LF_TTCN && float_val != 0.0){
      // ttcn2str, use the TTCN-3 standard format and print enough 
      // digits so when scanning the string back, we get the original floating point.
      // for 64bit double 17 digit is needed
      // The 0.0 is habdled by the normal logging case. The %f prints the 0.0 or -0.0 correctly
      double rabs = fabs(float_val);
      double exponent = floor(log10(rabs));
      double mantissa = rabs * pow(10.0, -exponent);

      TTCN_Logger::log_event("%s%.15g", float_val < 0.0 ? "-" : "", mantissa);
      if (floor(mantissa) == mantissa) TTCN_Logger::log_event( ".0");
      if (exponent != 0.0) TTCN_Logger::log_event("e%d", (int)exponent);
      
    } else {
      // Normal logging
      boolean f = (float_val > -MAX_DECIMAL_FLOAT && float_val <= -MIN_DECIMAL_FLOAT)
        || (float_val >= MIN_DECIMAL_FLOAT && float_val <   MAX_DECIMAL_FLOAT)
        || (float_val == 0.0);
      TTCN_Logger::log_event(f ? "%f" : "%e", float_val);
    }
    setlocale(LC_NUMERIC, loc);
  }
}

// float value class

FLOAT::FLOAT()
{
  bound_flag = FALSE;
}

FLOAT::FLOAT(double other_value)
{
  bound_flag = TRUE;
  float_value = other_value;
}

FLOAT::FLOAT(const FLOAT& other_value)
: Base_Type(other_value)
{
  other_value.must_bound("Copying an unbound float value.");
  bound_flag = TRUE;
  float_value = other_value.float_value;
}

void FLOAT::clean_up()
{
  bound_flag = FALSE;
}

FLOAT& FLOAT::operator=(double other_value)
{
  bound_flag = TRUE;
  float_value = other_value;
  return *this;
}

FLOAT& FLOAT::operator=(const FLOAT& other_value)
{
  other_value.must_bound("Assignment of an unbound float value.");
  bound_flag = TRUE;
  float_value = other_value.float_value;
  return *this;
}

double FLOAT::operator+() const
{
  must_bound("Unbound float operand of unary + operator.");
  return float_value;
}

double FLOAT::operator-() const
{
  must_bound("Unbound float operand of unary - operator (negation).");
  return -float_value;
}

boolean FLOAT::is_special(double flt_val)
{
  return ( (flt_val!=flt_val) || (flt_val==INFINITY) || (flt_val==-INFINITY) );
}

void FLOAT::check_numeric(double flt_val, const char *err_msg_begin)
{
  if (is_special(flt_val)) {
    TTCN_error("%s must be a numeric value instead of %g",
      err_msg_begin, flt_val);
  }
}

double FLOAT::operator+(double other_value) const
{
  must_bound("Unbound left operand of float addition.");
  return float_value + other_value;
}

double FLOAT::operator+(const FLOAT& other_value) const
{
  must_bound("Unbound left operand of float addition.");
  other_value.must_bound("Unbound right operand of float addition.");
  return float_value + other_value.float_value;
}

double FLOAT::operator-(double other_value) const
{
  must_bound("Unbound left operand of float subtraction.");
  return float_value - other_value;
}

double FLOAT::operator-(const FLOAT& other_value) const
{
  must_bound("Unbound left operand of float subtraction.");
  other_value.must_bound("Unbound right operand of float subtraction.");
  return float_value - other_value.float_value;
}

double FLOAT::operator*(double other_value) const
{
  must_bound("Unbound left operand of float multiplication.");
  return float_value * other_value;
}

double FLOAT::operator*(const FLOAT& other_value) const
{
  must_bound("Unbound left operand of float multiplication.");
  other_value.must_bound("Unbound right operand of float multiplication.");
  return float_value * other_value.float_value;
}

double FLOAT::operator/(double other_value) const
{
  must_bound("Unbound left operand of float division.");
  if (other_value == 0.0) TTCN_error("Float division by zero.");
  return float_value / other_value;
}

double FLOAT::operator/(const FLOAT& other_value) const
{
  must_bound("Unbound left operand of float division.");
  other_value.must_bound("Unbound right operand of float division.");
  if (other_value.float_value == 0.0) TTCN_error("Float division by zero.");
  return float_value / other_value.float_value;
}

boolean FLOAT::operator==(double other_value) const
{
  must_bound("Unbound left operand of float comparison.");
  return float_value == other_value;
}

boolean FLOAT::operator==(const FLOAT& other_value) const
{
  must_bound("Unbound left operand of float comparison.");
  other_value.must_bound("Unbound right operand of float comparison.");
  return float_value == other_value.float_value;
}

boolean FLOAT::operator<(double other_value) const
{
  must_bound("Unbound left operand of float comparison.");
  return float_value < other_value;
}

boolean FLOAT::operator<(const FLOAT& other_value) const
{
  must_bound("Unbound left operand of float comparison.");
  other_value.must_bound("Unbound right operand of float comparison.");
  return float_value < other_value.float_value;
}

boolean FLOAT::operator>(double other_value) const
{
  must_bound("Unbound left operand of float comparison.");
  return float_value > other_value;
}

boolean FLOAT::operator>(const FLOAT& other_value) const
{
  must_bound("Unbound left operand of float comparison.");
  other_value.must_bound("Unbound right operand of float comparison.");
  return float_value > other_value.float_value;
}

FLOAT::operator double() const
{
  must_bound("Using the value of an unbound float variable.");
  return float_value;
}

void FLOAT::log() const
{
  if (bound_flag) log_float(float_value);
  else TTCN_Logger::log_event_unbound();
}

void FLOAT::set_param(Module_Param& param) {
  param.basic_check(Module_Param::BC_VALUE, "float value");
  Module_Param_Ptr mp = &param;
#ifdef TITAN_RUNTIME_2
  if (param.get_type() == Module_Param::MP_Reference) {
    mp = param.get_referenced_param();
  }
#endif
  switch (mp->get_type()) {
  case Module_Param::MP_Float: {
    clean_up();
    bound_flag = TRUE;
    float_value = mp->get_float();
    break; }
  case Module_Param::MP_Expression:
    switch (mp->get_expr_type()) {
    case Module_Param::EXPR_NEGATE: {
      FLOAT operand;
      operand.set_param(*mp->get_operand1());
      *this = - operand;
      break; }
    case Module_Param::EXPR_ADD: {
      FLOAT operand1, operand2;
      operand1.set_param(*mp->get_operand1());
      operand2.set_param(*mp->get_operand2());
      *this = operand1 + operand2;
      break; }
    case Module_Param::EXPR_SUBTRACT: {
      FLOAT operand1, operand2;
      operand1.set_param(*mp->get_operand1());
      operand2.set_param(*mp->get_operand2());
      *this = operand1 - operand2;
      break; }
    case Module_Param::EXPR_MULTIPLY: {
      FLOAT operand1, operand2;
      operand1.set_param(*mp->get_operand1());
      operand2.set_param(*mp->get_operand2());
      *this = operand1 * operand2;
      break; }
    case Module_Param::EXPR_DIVIDE: {
      FLOAT operand1, operand2;
      operand1.set_param(*mp->get_operand1());
      operand2.set_param(*mp->get_operand2());
      if (operand2 == 0.0) {
        param.error("Floating point division by zero.");
      }
      *this = operand1 / operand2;
      break; }
    default:
      param.expr_type_error("a float");
      break;
    }
    break;
  default:
    param.type_error("float value");
    break;
  }
}

#ifdef TITAN_RUNTIME_2
Module_Param* FLOAT::get_param(Module_Param_Name& /* param_name */) const
{
  if (!bound_flag) {
    return new Module_Param_Unbound();
  }
  return new Module_Param_Float(float_value);
}
#endif

void FLOAT::encode_text(Text_Buf& text_buf) const
{
  must_bound("Text encoder: Encoding an unbound float value.");
  text_buf.push_double(float_value);
}

void FLOAT::decode_text(Text_Buf& text_buf)
{
  bound_flag = TRUE;
  float_value = text_buf.pull_double();
}

void FLOAT::encode(const TTCN_Typedescriptor_t& p_td, TTCN_Buffer& p_buf,
                   int p_coding, ...) const
{
  va_list pvar;
  va_start(pvar, p_coding);
  switch(p_coding) {
  case TTCN_EncDec::CT_BER: {
    TTCN_EncDec_ErrorContext ec("While BER-encoding type '%s': ", p_td.name);
    unsigned BER_coding=va_arg(pvar, unsigned);
    BER_encode_chk_coding(BER_coding);
    ASN_BER_TLV_t *tlv=BER_encode_TLV(p_td, BER_coding);
    tlv->put_in_buffer(p_buf);
    ASN_BER_TLV_t::destruct(tlv);
    break;}
  case TTCN_EncDec::CT_RAW: {
    TTCN_EncDec_ErrorContext ec("While RAW-encoding type '%s': ", p_td.name);
    if(!p_td.raw)
    TTCN_EncDec_ErrorContext::error_internal
      ("No RAW descriptor available for type '%s'.", p_td.name);
    RAW_enc_tr_pos rp;
    rp.level=0;
    rp.pos=NULL;
    RAW_enc_tree root(TRUE,NULL,&rp,1,p_td.raw);
    RAW_encode(p_td, root);
    root.put_to_buf(p_buf);
    break;}
  case TTCN_EncDec::CT_XER: {
    TTCN_EncDec_ErrorContext ec("While XER-encoding type '%s': ", p_td.name);
    unsigned XER_coding=va_arg(pvar, unsigned);
    XER_encode(*p_td.xer, p_buf, XER_coding, 0, 0, 0);
    break;}
  case TTCN_EncDec::CT_JSON: {
    TTCN_EncDec_ErrorContext ec("While JSON-encoding type '%s': ", p_td.name);
    if(!p_td.json)
      TTCN_EncDec_ErrorContext::error_internal
        ("No JSON descriptor available for type '%s'.", p_td.name);
    JSON_Tokenizer tok(va_arg(pvar, int) != 0);
    JSON_encode(p_td, tok, FALSE);
    p_buf.put_s(tok.get_buffer_length(), (const unsigned char*)tok.get_buffer());
    break;}
  case TTCN_EncDec::CT_OER: {
    TTCN_EncDec_ErrorContext ec("While OER-encoding type '%s': ", p_td.name);
    if(!p_td.oer)  TTCN_EncDec_ErrorContext::error_internal(
      "No OER descriptor available for type '%s'.", p_td.name);
    OER_encode(p_td, p_buf);
    break;}
  default:
    TTCN_error("Unknown coding method requested to encode type '%s'",
               p_td.name);
  }
  va_end(pvar);
}

void FLOAT::decode(const TTCN_Typedescriptor_t& p_td, TTCN_Buffer& p_buf,
                   int p_coding, ...)
{
  va_list pvar;
  va_start(pvar, p_coding);
  switch(p_coding) {
  case TTCN_EncDec::CT_BER: {
    TTCN_EncDec_ErrorContext ec("While BER-decoding type '%s': ", p_td.name);
    unsigned L_form=va_arg(pvar, unsigned);
    ASN_BER_TLV_t tlv;
    BER_decode_str2TLV(p_buf, tlv, L_form);
    BER_decode_TLV(p_td, tlv, L_form);
    if(tlv.isComplete) p_buf.increase_pos(tlv.get_len());
    break;}
  case TTCN_EncDec::CT_RAW: {
    TTCN_EncDec_ErrorContext ec("While RAW-decoding type '%s': ", p_td.name);
    if(!p_td.raw)
      TTCN_EncDec_ErrorContext::error_internal
        ("No RAW descriptor available for type '%s'.", p_td.name);
    raw_order_t order;
    switch(p_td.raw->top_bit_order){
    case TOP_BIT_LEFT:
      order=ORDER_LSB;
      break;
    case TOP_BIT_RIGHT:
    default:
      order=ORDER_MSB;
    }
    if(RAW_decode(p_td, p_buf, p_buf.get_len()*8, order)<0)
      ec.error(TTCN_EncDec::ET_INCOMPL_MSG,
        "Can not decode type '%s', because invalid or incomplete"
        " message was received"
        , p_td.name);
    break;}
  case TTCN_EncDec::CT_XER: {
    TTCN_EncDec_ErrorContext ec("While XER-decoding type '%s': ", p_td.name);
    unsigned XER_coding=va_arg(pvar, unsigned);
    XmlReaderWrap reader(p_buf);
    for (int success = reader.Read(); success==1; success=reader.Read()) {
      int type = reader.NodeType();
      if (type==XML_READER_TYPE_ELEMENT)
	break;
    }
    XER_decode(*p_td.xer, reader, XER_coding, XER_NONE, 0);
    size_t bytes = reader.ByteConsumed();
    p_buf.set_pos(bytes);
    break;}
  case TTCN_EncDec::CT_JSON: {
    TTCN_EncDec_ErrorContext ec("While JSON-decoding type '%s': ", p_td.name);
    if(!p_td.json)
      TTCN_EncDec_ErrorContext::error_internal
        ("No JSON descriptor available for type '%s'.", p_td.name);
    JSON_Tokenizer tok((const char*)p_buf.get_data(), p_buf.get_len());
    if(JSON_decode(p_td, tok, FALSE, FALSE)<0)
      ec.error(TTCN_EncDec::ET_INCOMPL_MSG,
               "Can not decode type '%s', because invalid or incomplete"
               " message was received"
               , p_td.name);
    p_buf.set_pos(tok.get_buf_pos());
    break;}
  case TTCN_EncDec::CT_OER: {
      TTCN_EncDec_ErrorContext ec("While OER-decoding type '%s': ", p_td.name);
    if(!p_td.oer)  TTCN_EncDec_ErrorContext::error_internal(
      "No OER descriptor available for type '%s'.", p_td.name);
    OER_struct p_oer;
    OER_decode(p_td, p_buf, p_oer);
    break;}
  default:
    TTCN_error("Unknown coding method requested to decode type '%s'",
               p_td.name);
  }
  va_end(pvar);
}

ASN_BER_TLV_t*
FLOAT::BER_encode_TLV(const TTCN_Typedescriptor_t& p_td,
                      unsigned p_coding) const
{
  BER_chk_descr(p_td);
  ASN_BER_TLV_t *new_tlv=BER_encode_chk_bound(is_bound());
  if(!new_tlv) {
    if(float_value==0.0) {
      new_tlv=ASN_BER_TLV_t::construct();
      // nothing to do, Vlen is 0
    }
    /* +Infinity */
    else if(float_value==(double)INFINITY) { // INFINITY may be float => cast
      new_tlv=ASN_BER_TLV_t::construct(1, NULL);
      new_tlv->V.str.Vstr[0]=0x40;
    }
    /* -Infinity */
    else if(float_value==-(double)INFINITY) {
      new_tlv=ASN_BER_TLV_t::construct(1, NULL);
      new_tlv->V.str.Vstr[0]=0x41;
    }
    else if(isnan((double)float_value)) {
      TTCN_EncDec_ErrorContext::error_internal("Value is NaN.");
    }
    else {
      new_tlv=ASN_BER_TLV_t::construct();
      double mantissa, exponent;
      exponent=floor(log10(fabs(float_value)))+1.0-DBL_DIG;
      mantissa=floor(float_value*pow(10.0,-exponent)+0.5);
      if(mantissa != 0.0) {
        while(fmod(mantissa,10.0) == 0.0) {
          mantissa/=10.0,exponent+=1.0;
        }
      }
      /** \todo review
          gcc 2.95:
          in mprintf below:
          warning: `.' not followed by `*' or digit in format
       */
      new_tlv->V.str.Vstr=(unsigned char*)
        mprintf("\x03%.f.E%s%.0f", mantissa, exponent==0.0?"+":"", exponent);
      new_tlv->V.str.Vlen=1+strlen((const char*)&new_tlv->V.str.Vstr[1]);
    }
  }
  new_tlv=ASN_BER_V2TLV(new_tlv, p_td, p_coding);
  return new_tlv;
}

boolean FLOAT::BER_decode_TLV(const TTCN_Typedescriptor_t& p_td,
                              const ASN_BER_TLV_t& p_tlv,
                              unsigned L_form)
{
  bound_flag = FALSE;
  BER_chk_descr(p_td);
  ASN_BER_TLV_t stripped_tlv;
  BER_decode_strip_tags(*p_td.ber, p_tlv, L_form, stripped_tlv);
  TTCN_EncDec_ErrorContext ec("While decoding REAL type: ");
  stripped_tlv.chk_constructed_flag(FALSE);
  if (!stripped_tlv.isComplete) return FALSE;
  size_t Vlen=stripped_tlv.V.str.Vlen;
  unsigned char *Vstr=stripped_tlv.V.str.Vstr;
  if(Vlen==0) {
    float_value=0.0;
  }
  else if(Vstr[0] & 0x80) {
    /* binary encoding */
    /** \todo Perhaps it were good to implement this. Perhaps not. :) */
    ec.warning("Sorry, decoding of binary encoded REAL values not"
               " supported.");
    float_value=0.0;
  }
  else if(Vstr[0] & 0x40) {
    /* SpecialRealValue */
    if(Vlen>1)
      ec.error(TTCN_EncDec::ET_INVAL_MSG,
               "In case of SpecialRealValue, the length of V-part must be 1"
               " (See X.690 8.5.8).");
    if(Vstr[0] & 0x3E)
      ec.error(TTCN_EncDec::ET_INVAL_MSG,
               "This is a reserved value: 0x%x (See X.690 8.5.8).",
               Vstr[0]);
    if(Vstr[0] & 0x01)
      /* MINUS-INFINITY */
      float_value=-INFINITY;
    else
      /* PLUS-INFINITY */
      float_value=INFINITY;
  }
  else {
    /* decimal encoding */
    if((Vstr[0] & 0x3C) || (Vstr[0] & 0x3F) == 0x00 )
      ec.error(TTCN_EncDec::ET_INVAL_MSG,
               "This is a reserved value: 0x%x (See X.690 8.5.7).",
               Vstr[0]);
    int NR=Vstr[0] & 0x03; // which NumericalRepresentation
    boolean
      leadingzero=FALSE,
      NR_error=FALSE;
    unsigned char
      *Vstr_last=Vstr+Vlen-1,
      *sign=NULL,
      *mant1=NULL,
      *decmark=NULL,
      *mant2=NULL,
      *expmark=NULL,
      *expsign=NULL,
      *expo=NULL,
      *ptr=Vstr+1;
    size_t
      mant1_len=0,
      mant2_len=0,
      expo_len=0;
    long exponum;
    if(Vlen==1) goto dec_error;
    while(*ptr==' ') {
      if(ptr==Vstr_last) goto dec_error;
      ptr++;
    }
    if(*ptr=='+' || *ptr=='-') {
      sign=ptr;
      if(ptr==Vstr_last) goto dec_error;
      ptr++;
    }
    while(*ptr=='0') {
      leadingzero=TRUE;
      if(ptr==Vstr_last) goto str_end;
      ptr++;
    }
    while(*ptr>='0' && *ptr<='9') {
      if(mant1_len==0) mant1=ptr;
      mant1_len++;
      if(ptr==Vstr_last) goto str_end;
      ptr++;
    }
    if(*ptr=='.' || *ptr==',') {
      decmark=ptr;
      if(ptr==Vstr_last) goto str_end;
      ptr++;
    }
    while(*ptr>='0' && *ptr<='9') {
      if(mant2_len==0) mant2=ptr;
      mant2_len++;
      if(ptr==Vstr_last) goto str_end;
      ptr++;
    }
    if(!leadingzero && !mant1 && !mant2) goto dec_error;
    if(*ptr=='e' || *ptr=='E') {
      expmark=ptr;
      if(ptr==Vstr_last) goto dec_error;
      ptr++;
    }
    if(*ptr=='+' || *ptr=='-') {
      expsign=ptr;
      if(ptr==Vstr_last) goto dec_error;
      ptr++;
    }
    while(*ptr=='0') {
      expo=ptr;
      if(ptr==Vstr_last) goto str_end;
      ptr++;
    }
    while(*ptr>='0' && *ptr<='9') {
      if(expo_len==0) expo=ptr;
      expo_len++;
      if(ptr==Vstr_last) goto str_end;
      ptr++;
    }
    if(expo_len==0 && expo!=NULL) expo_len=1; /* only leading zero */
    if(expsign && !expo) goto dec_error;
    ec.error(TTCN_EncDec::ET_INVAL_MSG,
             "Superfluous part at the end of decimal encoding.");
  str_end:
    /* check NR */
    if(NR==1) {
      if(decmark || expmark) NR_error=TRUE;
    }
    else if(NR==2) {
      if(expmark) NR_error=TRUE;
    }
    if(NR_error)
      ec.error(TTCN_EncDec::ET_INVAL_MSG,
               "This decimal encoding does not conform to NR%d form.", NR);
    while(mant2_len>1 && mant2[mant2_len-1]=='0') mant2_len--;
    if(mant2_len==1 && *mant2=='0') mant2_len=0, mant2=NULL;
    float_value=0.0;
    if(mant1) for(size_t i=0; i<mant1_len; i++) {
      float_value*=10.0;
      float_value+=static_cast<double>(mant1[i]-'0');
    } // for i if...
    if(mant2) for(size_t i=0; i<mant2_len; i++) {
      float_value*=10.0;
      float_value+=static_cast<double>(mant2[i]-'0');
    } // for i if...
    exponum=0;
    if(expo) {
      if(ceil(log10(log10(DBL_MAX)))<expo_len) {
        /* overflow */
        if(expsign && *expsign=='-') {
          float_value=0.0;
        }
        else {
          if(sign && *sign=='-') float_value=-INFINITY;
          else float_value=INFINITY;
        }
        goto end;
      } // overflow
      else {
        /* no overflow */
        for(size_t i=0; i<expo_len; i++) {
          exponum*=10;
          exponum+=static_cast<int>(expo[i]-'0');
        } // for i
        if(expsign && *expsign=='-')
          exponum*=-1;
      } // no overflow
    } // if expo
    if(mant2) exponum-=mant2_len;
    float_value*=pow(10.0, static_cast<double>(exponum));
    goto end;
  dec_error:
    ec.error(TTCN_EncDec::ET_INVAL_MSG, "Erroneous decimal encoding.");
    float_value=0.0;
  }
 end:
  bound_flag=TRUE;
  return TRUE;
}

int FLOAT::RAW_encode(const TTCN_Typedescriptor_t& p_td, RAW_enc_tree& myleaf) const
{
  unsigned char *bc;
  unsigned char *dv;
  int length = p_td.raw->fieldlength / 8;
  double tmp = float_value;
  if (!is_bound()) {
    TTCN_EncDec_ErrorContext::error(TTCN_EncDec::ET_UNBOUND,
      "Encoding an unbound value.");
    tmp = 0.0;
  }
  if (isnan(tmp)) {
    TTCN_EncDec_ErrorContext::error_internal("Value is NaN.");
  }
  if (myleaf.must_free) Free(myleaf.body.leaf.data_ptr);
  if (length > RAW_INT_ENC_LENGTH) {
    myleaf.body.leaf.data_ptr = bc = (unsigned char*)Malloc(length*sizeof(*bc));
    myleaf.must_free = TRUE;
    myleaf.data_ptr_used = TRUE;
  }
  else {
    bc = myleaf.body.leaf.data_array;
  }
  if (length == 8) {
    dv = (unsigned char *) &tmp;
#if defined __sparc__ || defined __sparc
    memcpy(bc,dv,8);
#else
    for (int i = 0, k = 7; i < 8; i++, k--) bc[i] = dv[k];
#endif
  }
  else if (length == 4) {
    if (tmp == 0.0) memset(bc, 0, 4);
    else if (tmp == -0.0) {
      memset(bc, 0, 4);
      bc[0] |= 0x80;
    }
    else {
#if defined __sparc__ || defined __sparc
      int index=0;
      int adj=1;
#else
      int index = 7;
      int adj = -1;
#endif
      dv = (unsigned char *) &tmp;
      bc[0] = dv[index] & 0x80;
      int exponent = dv[index] & 0x7F;
      exponent <<= 4;
      index += adj;
      exponent += (dv[index] & 0xF0) >> 4;
      exponent -= 1023;

      if (exponent > 127) {
        TTCN_EncDec_ErrorContext::error(TTCN_EncDec::ET_LEN_ERR,
          "The float value '%f' is out of the range of "
          "the single precision: %s", (double)float_value, p_td.name);
        tmp = 0.0;
        exponent = 0;
      }
      else if (exponent < -127) {
        TTCN_EncDec_ErrorContext::error(TTCN_EncDec::ET_FLOAT_TR,
          "The float value '%f' is too small to represent it "
          "in single precision: %s", (double)float_value, p_td.name);
        tmp = 0.0;
        exponent = 0;
      }
      else exponent += 127;
      bc[0] |= (exponent >> 1) & 0x7F;
      bc[1] = ((exponent << 7) & 0x80) | ((dv[index] & 0x0F) << 3)
        | ((dv[index + adj] & 0xE0) >> 5);
      index += adj;
      bc[2] = ((dv[index] & 0x1F) << 3) | ((dv[index + adj] & 0xE0) >> 5);
      index += adj;
      bc[3] = ((dv[index] & 0x1F) << 3) | ((dv[index + adj] & 0xE0) >> 5);
    }
  }
  else
    TTCN_EncDec_ErrorContext::error_internal("Invalid FLOAT length %d", length);

  myleaf.coding_par.csn1lh = p_td.raw->csn1lh;
  return myleaf.length = p_td.raw->fieldlength;
}

int FLOAT::RAW_decode(const TTCN_Typedescriptor_t& p_td, TTCN_Buffer& buff,
  int limit, raw_order_t top_bit_ord, boolean no_err, int /*sel_field*/,
  boolean /*first_call*/, const RAW_Force_Omit* /*force_omit*/)
{
  int prepaddlength = buff.increase_pos_padd(p_td.raw->prepadding);
  limit -= prepaddlength;
  int decode_length = p_td.raw->fieldlength;
  if ( p_td.raw->fieldlength > limit
    || p_td.raw->fieldlength > (int) buff.unread_len_bit()) {
    if (no_err) return -1;
    TTCN_EncDec_ErrorContext::error(TTCN_EncDec::ET_LEN_ERR,
      "There is not enough bits in the buffer to decode type %s.", p_td.name);
    decode_length = limit > (int) buff.unread_len_bit()
      ? buff.unread_len_bit() : limit;
    bound_flag = TRUE;
    float_value = 0.0;
    decode_length += buff.increase_pos_padd(p_td.raw->padding);
    return decode_length + prepaddlength;
  }
  double tmp = 0.0;
  unsigned char data[16];
  RAW_coding_par cp;
  boolean orders = p_td.raw->bitorderinoctet == ORDER_MSB;
  if (p_td.raw->bitorderinfield == ORDER_MSB) orders = !orders;
  cp.bitorder = orders ? ORDER_MSB : ORDER_LSB;
  orders = p_td.raw->byteorder == ORDER_MSB;
  if (p_td.raw->bitorderinfield == ORDER_MSB) orders = !orders;
  cp.byteorder = orders ? ORDER_MSB : ORDER_LSB;
  cp.fieldorder = p_td.raw->fieldorder;
  cp.hexorder = ORDER_LSB;
  cp.csn1lh = p_td.raw->csn1lh;
  buff.get_b((size_t) decode_length, data, cp, top_bit_ord);
  if (decode_length == 64) {
    unsigned char *dv = (unsigned char *) &tmp;
#if defined __sparc__ || defined __sparc
    memcpy(dv,data,8);
#else
    for (int i = 0, k = 7; i < 8; i++, k--) dv[i] = data[k];
#endif
    if (isnan(tmp)) {
      if (no_err) return -1;
      TTCN_EncDec_ErrorContext::error(TTCN_EncDec::ET_LEN_ERR,
        "Not a Number received for type %s.", p_td.name);
      tmp = 0.0;
    }
  }
  else if (decode_length == 32) {
    int sign = (data[0] & 0x80) >> 7;
    int exponent = ((data[0] & 0x7F) << 1) | ((data[1] & 0x80) >> 7);
    int fraction = ((data[1] & 0x7F) << 1) | ((data[2] & 0x80) >> 7);
    fraction <<= 8;
    fraction += ((data[2] & 0x7F) << 1) | ((data[3] & 0x80) >> 7);
    fraction <<= 7;
    fraction += data[3] & 0x7F;
    if (exponent == 0 && fraction == 0) tmp = sign ? -0.0 : 0.0;
    else if (exponent == 0xFF && fraction != 0) {
      if (no_err) return -1;
      TTCN_EncDec_ErrorContext::error(TTCN_EncDec::ET_LEN_ERR,
        "Not a Number received for type %s.", p_td.name);
      tmp = 0.0;
    }
    else if (exponent == 0 && fraction != 0) {
      double sign_v = sign ? -1.0 : 1.0;
      tmp = sign_v * (static_cast<double> (fraction) / 8388608.0)
        * pow(2.0, -126.0);
    }
    else {
      double sign_v = sign ? -1.0 : 1.0;
      exponent -= 127;
      tmp = sign_v * (1.0 + static_cast<double> (fraction) / 8388608.0)
        * pow(2.0, static_cast<double> (exponent));
    }

  }
  decode_length += buff.increase_pos_padd(p_td.raw->padding);
  bound_flag = TRUE;
  float_value = tmp;
  return decode_length + prepaddlength;
}

const char* XER_POS_INF_STR = "INF";
const char* XER_NEG_INF_STR = "-INF";
const char* XER_NAN_STR = "NaN";

int FLOAT::XER_encode(const XERdescriptor_t& p_td,
  TTCN_Buffer& p_buf, unsigned int flavor, unsigned int /*flavor2*/, int indent, embed_values_enc_struct_t*) const
{
  if(!is_bound()) {
    TTCN_EncDec_ErrorContext::error(
      TTCN_EncDec::ET_UNBOUND, "Encoding an unbound float value.");
  }
  int exer  = is_exer(flavor |= SIMPLE_TYPE);
  // SIMPLE_TYPE has no influence on is_exer, we set it for later
  int encoded_length=(int)p_buf.get_len();
  flavor &= ~XER_RECOF; // float doesn't care

  begin_xml(p_td, p_buf, flavor, indent, FALSE);

  if (exer && (p_td.xer_bits & XER_DECIMAL)) {
    char buf[312];
    int n = 0;
    if (isnan((double)float_value)) {
      n = snprintf(buf, sizeof(buf), "%s", XER_NAN_STR);
    } else if ((double)float_value == (double)INFINITY) {
      n = snprintf(buf, sizeof(buf), "%s", XER_POS_INF_STR);
    } else if ((double)float_value == -(double)INFINITY) {
      n = snprintf(buf, sizeof(buf), "%s", XER_NEG_INF_STR);
    } else {
      n = snprintf(buf, sizeof(buf), "%f", (double)float_value);
      if (p_td.fractionDigits != -1) {
        char *p = strchr(buf, '.');
        if (p != NULL) {
          int offset = p_td.fractionDigits == 0 ? 0 : p_td.fractionDigits + 1;
          p[offset] = 0;
          n = strlen(buf);
        }
      }
    }
    p_buf.put_s((size_t)n, (const unsigned char*)buf);
  }
  else {
    CHARSTRING value;
    if (isnan((double)float_value)) {
      value = XER_NAN_STR;
    } else if ((double)float_value == (double)INFINITY) {
      value = XER_POS_INF_STR;
    } else if ((double)float_value == -(double)INFINITY) {
      value = XER_NEG_INF_STR;
    } else {
      value = float2str(float_value);
    }
    p_buf.put_string(value);
  }

  end_xml(p_td, p_buf, flavor, indent, FALSE);

  return (int)p_buf.get_len() - encoded_length;
}

boolean FLOAT::is_float(const char* p_str)
{
  boolean first_digit = FALSE; // first digit reached
  boolean decimal_point = FALSE; // decimal point (.) reached
  boolean exponent_mark = FALSE; // exponential mark (e or E) reached
  boolean exponent_sign = FALSE; // sign of the exponential (- or +) reached
  
  if ('-' == *p_str || '+' == *p_str) {
    ++p_str;
  }
  
  while (0 != *p_str) {
    switch(*p_str) {
    case '.':
      if (decimal_point || exponent_mark || !first_digit) {
        return FALSE;
      }
      decimal_point = TRUE;
      first_digit = FALSE;
      break;
    case 'e':
    case 'E':
      if (exponent_mark || !first_digit) {
        return FALSE;
      }
      exponent_mark = TRUE;
      first_digit = FALSE;
      break;
    case '0':
    case '1':
    case '2':
    case '3':
    case '4':
    case '5':
    case '6':
    case '7':
    case '8':
    case '9':
      first_digit = TRUE;
      break;
    case '-':
    case '+':
      if (exponent_sign || !exponent_mark || first_digit) {
        return FALSE;
      }
      exponent_sign = TRUE;
      break;
    default:
      return FALSE;
    }
    
    ++p_str; 
  }
  return first_digit;
}

int FLOAT::XER_decode(const XERdescriptor_t& p_td, XmlReaderWrap& reader,
  unsigned int flavor, unsigned int /*flavor2*/, embed_values_dec_struct_t*)
{
  bound_flag = FALSE;
  boolean exer  = is_exer(flavor);
  int success = reader.Ok();
  if (success <= 0) return 0;
  boolean own_tag = !(exer && (p_td.xer_bits & UNTAGGED)) && !is_exerlist(flavor);

  if (!own_tag) goto tagless;
  if (exer && (p_td.xer_bits & XER_ATTRIBUTE)) {
    verify_name(reader, p_td, exer);
tagless:
    const char * value = (const char *)reader.Value();
    if (value) {
      if (is_float(value)) {
        if (exer && (p_td.xer_bits & XER_DECIMAL) && p_td.fractionDigits != -1) {
          const char *p = strchr(value, '.');
          if (p != NULL) {
            unsigned int fraction_digits_pos = (int)(p - value) + 1 + p_td.fractionDigits;
            if (fraction_digits_pos < strlen(value)) {
              TTCN_EncDec_ErrorContext::error(TTCN_EncDec::ET_FLOAT_TR,
                "The float value (%s) contains too many fractionDigits. Expected %i or less.",
                value,
                p_td.fractionDigits);
            }
          }
        }
        bound_flag = TRUE;
        sscanf(value, "%lf", &float_value);
      } else if (strcmp(XER_NAN_STR, value) == 0 ) {
        bound_flag = TRUE;
#ifdef NAN
        float_value = NAN;
#else
        float_value = INFINITY + (-INFINITY);
#endif
      } else if (strcmp(XER_POS_INF_STR, value) == 0) {
        bound_flag = TRUE;
        float_value = (double)INFINITY;
      } else if (strcmp(XER_NEG_INF_STR, value) == 0) {
        bound_flag = TRUE;
        float_value = -(double)INFINITY;
      }
    }
    // Let the caller do reader.AdvanceAttribute();
  }
  else {
    int depth = -1;
    for (; success == 1; success = reader.Read()) {
      int type = reader.NodeType();
      if (XML_READER_TYPE_ELEMENT == type) {
        // If our parent is optional and there is an unexpected tag then return and
        // we stay unbound.
        if ((flavor & XER_OPTIONAL) && !check_name((const char*)reader.LocalName(), p_td, exer)) {
          return -1;
        }
        verify_name(reader, p_td, exer);
        if (reader.IsEmptyElement()) {
          if (exer && p_td.dfeValue != 0) {
            *this = *static_cast<const FLOAT*>(p_td.dfeValue);
          }
          reader.Read();
          break;
        }
        depth = reader.Depth();
      }
      else if (XML_READER_TYPE_TEXT == type && depth != -1) {
        const char * value = (const char*)reader.Value();
        if (value) {
          if (is_float(value)) {
            if (exer && (p_td.xer_bits & XER_DECIMAL) && p_td.fractionDigits != -1) {
              const char *p = strchr(value, '.');
              if (p != NULL) {
                unsigned int fraction_digits_pos = (int)(p - value) + 1 + p_td.fractionDigits;
                if (fraction_digits_pos < strlen(value)) {
                  TTCN_EncDec_ErrorContext::error(TTCN_EncDec::ET_FLOAT_TR,
                    "The float value (%s) contains too many fractionDigits. Expected %i or less.",
                    value,
                    p_td.fractionDigits);
                }
              }
            }
            bound_flag = TRUE;
            sscanf(value, "%lf", &float_value);
          } else if (strcmp("NaN", value) == 0 ) {
            bound_flag = TRUE;
#ifdef NAN
            float_value = NAN;
#else
            float_value = INFINITY + (-INFINITY);
#endif
          } else if (strcmp("INF", value) == 0) {
            bound_flag = TRUE;
            float_value = (double)INFINITY;
          } else if (strcmp("-INF", value) == 0) {
            bound_flag = TRUE;
            float_value = -(double)INFINITY;
          }
        }
      }
      else if (XML_READER_TYPE_END_ELEMENT == type) {
        verify_end(reader, p_td, depth, exer);
        if (!bound_flag && exer && p_td.dfeValue != 0) {
          *this = *static_cast<const FLOAT*>(p_td.dfeValue);
        }
        reader.Read();
        break;
      }
    } // next read
  } // if not attribute
  return 1; // decode successful
}

const char* POS_INF_STR = "\"infinity\"";
const char* NEG_INF_STR = "\"-infinity\"";
const char* NAN_STR = "\"not_a_number\"";

int FLOAT::JSON_encode(const TTCN_Typedescriptor_t&, JSON_Tokenizer& p_tok, boolean) const
{
  if (!is_bound()) {
    TTCN_EncDec_ErrorContext::error(TTCN_EncDec::ET_UNBOUND,
      "Encoding an unbound float value.");
    return -1;
  }
  
  double value = (double)float_value;
  if ((double)INFINITY == value) {
    return p_tok.put_next_token(JSON_TOKEN_STRING, POS_INF_STR);
  }
  if (-(double)INFINITY == value) {
    return p_tok.put_next_token(JSON_TOKEN_STRING, NEG_INF_STR);
  }
  if (isnan(value)) {
    return p_tok.put_next_token(JSON_TOKEN_STRING, NAN_STR);
  }
  
  // true if decimal representation possible (use %f format)
  boolean decimal_repr = (value == 0.0)
    || (value > -MAX_DECIMAL_FLOAT && value <= -MIN_DECIMAL_FLOAT)
    || (value >= MIN_DECIMAL_FLOAT && value < MAX_DECIMAL_FLOAT);
  
  char* tmp_str = mprintf(decimal_repr ? "%f" : "%e", value);
  int enc_len = p_tok.put_next_token(JSON_TOKEN_NUMBER, tmp_str);
  Free(tmp_str);
  return enc_len;
}

int FLOAT::JSON_decode(const TTCN_Typedescriptor_t& p_td, JSON_Tokenizer& p_tok, boolean p_silent, boolean, int)
{
  bound_flag = FALSE;
  json_token_t token = JSON_TOKEN_NONE;
  char* value = 0;
  size_t value_len = 0;
  size_t dec_len = 0;
  boolean use_default = FALSE;
  if (p_td.json->default_value.type == JD_STANDARD && 0 == p_tok.get_buffer_length()) {
    *this = *static_cast<const FLOAT*>(p_td.json->default_value.val);
    return dec_len;
  }
  if (p_td.json->default_value.type == JD_LEGACY && 0 == p_tok.get_buffer_length()) {
    // No JSON data in the buffer -> use default value
    value = const_cast<char*>(p_td.json->default_value.str);
    value_len = strlen(value);
    use_default = TRUE;
  } else {
    dec_len = p_tok.get_next_token(&token, &value, &value_len);
  }
  if (JSON_TOKEN_ERROR == token) {
    JSON_ERROR(TTCN_EncDec::ET_INVAL_MSG, JSON_DEC_BAD_TOKEN_ERROR, "");
    return JSON_ERROR_FATAL;
  }
  else if (JSON_TOKEN_STRING == token || use_default) {
    if (0 == strncmp(value, POS_INF_STR + (use_default ? 1 : 0), value_len)) {
      bound_flag = TRUE;
      float_value = INFINITY;
    }
    else if (0 == strncmp(value, NEG_INF_STR + (use_default ? 1 : 0), value_len)) {
      bound_flag = TRUE;
      float_value = -INFINITY;
    }
    else if (0 == strncmp(value, NAN_STR + (use_default ? 1 : 0), value_len)) {
      bound_flag = TRUE;
#ifdef NAN
      float_value = NAN;
#else
      float_value = INFINITY + (-INFINITY);
#endif
    }
    else if (!use_default) {
      char* spec_val = mprintf("float (%s, %s or %s)", POS_INF_STR, NEG_INF_STR, NAN_STR);
      JSON_ERROR(TTCN_EncDec::ET_INVAL_MSG, JSON_DEC_FORMAT_ERROR, "string", spec_val);
      Free(spec_val);
      bound_flag = FALSE;
      return JSON_ERROR_FATAL;
    }
  }
  else if (JSON_TOKEN_NUMBER == token) {
    char* value2 = mcopystrn(value, value_len);
    sscanf(value2, "%lf", &float_value);
    bound_flag = TRUE;
    Free(value2);
  } else {
    return JSON_ERROR_INVALID_TOKEN;
  }
  if (!bound_flag && use_default) {
    // Already checked the default value for the string possibilities, now
    // check for a valid number
    char* value2 = mcopystrn(value, value_len);
    sscanf(value2, "%lf", &float_value);
    bound_flag = TRUE;
    Free(value2);
  }
  return (int)dec_len;
}

int FLOAT::OER_encode(const TTCN_Typedescriptor_t&, TTCN_Buffer& p_buf) const {
  if (!is_bound()) {
    TTCN_EncDec_ErrorContext::error(TTCN_EncDec::ET_UNBOUND,
      "Encoding an unbound float value.");
    return -1;
  }
  // Mostly copied from BER
  if (float_value==0.0) {
    p_buf.put_c(0);
  } else if (float_value==(double)INFINITY) {
    p_buf.put_c(1);
    p_buf.put_c(64);
  } else if (float_value==-(double)INFINITY) {
    p_buf.put_c(1);
    p_buf.put_c(65);
  } else if (isnan((double)float_value)) {
    p_buf.put_c(1);
    p_buf.put_c(66);
  } else {
    double mantissa, exponent;
    exponent=floor(log10(fabs(float_value)))+1.0-DBL_DIG;
    mantissa=floor(float_value*pow(10.0,-exponent)+0.5);
    if(mantissa != 0.0) {
      while(fmod(mantissa,10.0) == 0.0) {
        mantissa/=10.0,exponent+=1.0;
      }
    }
    char * uc =
      mprintf("\x03%.f.E%s%.0f", mantissa, exponent==0.0?"+":"", exponent);
    size_t len = mstrlen(uc);
    p_buf.put_c(len);
    p_buf.put_s(len, (unsigned char*)uc);
    Free(uc);
  }
  return 0;
}
  
int FLOAT::OER_decode(const TTCN_Typedescriptor_t&, TTCN_Buffer& p_buf, OER_struct&) {
  TTCN_EncDec_ErrorContext ec("While decoding REAL type: ");
  const unsigned char* uc = p_buf.get_read_data();
  const size_t bytes = uc[0];
  p_buf.increase_pos(1);
  uc = p_buf.get_read_data();
  // Mostly copied from BER
  if(bytes==0) {
    float_value=0.0;
  }
  else if(uc[0] & 0x80) {
    /* binary encoding */
    /** \todo Perhaps it were good to implement this. Perhaps not. :) */
    ec.warning("Sorry, decoding of binary encoded REAL values not"
               " supported.");
    float_value=0.0;
  }
  else if(uc[0] & 0x40) {
    /* SpecialRealValue */
    if(bytes>1)
      ec.error(TTCN_EncDec::ET_INVAL_MSG,
               "In case of SpecialRealValue, the length of V-part must be 1"
               " (See X.690 8.5.8).");
    if (uc[0] & 0x02) {
      float_value=NOT_A_NUMBER;
    } else if(uc[0] & 0x01)
      /* MINUS-INFINITY */
      float_value=-INFINITY;
    else {
      /* PLUS-INFINITY */
      float_value=INFINITY;
    }
  }
  else {
    /* decimal encoding */
    if((uc[0] & 0x3C) || (uc[0] & 0x3F) == 0x00 )
      ec.error(TTCN_EncDec::ET_INVAL_MSG,
               "This is a reserved value: 0x%x (See X.690 8.5.7).",
               uc[0]);
    int NR=uc[0] & 0x03; // which NumericalRepresentation
    boolean
      leadingzero=FALSE,
      NR_error=FALSE;
    const unsigned char
      *Vstr_last=uc+bytes-1,
      *sign=NULL,
      *mant1=NULL,
      *decmark=NULL,
      *mant2=NULL,
      *expmark=NULL,
      *expsign=NULL,
      *expo=NULL,
      *ptr=uc+1;
    size_t
      mant1_len=0,
      mant2_len=0,
      expo_len=0;
    long exponum;
    if(bytes==1) goto dec_error;
    while(*ptr==' ') {
      if(ptr==Vstr_last) goto dec_error;
      ptr++;
    }
    if(*ptr=='+' || *ptr=='-') {
      sign=ptr;
      if(ptr==Vstr_last) goto dec_error;
      ptr++;
    }
    while(*ptr=='0') {
      leadingzero=TRUE;
      if(ptr==Vstr_last) goto str_end;
      ptr++;
    }
    while(*ptr>='0' && *ptr<='9') {
      if(mant1_len==0) mant1=ptr;
      mant1_len++;
      if(ptr==Vstr_last) goto str_end;
      ptr++;
    }
    if(*ptr=='.' || *ptr==',') {
      decmark=ptr;
      if(ptr==Vstr_last) goto str_end;
      ptr++;
    }
    while(*ptr>='0' && *ptr<='9') {
      if(mant2_len==0) mant2=ptr;
      mant2_len++;
      if(ptr==Vstr_last) goto str_end;
      ptr++;
    }
    if(!leadingzero && !mant1 && !mant2) goto dec_error;
    if(*ptr=='e' || *ptr=='E') {
      expmark=ptr;
      if(ptr==Vstr_last) goto dec_error;
      ptr++;
    }
    if(*ptr=='+' || *ptr=='-') {
      expsign=ptr;
      if(ptr==Vstr_last) goto dec_error;
      ptr++;
    }
    while(*ptr=='0') {
      expo=ptr;
      if(ptr==Vstr_last) goto str_end;
      ptr++;
    }
    while(*ptr>='0' && *ptr<='9') {
      if(expo_len==0) expo=ptr;
      expo_len++;
      if(ptr==Vstr_last) goto str_end;
      ptr++;
    }
    if(expo_len==0 && expo!=NULL) expo_len=1; /* only leading zero */
    if(expsign && !expo) goto dec_error;
    ec.error(TTCN_EncDec::ET_INVAL_MSG,
             "Superfluous part at the end of decimal encoding.");
  str_end:
    /* check NR */
    if(NR==1) {
      if(decmark || expmark) NR_error=TRUE;
    }
    else if(NR==2) {
      if(expmark) NR_error=TRUE;
    }
    if(NR_error)
      ec.error(TTCN_EncDec::ET_INVAL_MSG,
               "This decimal encoding does not conform to NR%d form.", NR);
    while(mant2_len>1 && mant2[mant2_len-1]=='0') mant2_len--;
    if(mant2_len==1 && *mant2=='0') mant2_len=0, mant2=NULL;
    float_value=0.0;
    if(mant1) for(size_t i=0; i<mant1_len; i++) {
      float_value*=10.0;
      float_value+=static_cast<double>(mant1[i]-'0');
    } // for i if...
    if(mant2) for(size_t i=0; i<mant2_len; i++) {
      float_value*=10.0;
      float_value+=static_cast<double>(mant2[i]-'0');
    } // for i if...
    exponum=0;
    if(expo) {
      if(ceil(log10(log10(DBL_MAX)))<expo_len) {
        /* overflow */
        if(expsign && *expsign=='-') {
          float_value=0.0;
        }
        else {
          if(sign && *sign=='-') float_value=-INFINITY;
          else float_value=INFINITY;
        }
        goto end;
      } // overflow
      else {
        /* no overflow */
        for(size_t i=0; i<expo_len; i++) {
          exponum*=10;
          exponum+=static_cast<int>(expo[i]-'0');
        } // for i
        if(expsign && *expsign=='-')
          exponum*=-1;
      } // no overflow
    } // if expo
    if(mant2) exponum-=mant2_len;
    float_value*=pow(10.0, static_cast<double>(exponum));
    goto end;
  dec_error:
    ec.error(TTCN_EncDec::ET_INVAL_MSG, "Erroneous decimal encoding.");
    float_value=0.0;
  }
 end:
  p_buf.increase_pos(bytes);
  bound_flag=TRUE;
  return 0;
}

// global functions

double operator+(double double_value, const FLOAT& other_value)
{
  other_value.must_bound("Unbound right operand of float addition.");
  return double_value + other_value.float_value;
}

double operator-(double double_value, const FLOAT& other_value)
{
  other_value.must_bound("Unbound right operand of float subtraction.");
  return double_value - other_value.float_value;
}

double operator*(double double_value, const FLOAT& other_value)
{
  other_value.must_bound("Unbound right operand of float multiplication.");
  return double_value * other_value.float_value;
}

double operator/(double double_value, const FLOAT& other_value)
{
  other_value.must_bound("Unbound right operand of float division.");
  if (other_value.float_value == 0.0) TTCN_error("Float division by zero.");
  return double_value / other_value.float_value;
}

boolean operator==(double double_value, const FLOAT& other_value)
{
  other_value.must_bound("Unbound right operand of float comparison.");
  return double_value == other_value.float_value || // check if they're both NaN
    (double_value != double_value &&
     other_value.float_value != other_value.float_value);
}

boolean operator<(double double_value, const FLOAT& other_value)
{
  other_value.must_bound("Unbound right operand of float comparison.");
  return double_value < other_value.float_value;
}

boolean operator>(double double_value, const FLOAT& other_value)
{
  other_value.must_bound("Unbound right operand of float comparison.");
  return double_value > other_value.float_value;
}

// float template class

void FLOAT_template::clean_up()
{
  switch (template_selection) {
  case VALUE_LIST:
  case COMPLEMENTED_LIST:
  case CONJUNCTION_MATCH:
    delete [] value_list.list_value;
    break;
  case IMPLICATION_MATCH:
    delete implication_.precondition;
    delete implication_.implied_template;
    break;
  case DYNAMIC_MATCH:
    dyn_match->ref_count--;
    if (dyn_match->ref_count == 0) {
      delete dyn_match->ptr;
      delete dyn_match;
    }
    break;
  default:
    break;
  }
  template_selection = UNINITIALIZED_TEMPLATE;
}

void FLOAT_template::copy_template(const FLOAT_template& other_value)
{
  switch (other_value.template_selection) {
  case SPECIFIC_VALUE:
    single_value = other_value.single_value;
    break;
  case OMIT_VALUE:
  case ANY_VALUE:
  case ANY_OR_OMIT:
    break;
  case VALUE_LIST:
  case COMPLEMENTED_LIST:
  case CONJUNCTION_MATCH:
    value_list.n_values = other_value.value_list.n_values;
    value_list.list_value = new FLOAT_template[value_list.n_values];
    for (unsigned int i = 0; i < value_list.n_values; i++)
      value_list.list_value[i].copy_template(
        other_value.value_list.list_value[i]);
    break;
  case VALUE_RANGE:
    value_range = other_value.value_range;
    break;
  case IMPLICATION_MATCH:
    implication_.precondition = new FLOAT_template(*other_value.implication_.precondition);
    implication_.implied_template = new FLOAT_template(*other_value.implication_.implied_template);
    break;
  case DYNAMIC_MATCH:
    dyn_match = other_value.dyn_match;
    dyn_match->ref_count++;
    break;
  default:
    TTCN_error("Copying an uninitialized/unsupported float template.");
  }
  set_selection(other_value);
}

FLOAT_template::FLOAT_template()
{

}

FLOAT_template::FLOAT_template(template_sel other_value)
  : Base_Template(other_value)
{
  check_single_selection(other_value);
}

FLOAT_template::FLOAT_template(double other_value)
  : Base_Template(SPECIFIC_VALUE)
{
  single_value = other_value;
}

FLOAT_template::FLOAT_template(const FLOAT& other_value)
  : Base_Template(SPECIFIC_VALUE)
{
  other_value.must_bound("Creating a template from an unbound float value.");
  single_value = other_value.float_value;
}

FLOAT_template::FLOAT_template(const OPTIONAL<FLOAT>& other_value)
{
  switch (other_value.get_selection()) {
  case OPTIONAL_PRESENT:
    set_selection(SPECIFIC_VALUE);
    single_value = (double)(const FLOAT&)other_value;
    break;
  case OPTIONAL_OMIT:
    set_selection(OMIT_VALUE);
    break;
  case OPTIONAL_UNBOUND:
    TTCN_error("Creating a float template from an unbound optional field.");
  }
}

FLOAT_template::FLOAT_template(const FLOAT_template& other_value)
: Base_Template()
{
  copy_template(other_value);
}

FLOAT_template::FLOAT_template(FLOAT_template* p_precondition, FLOAT_template* p_implied_template)
: Base_Template(IMPLICATION_MATCH)
{
  implication_.precondition = p_precondition;
  implication_.implied_template = p_implied_template;
}

FLOAT_template::FLOAT_template(Dynamic_Match_Interface<FLOAT>* p_dyn_match)
: Base_Template(DYNAMIC_MATCH)
{
  dyn_match = new dynmatch_struct<FLOAT>;
  dyn_match->ptr = p_dyn_match;
  dyn_match->ref_count = 1;
}

FLOAT_template::~FLOAT_template()
{
  clean_up();
}

FLOAT_template& FLOAT_template::operator=(template_sel other_value)
{
  check_single_selection(other_value);
  clean_up();
  set_selection(other_value);
  return *this;
}

FLOAT_template& FLOAT_template::operator=(double other_value)
{
  clean_up();
  set_selection(SPECIFIC_VALUE);
  single_value = other_value;
  return *this;
}

FLOAT_template& FLOAT_template::operator=(const FLOAT& other_value)
{
  other_value.must_bound("Assignment of an unbound float value "
                         "to a template.");
  clean_up();
  set_selection(SPECIFIC_VALUE);
  single_value = other_value.float_value;
  return *this;
}

FLOAT_template& FLOAT_template::operator=(const OPTIONAL<FLOAT>& other_value)
{
  clean_up();
  switch (other_value.get_selection()) {
  case OPTIONAL_PRESENT:
    set_selection(SPECIFIC_VALUE);
    single_value = (double)(const FLOAT&)other_value;
    break;
  case OPTIONAL_OMIT:
    set_selection(OMIT_VALUE);
    break;
  case OPTIONAL_UNBOUND:
    TTCN_error("Assignment of an unbound optional field to a float template.");
  }
  return *this;
}

FLOAT_template& FLOAT_template::operator=(const FLOAT_template& other_value)
{
  if (&other_value != this) {
    clean_up();
    copy_template(other_value);
  }
  return *this;
}

boolean FLOAT_template::match(double other_value, boolean /* legacy */) const
{
  switch (template_selection) {
  case SPECIFIC_VALUE:
    return single_value == other_value || // check if they're both NaN
      (single_value != single_value && other_value != other_value);
  case OMIT_VALUE:
    return FALSE;
  case ANY_VALUE:
  case ANY_OR_OMIT:
    return TRUE;
  case VALUE_LIST:
  case COMPLEMENTED_LIST:
    for (unsigned int i = 0; i < value_list.n_values; i++)
      if(value_list.list_value[i].match(other_value))
        return template_selection == VALUE_LIST;
    return template_selection == COMPLEMENTED_LIST;
  case VALUE_RANGE:
    
    return (
      // Min boundary is -infinity and (not min exclusive            or  the value is larger than -infinity)
      (!value_range.min_is_present && (!value_range.min_is_exclusive || other_value != MINUS_INFINITY)) ||
      // Min boundary is a number and not min exclusive           and it is less or equal than the value   or
      (value_range.min_is_present && !value_range.min_is_exclusive && value_range.min_value <= other_value) ||
      // Min boundary is a number and min exclusive              and it is less than the value
      (value_range.min_is_present && value_range.min_is_exclusive && value_range.min_value < other_value))
      &&
      // Max boundary is infinity  and  (not min exclusive            or the value is smaller than infinity)
      ((!value_range.max_is_present && (!value_range.max_is_exclusive || other_value != PLUS_INFINITY)) ||
      // Max boundary is a number and not max exclusive           and it is more or equal than the value   or
       (value_range.max_is_present && !value_range.max_is_exclusive && value_range.max_value >= other_value) ||
      // Max boundary is a number and max exclusive               and it is more than the value
       (value_range.max_is_present && value_range.max_is_exclusive && value_range.max_value > other_value));
  case CONJUNCTION_MATCH:
    for (unsigned int i = 0; i < value_list.n_values; i++) {
      if (!value_list.list_value[i].match(other_value)) {
        return FALSE;
      }
    }
    return TRUE;
  case IMPLICATION_MATCH:
    return !implication_.precondition->match(other_value) || implication_.implied_template->match(other_value);
  case DYNAMIC_MATCH:
    return dyn_match->ptr->match(other_value);
  default:
    TTCN_error("Matching with an uninitialized/unsupported float template.");
  }
  return FALSE;
}

boolean FLOAT_template::match(const FLOAT& other_value, boolean /* legacy */) const
{
  if (!other_value.is_bound()) return FALSE;
  return match(other_value.float_value);
}


void FLOAT_template::set_type(template_sel template_type,
  unsigned int list_length)
{
  clean_up();
  switch (template_type) {
  case VALUE_LIST:
  case COMPLEMENTED_LIST:
  case CONJUNCTION_MATCH:
    set_selection(template_type);
    value_list.n_values = list_length;
    value_list.list_value = new FLOAT_template[list_length];
    break;
  case VALUE_RANGE:
    set_selection(VALUE_RANGE);
    value_range.min_is_present = FALSE;
    value_range.max_is_present = FALSE;
    value_range.min_is_exclusive = FALSE;
    value_range.max_is_exclusive = FALSE;
    break;
  default:
    TTCN_error("Setting an invalid type for a float template.");
  }
}

FLOAT_template& FLOAT_template::list_item(unsigned int list_index)
{
  if (template_selection != VALUE_LIST &&
      template_selection != COMPLEMENTED_LIST &&
      template_selection != CONJUNCTION_MATCH)
    TTCN_error("Accessing a list element of a non-list float template.");
  if (list_index >= value_list.n_values)
    TTCN_error("Index overflow in a float value list template.");
  return value_list.list_value[list_index];
}

void FLOAT_template::set_min(double min_value)
{
  if (template_selection != VALUE_RANGE)
    TTCN_error("Float template is not range when setting lower limit.");
  if (value_range.max_is_present && value_range.max_value < min_value)
    TTCN_error("The lower limit of the range is greater than the "
               "upper limit in a float template.");
  value_range.min_is_present = TRUE;
  value_range.min_is_exclusive = FALSE;
  value_range.min_value = min_value;
}

void FLOAT_template::set_min(const FLOAT& min_value)
{
  min_value.must_bound("Using an unbound value when setting the lower bound "
                       "in a float range template.");
  set_min(min_value.float_value);
}

void FLOAT_template::set_max(double max_value)
{
  if (template_selection != VALUE_RANGE)
    TTCN_error("Float template is not range when setting upper limit.");
  if (value_range.min_is_present && value_range.min_value > max_value)
    TTCN_error("The upper limit of the range is smaller than the "
               "lower limit in a float template.");
  value_range.max_is_present = TRUE;
  value_range.max_is_exclusive = FALSE;
  value_range.max_value = max_value;
}

void FLOAT_template::set_max(const FLOAT& max_value)
{
  max_value.must_bound("Using an unbound value when setting the upper bound "
                       "in a float range template.");
  set_max(max_value.float_value);
}

void FLOAT_template::set_min_exclusive(boolean min_exclusive)
{
  if (template_selection != VALUE_RANGE)
    TTCN_error("Float template is not range when setting lower limit exclusiveness.");
  value_range.min_is_exclusive = min_exclusive;
}

void FLOAT_template::set_max_exclusive(boolean max_exclusive)
{
  if (template_selection != VALUE_RANGE)
    TTCN_error("Float template is not range when setting upper limit exclusiveness.");
  value_range.max_is_exclusive = max_exclusive;
}

double FLOAT_template::valueof() const
{
  if (template_selection != SPECIFIC_VALUE || is_ifpresent)
    TTCN_error("Performing a valueof "
               "or send operation on a non-specific float template.");
  return single_value;
}

void FLOAT_template::log() const
{
  switch (template_selection) {
  case SPECIFIC_VALUE:
    log_float(single_value);
    break;
  case COMPLEMENTED_LIST:
    TTCN_Logger::log_event_str("complement");
    // no break
  case CONJUNCTION_MATCH:
    if (template_selection == CONJUNCTION_MATCH) {
      TTCN_Logger::log_event_str("conjunct");
    }
    // no break
  case VALUE_LIST:
    TTCN_Logger::log_char('(');
    for (unsigned int i = 0; i < value_list.n_values; i++) {
      if (i > 0) TTCN_Logger::log_event_str(", ");
      value_list.list_value[i].log();
    }
    TTCN_Logger::log_char(')');
    break;
  case VALUE_RANGE:
    TTCN_Logger::log_char('(');
    if (value_range.min_is_exclusive) TTCN_Logger::log_char('!');
    if (value_range.min_is_present) log_float(value_range.min_value);
    else TTCN_Logger::log_event_str("-infinity");
    TTCN_Logger::log_event_str(" .. ");
    if (value_range.max_is_exclusive) TTCN_Logger::log_char('!');
    if (value_range.max_is_present) log_float(value_range.max_value);
    else TTCN_Logger::log_event_str("infinity");
    TTCN_Logger::log_char(')');
    break;
  case IMPLICATION_MATCH:
    implication_.precondition->log();
    TTCN_Logger::log_event_str(" implies ");
    implication_.implied_template->log();
    break;
  case DYNAMIC_MATCH:
    TTCN_Logger::log_event_str("@dynamic template");
    break;
  default:
    log_generic();
    break;
  }
  log_ifpresent();
}

void FLOAT_template::log_match(const FLOAT& match_value,
                               boolean /* legacy */) const
{
  if (TTCN_Logger::VERBOSITY_COMPACT == TTCN_Logger::get_matching_verbosity()
  &&  TTCN_Logger::get_logmatch_buffer_len() != 0) {
    TTCN_Logger::print_logmatch_buffer();
    TTCN_Logger::log_event_str(" := ");
  }
  match_value.log();
  TTCN_Logger::log_event_str(" with ");
  log();
  if (match(match_value)) TTCN_Logger::log_event_str(" matched");
  else TTCN_Logger::log_event_str(" unmatched");
}

void FLOAT_template::set_param(Module_Param& param) {
  param.basic_check(Module_Param::BC_TEMPLATE, "float template");
  Module_Param_Ptr mp = &param;
#ifdef TITAN_RUNTIME_2
  if (param.get_type() == Module_Param::MP_Reference) {
    mp = param.get_referenced_param();
  }
#endif
  switch (mp->get_type()) {
  case Module_Param::MP_Omit:
    *this = OMIT_VALUE;
    break;
  case Module_Param::MP_Any:
    *this = ANY_VALUE;
    break;
  case Module_Param::MP_AnyOrNone:
    *this = ANY_OR_OMIT;
    break;
  case Module_Param::MP_List_Template:
  case Module_Param::MP_ComplementList_Template:
  case Module_Param::MP_ConjunctList_Template: {
    FLOAT_template temp;
    temp.set_type(mp->get_type() == Module_Param::MP_List_Template ?
      VALUE_LIST : (mp->get_type() == Module_Param::MP_ConjunctList_Template ?
      CONJUNCTION_MATCH : COMPLEMENTED_LIST), mp->get_size());
    for (size_t i=0; i<mp->get_size(); i++) {
      temp.list_item(i).set_param(*mp->get_elem(i));
    }
    *this = temp;
    break; }
  case Module_Param::MP_Float:
    *this = mp->get_float();
    break;
  case Module_Param::MP_FloatRange:
    set_type(VALUE_RANGE);
    if (mp->has_lower_float()) set_min(mp->get_lower_float());
    if (mp->has_upper_float()) set_max(mp->get_upper_float());
    set_min_exclusive(mp->get_is_min_exclusive());
    set_max_exclusive(mp->get_is_max_exclusive());
    break;
  case Module_Param::MP_Implication_Template: {
    FLOAT_template* precondition = new FLOAT_template;
    precondition->set_param(*mp->get_elem(0));
    FLOAT_template* implied_template = new FLOAT_template;
    implied_template->set_param(*mp->get_elem(1));
    *this = FLOAT_template(precondition, implied_template);
  } break;
  case Module_Param::MP_Expression:
    switch (mp->get_expr_type()) {
    case Module_Param::EXPR_NEGATE: {
      FLOAT operand;
      operand.set_param(*mp->get_operand1());
      *this = - operand;
      break; }
    case Module_Param::EXPR_ADD: {
      FLOAT operand1, operand2;
      operand1.set_param(*mp->get_operand1());
      operand2.set_param(*mp->get_operand2());
      *this = operand1 + operand2;
      break; }
    case Module_Param::EXPR_SUBTRACT: {
      FLOAT operand1, operand2;
      operand1.set_param(*mp->get_operand1());
      operand2.set_param(*mp->get_operand2());
      *this = operand1 - operand2;
      break; }
    case Module_Param::EXPR_MULTIPLY: {
      FLOAT operand1, operand2;
      operand1.set_param(*mp->get_operand1());
      operand2.set_param(*mp->get_operand2());
      *this = operand1 * operand2;
      break; }
    case Module_Param::EXPR_DIVIDE: {
      FLOAT operand1, operand2;
      operand1.set_param(*mp->get_operand1());
      operand2.set_param(*mp->get_operand2());
      if (operand2 == 0.0) {
        param.error("Floating point division by zero.");
      }
      *this = operand1 / operand2;
      break; }
    default:
      param.expr_type_error("a float");
      break;
    }
    break;
  default:
    param.type_error("float template");
  }
  is_ifpresent = param.get_ifpresent() || mp->get_ifpresent();
}

#ifdef TITAN_RUNTIME_2
Module_Param* FLOAT_template::get_param(Module_Param_Name& param_name) const
{
  Module_Param* mp = NULL;
  switch (template_selection) {
  case UNINITIALIZED_TEMPLATE:
    mp = new Module_Param_Unbound();
    break;
  case OMIT_VALUE:
    mp = new Module_Param_Omit();
    break;
  case ANY_VALUE:
    mp = new Module_Param_Any();
    break;
  case ANY_OR_OMIT:
    mp = new Module_Param_AnyOrNone();
    break;
  case SPECIFIC_VALUE:
    mp = new Module_Param_Float(single_value);
    break;
  case VALUE_LIST:
  case COMPLEMENTED_LIST:
  case CONJUNCTION_MATCH: {
    if (template_selection == VALUE_LIST) {
      mp = new Module_Param_List_Template();
    }
    else if (template_selection == CONJUNCTION_MATCH) {
      mp = new Module_Param_ConjunctList_Template();
    }
    else {
      mp = new Module_Param_ComplementList_Template();
    }
    for (size_t i = 0; i < value_list.n_values; ++i) {
      mp->add_elem(value_list.list_value[i].get_param(param_name));
    }
    break; }
  case VALUE_RANGE:
    mp = new Module_Param_FloatRange(
      value_range.min_value, value_range.min_is_present,
      value_range.max_value, value_range.max_is_present, value_range.min_is_exclusive, value_range.max_is_exclusive);
    break;
  case IMPLICATION_MATCH:
    mp = new Module_Param_Implication_Template();
    mp->add_elem(implication_.precondition->get_param(param_name));
    mp->add_elem(implication_.implied_template->get_param(param_name));
    break;
  default:
    TTCN_error("Referencing an uninitialized/unsupported float template.");
    break;
  }
  if (is_ifpresent) {
    mp->set_ifpresent();
  }
  return mp;
}
#endif

void FLOAT_template::encode_text(Text_Buf& text_buf) const
{
  encode_text_base(text_buf);
  switch (template_selection) {
  case OMIT_VALUE:
  case ANY_VALUE:
  case ANY_OR_OMIT:
    break;
  case SPECIFIC_VALUE:
    text_buf.push_double(single_value);
    break;
  case VALUE_LIST:
  case COMPLEMENTED_LIST:
    text_buf.push_int(value_list.n_values);
    for (unsigned int i = 0; i < value_list.n_values; i++)
      value_list.list_value[i].encode_text(text_buf);
    break;
  case VALUE_RANGE:
    text_buf.push_int(value_range.min_is_present ? 1 : 0);
    if (value_range.min_is_present)
      text_buf.push_double(value_range.min_value);
    text_buf.push_int(value_range.max_is_present ? 1 : 0);
    if (value_range.max_is_present)
      text_buf.push_double(value_range.max_value);
    break;
  default:
    TTCN_error("Text encoder: Encoding an undefined/unsupported "
               "float template.");
  }
}

void FLOAT_template::decode_text(Text_Buf& text_buf)
{
  clean_up();
  decode_text_base(text_buf);
  switch (template_selection) {
  case OMIT_VALUE:
  case ANY_VALUE:
  case ANY_OR_OMIT:
    break;
  case SPECIFIC_VALUE:
    single_value = text_buf.pull_double();
    break;
  case VALUE_LIST:
  case COMPLEMENTED_LIST:
    value_list.n_values = text_buf.pull_int().get_val();
    value_list.list_value = new FLOAT_template[value_list.n_values];
    for (unsigned int i = 0; i < value_list.n_values; i++)
      value_list.list_value[i].decode_text(text_buf);
    break;
  case VALUE_RANGE:
    value_range.min_is_present = text_buf.pull_int() != 0;
    if (value_range.min_is_present)
      value_range.min_value = text_buf.pull_double();
    value_range.max_is_present = text_buf.pull_int() != 0;
    if (value_range.max_is_present)
      value_range.max_value = text_buf.pull_double();
    value_range.min_is_exclusive = FALSE;
    value_range.max_is_exclusive = FALSE;
    break;
  default:
    TTCN_error("Text decoder: An unknown/unsupported selection was "
               "received for a float template.");
  }
}

boolean FLOAT_template::is_present(boolean legacy /* = FALSE */) const
{
  if (template_selection==UNINITIALIZED_TEMPLATE) return FALSE;
  return !match_omit(legacy);
}

boolean FLOAT_template::match_omit(boolean legacy /* = FALSE */) const
{
  if (is_ifpresent) return TRUE;
  switch (template_selection) {
  case OMIT_VALUE:
  case ANY_OR_OMIT:
    return TRUE;
  case IMPLICATION_MATCH:
    return !implication_.precondition->match_omit() || implication_.implied_template->match_omit();
  case VALUE_LIST:
  case COMPLEMENTED_LIST:
    if (legacy) {
      // legacy behavior: 'omit' can appear in the value/complement list
      for (unsigned int i=0; i<value_list.n_values; i++)
        if (value_list.list_value[i].match_omit())
          return template_selection==VALUE_LIST;
      return template_selection==COMPLEMENTED_LIST;
    }
    // else fall through
  default:
    return FALSE;
  }
  return FALSE;
}

#ifndef TITAN_RUNTIME_2
void FLOAT_template::check_restriction(template_res t_res, const char* t_name,
                                       boolean legacy /* = FALSE */) const
{
  if (template_selection==UNINITIALIZED_TEMPLATE) return;
  switch ((t_name&&(t_res==TR_VALUE))?TR_OMIT:t_res) {
  case TR_VALUE:
    if (!is_ifpresent && template_selection==SPECIFIC_VALUE) return;
    break;
  case TR_OMIT:
    if (!is_ifpresent && (template_selection==OMIT_VALUE ||
        template_selection==SPECIFIC_VALUE)) return;
    break;
  case TR_PRESENT:
    if (!match_omit(legacy)) return;
    break;
  default:
    return;
  }
  TTCN_error("Restriction `%s' on template of type %s violated.",
             get_res_name(t_res), t_name ? t_name : "float");
}
#endif
