/******************************************************************************
 * Copyright (c) 2000-2025 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
 *   Ormandi, Matyas
 *   Raduly, Csaba
 *   Szabados, Kristof
 *   Szabo, Bence Janos
 *   Szabo, Janos Zoltan – initial implementation
 *   Szalai, Gabor
 *   Tatarka, Gabor
 *
 ******************************************************************************/
#include "Integer.hh"

#include <limits.h>
#include <strings.h>
#include <string.h>
#include <ctype.h>

#include "Error.hh"
#include "Optional.hh"
#include "Types.h"
#include "Encdec.hh"
#include "BER.hh"
#include "OER.hh"
#include "Charstring.hh"

#ifndef TITAN_ASN1ENC
#include "Logger.hh"
#include "Param_Types.hh"
#include "Addfunc.hh"
#include "RAW.hh"
#include "TEXT.hh"
#include "XmlReader.hh"
#endif

#include <openssl/bn.h>
#include <openssl/crypto.h>

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

#if defined(__GNUC__) && __GNUC__ >= 3
// To provide prediction information for the compiler.
// Borrowed from /usr/src/linux/include/linux/compiler.h.
#define likely(x)   __builtin_expect(!!(x), 1)
#define unlikely(x) __builtin_expect(!!(x), 0)
#else
#define likely(x)   (x)
#define unlikely(x) (x)
#endif

#ifndef TITAN_ASN1ENC
static const Token_Match integer_value_match("^([\t ]*-?[0-9]+).*$", TRUE);
#endif

int_val_t INTEGER::get_val() const
{
  if (likely(native_flag)) return int_val_t(val.native);
  else return int_val_t(BN_dup(val.openssl));
}

void INTEGER::set_val(const int_val_t& other_value)
{
  clean_up();
  bound_flag = TRUE;
  native_flag = other_value.native_flag;
  if (likely(native_flag)) val.native = other_value.val.native;
  else val.openssl = BN_dup(other_value.val.openssl);
}

INTEGER::INTEGER()
{
  bound_flag = FALSE;
  native_flag = TRUE;
}

INTEGER::INTEGER(int other_value)
{
  bound_flag = TRUE;
  native_flag = TRUE;
  val.native = other_value;
}

INTEGER::INTEGER(const INTEGER& other_value)
: Base_Type(other_value)
{
  other_value.must_bound("Copying an unbound integer value.");
  bound_flag = TRUE;
  native_flag = other_value.native_flag;
  if (likely(native_flag)) val.native = other_value.val.native;
  else val.openssl = BN_dup(other_value.val.openssl);
}

/// Return 0 if fail, 1 on success
int INTEGER::from_string(const char *s) {
  BIGNUM *other_value_int = NULL;
  if (BN_dec2bn(&other_value_int, s + (*s == '+')))
  {
    bound_flag = TRUE;
    if (BN_num_bits(other_value_int) > (int)sizeof(int) * 8 - 1) {
      native_flag = FALSE;
      val.openssl = other_value_int;
    } else {
      native_flag = TRUE;
      val.native = string2RInt(s);
      BN_free(other_value_int);
    }
    return 1;
  }
  else return 0;
}

INTEGER::INTEGER(const char *other_value)
{
  if (unlikely(!other_value))
    TTCN_error("Unexpected error when converting NULL pointer to integer");
  bound_flag = TRUE;
  if (!from_string(other_value)) TTCN_error(
    "Unexpected error when converting `%s' to integer", other_value);
}

// For internal use only.  It's not part of the public interface.
INTEGER::INTEGER(BIGNUM *other_value)
{
  if (unlikely(!other_value))
    TTCN_error("Unexpected error when initializing an integer");
  bound_flag = TRUE;
  native_flag = FALSE;
  val.openssl = other_value;
}

INTEGER::~INTEGER()
{
  if (!bound_flag) return;
  if (unlikely(!native_flag)) BN_free(val.openssl);
}

void INTEGER::clean_up()
{
  if (!bound_flag) return;
  if (unlikely(!native_flag)) BN_free(val.openssl);
  bound_flag = FALSE;
}

INTEGER& INTEGER::operator=(int other_value)
{
  clean_up();
  bound_flag = TRUE;
  native_flag = TRUE;
  val.native = other_value;
  return *this;
}

INTEGER& INTEGER::operator=(const INTEGER& other_value)
{
  if (this == &other_value)
    return *this;
  other_value.must_bound("Assignment of an unbound integer value.");
  clean_up();
  bound_flag = TRUE;
  native_flag = other_value.native_flag;
  if (likely(native_flag)) val.native = other_value.val.native;
  else val.openssl = BN_dup(other_value.val.openssl);
  return *this;
}

// A bit more specific than operator+().
INTEGER& INTEGER::operator++()
{
  must_bound("Unbound integer operand of unary increment operator.");
  if (likely(native_flag)) {
    if (unlikely(val.native == INT_MAX)) {
      BIGNUM *val_openssl = to_openssl(val.native);
      BIGNUM *one = BN_new();
      BN_set_word(one, 1);
      BN_add(val_openssl, val_openssl, one);
      BN_free(one);
      native_flag = FALSE;
      val.openssl = val_openssl;
    } else {
      val.native++;
    }
  } else {
    BIGNUM *one = BN_new();
    BN_set_word(one, 1);
    BN_add(val.openssl, val.openssl, one);
    BN_free(one);
  }
  return *this;
}

// A bit more specific than operator-().
INTEGER& INTEGER::operator--()
{
  must_bound("Unbound integer operand of unary decrement operator.");
  if (likely(native_flag)) {
    if (unlikely(val.native == INT_MIN)) {
      BIGNUM *val_openssl = to_openssl(val.native);
      BIGNUM *one = BN_new();
      BN_set_word(one, 1);
      BN_sub(val_openssl, val_openssl, one);
      BN_free(one);
      native_flag = FALSE;
      val.openssl = val_openssl;
    } else {
      val.native--;
    }
  } else {
    BIGNUM *one = BN_new();
    BN_set_word(one, 1);
    BN_sub(val.openssl, val.openssl, one);
    BN_free(one);
  }
  return *this;
}

INTEGER INTEGER::operator+() const
{
  must_bound("Unbound integer operand of unary + operator.");
  return *this;
}

INTEGER INTEGER::operator-() const
{
  must_bound("Unbound integer operand of unary - operator (negation).");
  if (likely(native_flag)) {
    if (unlikely(val.native == INT_MIN)) {
      BIGNUM *result = to_openssl(INT_MIN);
      BN_set_negative(result, 0);
      return INTEGER(result);
    } else {
      return INTEGER(-val.native);
    }
  } else {
    BIGNUM *int_max_plus_one = to_openssl(INT_MIN);
    BN_set_negative(int_max_plus_one, 0);
    int cmp = BN_cmp(val.openssl, int_max_plus_one);
    BN_free(int_max_plus_one);
    if (unlikely(cmp == 0)) {
      return INTEGER(INT_MIN);
    } else {
      BIGNUM *result = BN_dup(val.openssl);
      BN_set_negative(result, !BN_is_negative(result));
      return INTEGER(result);
    }
  }
}

INTEGER INTEGER::operator+(int other_value) const
{
  // Don't call out if slow.  Implement this specific case right here.
  return *this + INTEGER(other_value);
}

INTEGER INTEGER::operator+(const INTEGER& other_value) const
{
  must_bound("Unbound left operand of integer addition.");
  other_value.must_bound("Unbound right operand of integer addition.");
  //  *this +  other_value =   *this add other_value
  //  *this + -other_value =   *this sub other_value
  // -*this +  other_value =   other_value sub *this
  // -*this + -other_value = -(*this add other_value)
  // Use only inline functions and BN_* directly.  Call out for operator- in
  // the beginning.
  boolean this_neg = native_flag ? (val.native < 0)
    : BN_is_negative(val.openssl);
  boolean other_value_neg = other_value.native_flag
    ? (other_value.val.native < 0) : BN_is_negative(other_value.val.openssl);
  if (!this_neg && other_value_neg) return operator-(-other_value);
  if (this_neg && !other_value_neg) return other_value.operator-(-(*this));
  // at this point the values are either both positive or both negative
  if (likely(native_flag)) {
    if (likely(other_value.native_flag)) {
      // check int overflow/underflow pre-emptively
      if (unlikely((!this_neg && (val.native > INT_MAX - other_value.val.native)) ||
                   (this_neg && (val.native < INT_MIN - other_value.val.native)))) {
        BIGNUM *this_int = to_openssl(val.native);
        BIGNUM *other_val_int = to_openssl(other_value.val.native);
        BN_add(this_int, this_int, other_val_int);
        BN_free(other_val_int);
        return INTEGER(this_int);
      }
      else {
        return INTEGER(val.native + other_value.val.native);
      }
    } else {
      // int (>= 0) + BIGNUM == BIGNUM.
      BIGNUM *this_int = to_openssl(val.native);
      BN_add(this_int, this_int, other_value.val.openssl);
      return INTEGER(this_int);
    }
  } else {
    // BIGNUM + int (>= 0) == BIGNUM.
    BIGNUM *result = BN_new();
    BIGNUM *other_value_int;
    other_value_int = other_value.native_flag
      ? to_openssl(other_value.val.native) : other_value.val.openssl;
    BN_add(result, val.openssl, other_value_int);
    if (likely(other_value.native_flag)) BN_free(other_value_int);
    return INTEGER(result);
  }
}

INTEGER INTEGER::operator-(int other_value) const
{
  return *this - INTEGER(other_value);
}

INTEGER INTEGER::operator-(const INTEGER& other_value) const
{
  must_bound("Unbound left operand of integer subtraction.");
  other_value.must_bound("Unbound right operand of integer subtraction.");
  //  *this -  other_value =   *this sub other_value
  // -*this -  other_value = -(*this add other_value)
  //  *this - -other_value =   *this add other_value
  // -*this - -other_value =  -*this add other_value  = other_value sub *this
  boolean this_neg = native_flag ? (val.native < 0)
    : BN_is_negative(val.openssl);
  boolean other_value_neg = other_value.native_flag
    ? (other_value.val.native < 0) : BN_is_negative(other_value.val.openssl);
  if (!this_neg && other_value_neg) return operator+(-other_value);
  if (this_neg && !other_value_neg) return -other_value.operator+(-(*this));
  if (likely(native_flag)) {
    if (likely(other_value.native_flag)) {
      // Since both operands are non-negative the most negative result of a
      // subtraction can be -INT_MAX and according to limits.h:
      // # define INT_MIN (-INT_MAX - 1)
      return INTEGER(val.native - other_value.val.native);
    } else {
      BIGNUM *this_int = to_openssl(val.native);
      BN_sub(this_int, this_int, other_value.val.openssl);
      // The result can be small enough to fit in int.  Back conversion is a
      // costly operation using strings all the time.
      if (BN_num_bits(this_int) <= (int)sizeof(int) * 8 - 1) {
        char *result_str = BN_bn2dec(this_int);
        RInt result = string2RInt(result_str);
        OPENSSL_free(result_str);
        BN_free(this_int);
        return INTEGER(result);
      } else {
        return INTEGER(this_int);
      }
    }
  } else {
    BIGNUM *result = BN_new();
    BIGNUM *other_value_int = other_value.native_flag ?
      to_openssl(other_value.val.native) : other_value.val.openssl;
    BN_sub(result, val.openssl, other_value_int);
    if (other_value.native_flag) BN_free(other_value_int);
    if (BN_num_bits(result) <= (int)sizeof(int) * 8 - 1) {
      char *result_str = BN_bn2dec(result);
      RInt result_int = string2RInt(result_str);
      OPENSSL_free(result_str);
      BN_free(result);
      return INTEGER(result_int);
    } else {
      return INTEGER(result);
    }
  }
}

INTEGER INTEGER::operator*(int other_value) const
{
  return *this * INTEGER(other_value);
}

INTEGER INTEGER::operator*(const INTEGER& other_value) const
{
  must_bound("Unbound left operand of integer multiplication.");
  other_value.must_bound("Unbound right operand of integer multiplication.");
  if ((native_flag && val.native == 0) || (other_value.native_flag &&
    other_value.val.native == 0)) return INTEGER((int)0);
  if (likely(native_flag)) {
    if (likely(other_value.native_flag)) {
      // TODO: Improve.
      if (likely(abs(val.native) < 32768 && abs(other_value.val.native) < 32768)) {
    	return INTEGER(val.native * other_value.val.native);
      } else {
        BIGNUM *this_int = to_openssl(val.native);
        BIGNUM *other_value_int = to_openssl(other_value.val.native);
        BN_CTX *ctx = BN_CTX_new();
        BN_mul(this_int, this_int, other_value_int, ctx);
        BN_CTX_free(ctx);
        BN_free(other_value_int);
        if (BN_num_bits(this_int) < (int)sizeof(int) * 8) {
          BN_free(this_int);
          return INTEGER(val.native * other_value.val.native);
        } else {
          return INTEGER(this_int);
        }
      }
    } else {
      BIGNUM *this_int = to_openssl(val.native);
      BN_CTX *ctx = BN_CTX_new();
      BN_mul(this_int, this_int, other_value.val.openssl, ctx);
      BN_CTX_free(ctx);
      return INTEGER(this_int);
    }
  } else {
    BIGNUM *result = BN_new();
    BIGNUM *other_value_int = NULL;
    BN_CTX *ctx = BN_CTX_new();
    other_value_int = other_value.native_flag
      ? to_openssl(other_value.val.native) : other_value.val.openssl;
    BN_mul(result, val.openssl, other_value_int, ctx);
    BN_CTX_free(ctx);
    if (likely(other_value.native_flag)) BN_free(other_value_int);
    return INTEGER(result);
  }
}

INTEGER INTEGER::operator/(int other_value) const
{
  return *this / INTEGER(other_value);
}

INTEGER INTEGER::operator/(const INTEGER& other_value) const
{
  must_bound("Unbound left operand of integer division.");
  other_value.must_bound("Unbound right operand of integer division.");
  if (other_value == 0) TTCN_error("Integer division by zero.");
  if (native_flag && val.native == 0) return INTEGER((int)0);
  if (likely(native_flag)) {
    if (likely(other_value.native_flag)) {
      return INTEGER(val.native / other_value.val.native);
    } else {
      BIGNUM *this_int = to_openssl(val.native);
      BN_CTX *ctx = BN_CTX_new();
      BN_div(this_int, NULL, this_int, other_value.val.openssl, ctx);
      BN_CTX_free(ctx);
      if (BN_num_bits(this_int) <= (int)sizeof(int) * 8 - 1) {
        char *result_str = BN_bn2dec(this_int);
        RInt result = string2RInt(result_str);
        OPENSSL_free(result_str);
        BN_free(this_int);
        return INTEGER(result);
      } else {
        return INTEGER(this_int);
      }
    }
  } else {
    BIGNUM *result = BN_new();
    BIGNUM *other_value_int = NULL;
    BN_CTX *ctx = BN_CTX_new();
    other_value_int = other_value.native_flag
      ? to_openssl(other_value.val.native) : other_value.val.openssl;
    BN_div(result, NULL, val.openssl, other_value_int, ctx);
    if (likely(other_value.native_flag)) BN_free(other_value_int);
    BN_CTX_free(ctx);
    if (BN_num_bits(result) <= (int)sizeof(int) * 8 - 1) {
      char *result_str = BN_bn2dec(result);
      RInt result_i = string2RInt(result_str);
      OPENSSL_free(result_str);
      BN_free(result);
      return INTEGER(result_i);
    } else {
      return INTEGER(result);
    }
  }
}

boolean INTEGER::operator==(int other_value) const
{
  must_bound("Unbound left operand of integer comparison.");
  if (likely(native_flag)) {
    return val.native == other_value;
  } else {
    BIGNUM *other_value_int = to_openssl(other_value);
    int equal = BN_cmp(val.openssl, other_value_int);
    BN_free(other_value_int);
    return equal == 0;
  }
}

boolean INTEGER::operator==(const INTEGER& other_value) const
{
  must_bound("Unbound left operand of integer comparison.");
  other_value.must_bound("Unbound right operand of integer comparison.");
  if (likely(native_flag)) {
    if (likely(other_value.native_flag)) {
      return val.native == other_value.val.native;
    } else {
      BIGNUM *this_int = to_openssl(val.native);
      int equal = BN_cmp(this_int, other_value.val.openssl);
      BN_free(this_int);
      return equal == 0;
    }
  } else {
    if (likely(other_value.native_flag)) {
      BIGNUM *other_value_int = to_openssl(other_value.val.native);
      int equal = BN_cmp(val.openssl, other_value_int);
      BN_free(other_value_int);
      return equal == 0;
    } else {
      return BN_cmp(val.openssl, other_value.val.openssl) == 0;
    }
  }
}

boolean INTEGER::operator<(int other_value) const
{
  return *this < INTEGER(other_value);
}

boolean INTEGER::operator<(const INTEGER& other_value) const
{
  must_bound("Unbound left operand of integer comparison.");
  other_value.must_bound("Unbound right operand of integer comparison.");
  if (likely(native_flag)) {
    if (likely(other_value.native_flag)) {
      return val.native < other_value.val.native;
    } else {
      BIGNUM *this_int = to_openssl(val.native);
      int equal = BN_cmp(this_int, other_value.val.openssl);
      BN_free(this_int);
      return equal == -1;
    }
  } else {
    if (likely(other_value.native_flag)) {
      BIGNUM *other_value_int = to_openssl(other_value.val.native);
      int equal = BN_cmp(val.openssl, other_value_int);
      BN_free(other_value_int);
      return equal == -1;
    } else {
      return BN_cmp(val.openssl, other_value.val.openssl) == -1;
    }
  }
}

boolean INTEGER::operator>(int other_value) const
{
  return *this > INTEGER(other_value);
}

boolean INTEGER::operator>(const INTEGER& other_value) const
{
  // A simple call to operator< and operator== would be much simplier.
  must_bound("Unbound left operand of integer comparison.");
  other_value.must_bound("Unbound right operand of integer comparison.");
  if (likely(native_flag)) {
    if (likely(other_value.native_flag)) {
      return val.native > other_value.val.native;
    } else {
      BIGNUM *this_int = to_openssl(val.native);
      int equal = BN_cmp(this_int, other_value.val.openssl);
      BN_free(this_int);
      return equal == 1;
    }
  } else {
    if (likely(other_value.native_flag)) {
      BIGNUM *other_value_int = to_openssl(other_value.val.native);
      int equal = BN_cmp(val.openssl, other_value_int);
      BN_free(other_value_int);
      return equal == 1;
    } else {
      return BN_cmp(val.openssl, other_value.val.openssl) == 1;
    }
  }
}

INTEGER::operator int() const
{
  must_bound("Using the value of an unbound integer variable.");
  if (unlikely(!native_flag))
    TTCN_error("Invalid conversion of a large integer value");
  return val.native;
}

// To avoid ambiguity we have a separate function to convert our INTEGER
// object to long long.
long long int INTEGER::get_long_long_val() const
{
  must_bound("Using the value of an unbound integer variable.");
  if (likely(native_flag)) return val.native;
  boolean is_negative = BN_is_negative(val.openssl);
  long long int ret_val = 0;
  if (unlikely(BN_is_zero(val.openssl))) return 0;
  // It feels so bad accessing a BIGNUM directly, but faster than string
  // conversion...
  // I know, I had to fix this... Bence
  if ((size_t)BN_num_bytes(val.openssl) <= sizeof(BN_ULONG)) {
    return !is_negative ? BN_get_word(val.openssl) : -BN_get_word(val.openssl);
  }
  
  int num_bytes = BN_num_bytes(val.openssl);
  unsigned char* tmp = (unsigned char*)Malloc(num_bytes * sizeof(unsigned char));
  BN_bn2bin(val.openssl, tmp);
  ret_val = tmp[0] & 0xff;
  for (int i = 1; i < num_bytes; i++) {
    ret_val <<= 8;
    ret_val += tmp[i] & 0xff;
  }
  Free(tmp);
  return !is_negative ? ret_val : -ret_val;
}

void INTEGER::set_long_long_val(long long int other_value)
{
  clean_up();
  bound_flag = TRUE;
  // Seems to be a native.  It's very strange if someone calls this with a
  // small number.  A simple assignment should be used for such values.
  if (unlikely((RInt)other_value == other_value)) {
    native_flag = TRUE;
    val.native = other_value;
    return;
  }
  native_flag = FALSE;
  val.openssl = BN_new();
  // Make it 0.
  BN_zero(val.openssl);
  boolean is_negative = other_value < 0;
  unsigned long long int tmp = !is_negative ? other_value : -other_value;
  for (int i = sizeof(long long int) - 1; i >= 0; i--) {
    BN_add_word(val.openssl, (tmp >> 8 * i) & 0xff);
    if (i) BN_lshift(val.openssl, val.openssl, 8);
  }
  BN_set_negative(val.openssl, is_negative ? 1 : 0);
}

#ifndef TITAN_ASN1ENC
void INTEGER::log() const
{
  if (likely(bound_flag)) {
    if (likely(native_flag)) {
      TTCN_Logger::log_event("%d", val.native);
    } else {
      char *tmp = BN_bn2dec(val.openssl);
      TTCN_Logger::log_event("%s", tmp);
      OPENSSL_free(tmp);
    }
  } else {
    TTCN_Logger::log_event_unbound();
  }
}
#else
CHARSTRING INTEGER::log() const
{
  if (likely(bound_flag)) {
    CHARSTRING ret_val;
    if (likely(native_flag)) {
      char* tmp = mprintf("%d", val.native);
      ret_val = tmp;
      Free(tmp);
    } else {
      char *tmp = BN_bn2dec(val.openssl);
      ret_val = tmp;
      OPENSSL_free(tmp);
    }
    return ret_val;
  }
  else {
    return UNBOUND_VALUE_LOG;
  }
}
#endif

#ifndef TITAN_ASN1ENC
void INTEGER::set_param(Module_Param& param)
{
  param.basic_check(Module_Param::BC_VALUE, "integer 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_Integer: {
    clean_up();
    bound_flag = TRUE;
    const int_val_t* const int_val = mp->get_integer();
    native_flag = int_val->is_native();
    if (likely(native_flag)){
      val.native = int_val->get_val();
    } else {
      val.openssl = BN_dup(int_val->get_val_openssl());
    }
    break; }
  case Module_Param::MP_Expression:
    switch (mp->get_expr_type()) {
    case Module_Param::EXPR_NEGATE: {
      INTEGER operand;
      operand.set_param(*mp->get_operand1());
      *this = - operand;
      break; }
    case Module_Param::EXPR_ADD: {
      INTEGER operand1, operand2;
      operand1.set_param(*mp->get_operand1());
      operand2.set_param(*mp->get_operand2());
      *this = operand1 + operand2;
      break; }
    case Module_Param::EXPR_SUBTRACT: {
      INTEGER operand1, operand2;
      operand1.set_param(*mp->get_operand1());
      operand2.set_param(*mp->get_operand2());
      *this = operand1 - operand2;
      break; }
    case Module_Param::EXPR_MULTIPLY: {
      INTEGER operand1, operand2;
      operand1.set_param(*mp->get_operand1());
      operand2.set_param(*mp->get_operand2());
      *this = operand1 * operand2;
      break; }
    case Module_Param::EXPR_DIVIDE: {
      INTEGER operand1, operand2;
      operand1.set_param(*mp->get_operand1());
      operand2.set_param(*mp->get_operand2());
      if (operand2 == 0) {
        param.error("Integer division by zero.");
      }
      *this = operand1 / operand2;
      break; }
    default:
      param.expr_type_error("an integer");
      break;
    }
    break;
  default:
    param.type_error("integer value");
    break;
  }
}

#ifdef TITAN_RUNTIME_2
Module_Param* INTEGER::get_param(Module_Param_Name& /* param_name */) const
{
  if (!bound_flag) {
    return new Module_Param_Unbound();
  }
  if (native_flag) {
    return new Module_Param_Integer(new int_val_t(val.native));
  }
  return new Module_Param_Integer(new int_val_t(BN_dup(val.openssl)));
}
#endif

void INTEGER::encode_text(Text_Buf& text_buf) const
{
  must_bound("Text encoder: Encoding an unbound integer value.");
  if (likely(native_flag)) {
    text_buf.push_int(val.native);
  } else {
    int_val_t *tmp = new int_val_t(BN_dup(val.openssl));
    text_buf.push_int(*tmp);
    delete tmp;
  }
}

void INTEGER::decode_text(Text_Buf& text_buf)
{
  clean_up();
  bound_flag = TRUE;
  int_val_t tmp(text_buf.pull_int());
  if (likely(tmp.native_flag)) {
    native_flag = TRUE;
    val.native = tmp.get_val();
  } else {
    native_flag = FALSE;
    val.openssl = BN_dup(tmp.get_val_openssl());
  }
}
#endif // #ifndef TITAN_ASN1ENC

void INTEGER::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;}
#ifndef TITAN_ASN1ENC
  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_TEXT: {
    TTCN_EncDec_ErrorContext ec("While TEXT-encoding type '%s': ", p_td.name);
    if(!p_td.text)
      TTCN_EncDec_ErrorContext::error_internal
        ("No TEXT descriptor available for type '%s'.", p_td.name);
    TEXT_encode(p_td,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;}
#endif
  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;}
  case TTCN_EncDec::CT_PER: {
    TTCN_EncDec_ErrorContext ec("While PER-encoding type '%s': ", p_td.name);
    if (!p_td.per) TTCN_EncDec_ErrorContext::error_internal(
      "No PER descriptor available for type '%s'.", p_td.name);
    int opt = va_arg(pvar, int);
    PER_encode(p_td, p_buf, opt);
    break; }
  default:
    TTCN_error("Unknown coding method requested to encode type '%s'",
               p_td.name);
  }
  va_end(pvar);
}

void INTEGER::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;}
#ifndef TITAN_ASN1ENC
  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_TEXT: {
    Limit_Token_List limit;
    TTCN_EncDec_ErrorContext ec("While TEXT-decoding type '%s': ", p_td.name);
    if(!p_td.text)
      TTCN_EncDec_ErrorContext::error_internal
        ("No TEXT descriptor available for type '%s'.", p_td.name);
    const unsigned char *b=p_buf.get_data();
    int null_added=0;
    if(b[p_buf.get_len()-1]!='\0'){
      null_added=1;
      p_buf.set_pos(p_buf.get_len());
      p_buf.put_zero(8,ORDER_LSB);
      p_buf.rewind();
    }
    if(TEXT_decode(p_td,p_buf,limit)<0)
      ec.error(TTCN_EncDec::ET_INCOMPL_MSG,
               "Can not decode type '%s', because invalid or incomplete"
               " message was received"
               , p_td.name);
    if(null_added){
      size_t actpos=p_buf.get_pos();
      p_buf.set_pos(p_buf.get_len()-1);
      p_buf.cut_end();
      p_buf.set_pos(actpos);
    }
    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;}
#endif
  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;}
  case TTCN_EncDec::CT_PER: {
    TTCN_EncDec_ErrorContext ec("While PER-decoding type '%s': ", p_td.name);
    if(!p_td.per)  TTCN_EncDec_ErrorContext::error_internal(
      "No PER descriptor available for type '%s'.", p_td.name);
    int opt = va_arg(pvar, int);
    try {
      PER_decode(p_td, p_buf, opt);
      p_buf.PER_octet_align(FALSE);
    }
    catch (PER_Buffer_Exception) { }
    break; }
  default:
    TTCN_error("Unknown coding method requested to decode type '%s'",
               p_td.name);
  }
  va_end(pvar);
}

ASN_BER_TLV_t *INTEGER::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 (native_flag) {
      new_tlv = BER_encode_TLV_INTEGER(p_coding, val.native);
    } else {
      // Temporary.
      int_val_t *tmp = new int_val_t(BN_dup(val.openssl));
      new_tlv = BER_encode_TLV_INTEGER(p_coding, *tmp);
      delete tmp;
    }
  }
  new_tlv = ASN_BER_V2TLV(new_tlv, p_td, p_coding);
  return new_tlv;
}

boolean INTEGER::BER_decode_TLV(const TTCN_Typedescriptor_t& p_td,
                                const ASN_BER_TLV_t& p_tlv,
                                unsigned L_form)
{
  clean_up();
  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 INTEGER type: ");
  int_val_t tmp;
  boolean ret_val = BER_decode_TLV_INTEGER(stripped_tlv, L_form, tmp);
  if (tmp.is_native()) {
    native_flag = TRUE;
    val.native = tmp.get_val();
  } else {
    native_flag = FALSE;
    val.openssl = BN_dup(tmp.get_val_openssl());
  }
  if (ret_val) bound_flag = TRUE;
  return ret_val;
}

/**
 * Return the number of bits needed to represent an integer value `a'.  The
 * sign bit is added for negative values.  It has a different implementation
 * for `BIGNUM' values.
 *
 * @param a the integer in question
 * @return the number of bits needed to represent the given integer
 * in sign+magnitude
 */
int min_bits(int a)
{
  int bits = 0;
  int tmp = a;
  if (a < 0) {
    bits = 1;
    tmp = -a;
  }
  while (tmp != 0) {
    bits++;
    tmp /= 2;
  }
  return bits;
}

int min_bits(BIGNUM *a)
{
  if (!a) return 0;
  int bits = BN_num_bits(a) + BN_is_negative(a);
  return bits;
}

#ifndef TITAN_ASN1ENC
int INTEGER::TEXT_decode(const TTCN_Typedescriptor_t& p_td,
  TTCN_Buffer& buff, Limit_Token_List& limit, boolean no_err, boolean /*first_call*/)
{
  int decoded_length = 0;
  int str_len = -1;
  if (p_td.text->begin_decode) {
    int tl;
    if ((tl = p_td.text->begin_decode->match_begin(buff)) < 0) {
      if (no_err) return -1;
      TTCN_EncDec_ErrorContext::error(TTCN_EncDec::ET_TOKEN_ERR,
        "The specified token '%s' not found for '%s': ",
        (const char*)*(p_td.text->begin_decode), p_td.name);
      return 0;
    }
    decoded_length += tl;
    buff.increase_pos(tl);
  }
  if (buff.get_read_len() <= 1 && no_err) return -TTCN_EncDec::ET_LEN_ERR;
  if (p_td.text->select_token) {
    int tl;
    if ((tl = p_td.text->select_token->match_begin(buff)) < 0) {
      if (no_err) return -1;
      else tl = 0;
    }
    str_len = tl;
  } else if ( p_td.text->val.parameters
    &&        p_td.text->val.parameters->decoding_params.min_length != -1) {
    str_len = p_td.text->val.parameters->decoding_params.min_length;
  } else if (p_td.text->end_decode) {
    int tl;
    if ((tl = p_td.text->end_decode->match_first(buff)) < 0) {
      if (no_err) return -1;
      else tl = 0;
    }
    str_len = tl;
  } else if (limit.has_token()) {
    str_len = limit.match(buff);
  } 
  if(str_len<0) {
    int tl;
    if ((tl = integer_value_match.match_begin(buff)) < 0) {
      if (no_err) return -1;
      else tl = 0;
    }
    str_len = tl;
  }
  boolean err = FALSE;
  if (str_len > 0) {
    int offs = 0;
    char *atm = (char*)Malloc(str_len + 1); // sizeof(char) == 1 by definition
    const char *b = (const char*)buff.get_read_data();
    memcpy(atm, b, str_len);
    atm[str_len] = 0;
    // 0x2d ('-')
    // 0x20 (' ')
    // 0x30 ('0')
    int neg = *atm == 0x2d ? 1 : 0;
    if (!*(atm + neg)) {
      for (offs = neg; *(atm + offs) == 0x30; offs++) ;  // E.g. 0, -0, 00001234, -00001234.
      if (neg && offs > 1) *(atm + offs - 1) = *atm;  // E.g. -00001234 -> -000-1234.
      offs -= neg;
    } else {
      for(; atm[offs] == 0x20; offs++) ;
    }
    clean_up();
    if (0 == strlen(atm + offs) || 0 == from_string(atm+offs)) {
      err = TRUE;
      native_flag = TRUE;
      val.native = 0;
    }
    Free(atm);
    buff.increase_pos(str_len);
    decoded_length += str_len;
  } else {
    err = TRUE;
  }
  if (err) {
    if (no_err) return -1;
    TTCN_EncDec_ErrorContext::error(TTCN_EncDec::ET_TOKEN_ERR,
      "Can not decode a valid integer for '%s': ", p_td.name);
  }
  if (p_td.text->end_decode) {
    int tl;
    if ((tl = p_td.text->end_decode->match_begin(buff)) < 0) {
      if (no_err) return -1;
      TTCN_EncDec_ErrorContext::error(TTCN_EncDec::ET_TOKEN_ERR,
        "The specified token '%s' not found for '%s': ",
        (const char*)*(p_td.text->end_decode), p_td.name);
      return 0;
    }
    decoded_length += tl;
    buff.increase_pos(tl);
  }
  bound_flag = TRUE;
  return decoded_length;
}

int INTEGER::TEXT_encode(const TTCN_Typedescriptor_t& p_td,
  TTCN_Buffer& buff) const
{
  int encoded_length = 0;
  if (p_td.text->begin_encode) {
    buff.put_cs(*p_td.text->begin_encode);
    encoded_length += p_td.text->begin_encode->lengthof();
  }
  if (!is_bound()) {
    TTCN_EncDec_ErrorContext::error
      (TTCN_EncDec::ET_UNBOUND,"Encoding an unbound value.");
    if (p_td.text->end_encode) {
      buff.put_cs(*p_td.text->end_encode);
      encoded_length+=p_td.text->end_encode->lengthof();
    }
    return encoded_length;
  }
  char *tmp_str;
  // Temporary.
  if (native_flag) tmp_str = mprintf("%d", val.native);
  else tmp_str = BN_bn2dec(val.openssl);
  CHARSTRING ch(tmp_str);
  if (native_flag) Free(tmp_str);
  else OPENSSL_free(tmp_str);
  if (p_td.text->val.parameters == NULL) {
    buff.put_cs(ch);
    encoded_length += ch.lengthof();
  } else {
    TTCN_TEXTdescriptor_values params = p_td.text->val.parameters
      ->coding_params;
    if (params.min_length < 0) {
      buff.put_cs(ch);
      encoded_length += ch.lengthof();
    } else {
      unsigned char *p = NULL;
      int a = 0;
      size_t len = params.min_length + 1;
      buff.get_end(p, len);
      if (params.leading_zero) {
        if (native_flag) {
          a = snprintf((char*)p, len, "%0*d", params.min_length, val.native);
        } else {
          int chlen = ch.lengthof(), pad = 0;
          int neg = native_flag ? (val.native < 0) : BN_is_negative(val.openssl);
          if (params.min_length > chlen)
            pad = params.min_length - chlen + neg;
          // `sprintf' style.
          if (neg) *p = 0x2d;
          memset(p + neg, 0x30, pad);
          for (int i = 0; i < chlen - neg; i++)
            p[i + pad] = ch[i + neg].get_char();
          a += pad + chlen - neg;
        }
      } else {
        a = snprintf((char*)p, len, "%*s", p_td.text->val.parameters->
          coding_params.min_length, (const char*)ch);
      }
      buff.increase_length(a);
      encoded_length += a;
    }
  }
  if (p_td.text->end_encode) {
    buff.put_cs(*p_td.text->end_encode);
    encoded_length += p_td.text->end_encode->lengthof();
  }
  return encoded_length;
}

unsigned char INTX_MASKS[] = { 0 /*dummy*/, 0x01, 0x03, 0x07, 0x0F, 0x1F, 0x3F, 0x7F, 0xFF };

int INTEGER::RAW_encode(const TTCN_Typedescriptor_t& p_td, RAW_enc_tree& myleaf) const
{
  if (!native_flag) return RAW_encode_openssl(p_td, myleaf);
  unsigned char *bc;
  int length; // total length, in bytes
  int val_bits = 0, len_bits = 0; // only for IntX
  int value = val.native;
  boolean neg_sgbit = (value < 0) && (p_td.raw->comp == SG_SG_BIT);
  if (!is_bound()) {
    TTCN_EncDec_ErrorContext::error(TTCN_EncDec::ET_UNBOUND,
      "Encoding an unbound value.");
    value = 0;
    neg_sgbit = FALSE;
  }
  if (value == INT_MIN) {
    INTEGER big_value(to_openssl(val.native)); // too big for native
    return big_value.RAW_encode_openssl(p_td, myleaf);
  }
  if ((value < 0) && (p_td.raw->comp == SG_NO)) {
    TTCN_EncDec_ErrorContext::error(TTCN_EncDec::ET_SIGN_ERR,
      "Unsigned encoding of a negative number: %s", p_td.name);
    value = -value;
  }
  if (neg_sgbit) value = -value;
  //myleaf.ext_bit=EXT_BIT_NO;
  if (myleaf.must_free) Free(myleaf.body.leaf.data_ptr);
  if (p_td.raw->fieldlength == RAW_INTX) { // IntX (variable length)
    val_bits = (p_td.raw->comp != SG_NO); // bits needed to store the value
    int v2 = value;
    if (v2 < 0 && p_td.raw->comp == SG_2COMPL) {
      v2 = ~v2;
    }
    do {
      v2 >>= 1;
      ++val_bits;
    }
    while (v2 != 0);
    len_bits = 1 + val_bits / 8; // bits needed to store the length
    if (val_bits % 8 + len_bits % 8 > 8) {
      // the remainder of the value bits and the length bits do not fit into
      // an octet => an extra octet is needed and the length must be increased
      ++len_bits;
    }
    length = (len_bits + val_bits + 7) / 8;
    if (len_bits % 8 == 0 && val_bits % 8 != 0) {
      // special case: the value can be stored on 8k - 1 octets plus the partial octet
      // - len_bits = 8k is not enough, since there's no partial octet in that case
      // and the length would then be followed by 8k octets (and it only indicates
      // 8k - 1 further octets)
      // - len_bits = 8k + 1 is too much, since there are only 8k - 1 octets
      // following the partial octet (and 8k are indicated)
      // solution: len_bits = 8k + 1 and insert an extra empty octet
      ++len_bits;
      ++length;
    }
  }
  else { // not IntX, use the field length
    length = (p_td.raw->fieldlength + 7) / 8;
    int min_bits_ = min_bits(value);
    if (p_td.raw->comp == SG_SG_BIT) {
      ++min_bits_;
    }
    if (min_bits_ > p_td.raw->fieldlength) {
      TTCN_EncDec_ErrorContext::error(TTCN_EncDec::ET_LEN_ERR,
        "There are insufficient bits to encode '%s' : ", p_td.name);
      value = 0; // substitute with zero
    }
  }
  if (length > RAW_INT_ENC_LENGTH) { // does not fit in the small buffer
    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 (p_td.raw->fieldlength == RAW_INTX) {
    int i = 0;
    // treat the empty space between the value and the length as if it was part
    // of the value, too
    val_bits = length * 8 - len_bits;
    // first, encode the value
    do {
      bc[i] = value & INTX_MASKS[val_bits > 8 ? 8 : val_bits];
      ++i;
      value >>= 8;
      val_bits -= 8;
    }
    while (val_bits > 0);
    if (neg_sgbit) {
      // the sign bit is the first bit after the length
      unsigned char mask = 0x80 >> len_bits % 8;
      bc[i - 1] |= mask;
    }
    // second, encode the length (ignore the last zero)
    --len_bits;
    if (val_bits != 0) {
      // the remainder of the length is in the same octet as the remainder of the
      // value => step back onto it
      --i;
    }
    else {
      // the remainder of the length is in a separate octet
      bc[i] = 0;
    }
    // insert the length's partial octet
    unsigned char mask = 0x80;
    for (int j = 0; j < len_bits % 8; ++j) {
      bc[i] |= mask;
      mask >>= 1;
    }
    if (len_bits % 8 > 0 || val_bits != 0) {
      // there was a partial octet => step onto the first full octet
      ++i;
    }
    // insert the length's full octets
    while (len_bits >= 8) {
      // octets containing only ones in the length
      bc[i] = 0xFF;
      ++i;
      len_bits -= 8;
    }
    myleaf.length = length * 8;
  }
  else {
    for (int a = 0; a < length; a++) {
      bc[a] = value & 0xFF;
      value >>= 8;
    }
    if (neg_sgbit) {
      unsigned char mask = 0x01 << (p_td.raw->fieldlength - 1) % 8;
      bc[length - 1] |= mask;
    }
    myleaf.length = p_td.raw->fieldlength;
  }
  myleaf.coding_par.csn1lh = p_td.raw->csn1lh;
  return myleaf.length;
}

int INTEGER::RAW_encode_openssl(const TTCN_Typedescriptor_t& p_td,
  RAW_enc_tree& myleaf) const
{
  unsigned char *bc = NULL;
  int length; // total length, in bytes
  int val_bits = 0, len_bits = 0; // only for IntX
  BIGNUM *D = BN_new();
  BN_copy(D, val.openssl);
  boolean neg_sgbit = (BN_is_negative(D)) && (p_td.raw->comp == SG_SG_BIT);
  if (!is_bound()) {
    TTCN_EncDec_ErrorContext::error(TTCN_EncDec::ET_UNBOUND,
      "Encoding an unbound value.");
    BN_clear(D);
    neg_sgbit = FALSE;
  }
  if ((BN_is_negative(D)) && (p_td.raw->comp == SG_NO)) {
    TTCN_EncDec_ErrorContext::error(TTCN_EncDec::ET_SIGN_ERR,
      "Unsigned encoding of a negative number: %s", p_td.name);
    BN_set_negative(D, 0);
    neg_sgbit = FALSE;
  }
  // `if (neg_sgbit) tmp->neg = tmp->neg == 0;' is not needed, because the
  // sign is stored separately from the number.  Default encoding of negative
  // values in 2's complement form.
  if (myleaf.must_free) Free(myleaf.body.leaf.data_ptr);
  if (p_td.raw->fieldlength == RAW_INTX) {
    val_bits = BN_num_bits(D) + (p_td.raw->comp != SG_NO); // bits needed to store the value
    len_bits = 1 + val_bits / 8; // bits needed to store the length
    if (val_bits % 8 + len_bits % 8 > 8) {
      // the remainder of the value bits and the length bits do not fit into
      // an octet => an extra octet is needed and the length must be increased
      ++len_bits;
    }
    length = (len_bits + val_bits + 7) / 8;
    if (len_bits % 8 == 0 && val_bits % 8 != 0) {
      // special case: the value can be stored on 8k - 1 octets plus the partial octet
      // - len_bits = 8k is not enough, since there's no partial octet in that case
      // and the length would then be followed by 8k octets (and it only indicates
      // 8k - 1 further octets)
      // - len_bits = 8k + 1 is too much, since there are only 8k - 1 octets
      // following the partial octet (and 8k are indicated)
      // solution: len_bits = 8k + 1 and insert an extra empty octet
      ++len_bits;
      ++length;
    }
  }
  else {
    length = (p_td.raw->fieldlength + 7) / 8;
    if (min_bits(D) > p_td.raw->fieldlength) {
      TTCN_EncDec_ErrorContext::error(TTCN_EncDec::ET_LEN_ERR,
        "There are insufficient bits to encode '%s': ", p_td.name);
      // `tmp = -((-tmp) & BitMaskTable[min_bits(tmp)]);' doesn't make any sense
      // at all for negative values.  Just simply clear the value.
      BN_clear(D);
      neg_sgbit = FALSE;
    }
  }
  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;
  }
  boolean twos_compl = (BN_is_negative(D)) && !neg_sgbit;
  // Conversion to 2's complement.
  if (twos_compl) {
    BN_set_negative(D, 0);
    int num_bytes = BN_num_bytes(D);
    unsigned char* tmp = (unsigned char*)Malloc(num_bytes * sizeof(unsigned char));
    BN_bn2bin(D, tmp);
    for (int a = 0; a < num_bytes; a++) tmp[a] = ~tmp[a];
    BN_bin2bn(tmp, num_bytes, D);
    BN_add_word(D, 1);
    Free(tmp);
  }
  if (p_td.raw->fieldlength == RAW_INTX) {
    int i = 0;
    // treat the empty space between the value and the length as if it was part
    // of the value, too
    val_bits = length * 8 - len_bits;
    // first, encode the value
    unsigned num_bytes = BN_num_bytes(D);
    unsigned char* tmp = (unsigned char*)Malloc(num_bytes * sizeof(unsigned char));
    BN_bn2bin(D, tmp);
    do {
      bc[i] = (num_bytes-i > 0 ? tmp[num_bytes - (i + 1)] : (twos_compl ? 0xFF : 0)) & INTX_MASKS[val_bits > 8 ? 8 : val_bits];
      ++i;
      val_bits -= 8;
    }
    while (val_bits > 0);
    Free(tmp);
    BN_free(D);
    if (neg_sgbit) {
      // the sign bit is the first bit after the length
      unsigned char mask = 0x80 >> len_bits % 8;
      bc[i - 1] |= mask;
    }
    // second, encode the length (ignore the last zero)
    --len_bits;
    if (val_bits != 0) {
      // the remainder of the length is in the same octet as the remainder of the
      // value => step back onto it
      --i;
    }
    else {
      // the remainder of the length is in a separate octet
      bc[i] = 0;
    }
    // insert the length's partial octet
    unsigned char mask = 0x80;
    for (int j = 0; j < len_bits % 8; ++j) {
      bc[i] |= mask;
      mask >>= 1;
    }
    if (len_bits % 8 > 0 || val_bits != 0) {
      // there was a partial octet => step onto the first full octet
      ++i;
    }
    // insert the length's full octets
    while (len_bits >= 8) {
      // octets containing only ones in the length
      bc[i] = 0xFF;
      ++i;
      len_bits -= 8;
    }
    myleaf.length = length * 8;
  }
  else {
    int num_bytes = BN_num_bytes(D);
    unsigned char* tmp = (unsigned char*)Malloc(num_bytes * sizeof(unsigned char));
    BN_bn2bin(D, tmp);
    for (int a = 0; a < length; a++) {
      if (twos_compl && num_bytes - 1 < a) bc[a] = 0xff;
      else bc[a] = (num_bytes - a > 0 ? tmp[num_bytes - (a + 1)] : 0) & 0xff;
    }
    if (neg_sgbit) {
      unsigned char mask = 0x01 << (p_td.raw->fieldlength - 1) % 8;
      bc[length - 1] |= mask;
    }
    Free(tmp);
    BN_free(D);
    myleaf.length = p_td.raw->fieldlength;
  }
  myleaf.coding_par.csn1lh = p_td.raw->csn1lh;
  return myleaf.length;
}

int INTEGER::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*/)
{
  bound_flag = FALSE;
  int prepaddlength = buff.increase_pos_padd(p_td.raw->prepadding);
  limit -= prepaddlength;
  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;
  int decode_length = 0;
  int len_bits = 0; // only for IntX (amount of bits used to store the length)
  unsigned char len_data = 0; // only for IntX (an octet used to store the length)
  int partial_octet_bits = 0; // only for IntX (amount of value bits in the partial octet)
  if (p_td.raw->fieldlength == RAW_INTX) {
    // extract the length
    do {
      // check if at least 8 bits are available in the buffer
      if (8 > limit) {
        if (!no_err) {
          TTCN_EncDec_ErrorContext::error(TTCN_EncDec::ET_LEN_ERR,
            "There are not enough bits in the buffer to decode the length of IntX "
            "type %s (needed: %d, found: %d).", p_td.name, len_bits + 8,
            len_bits + limit);
        }
        return -TTCN_EncDec::ET_LEN_ERR; 
      }
      else {
        limit -= 8;
      }
      int nof_unread_bits = buff.unread_len_bit();
      if (nof_unread_bits < 8) {
        if (!no_err) {
          TTCN_EncDec_ErrorContext::error(TTCN_EncDec::ET_INCOMPL_MSG,
            "There are not enough bits in the buffer to decode the length of IntX "
            "type %s (needed: %d, found: %d).", p_td.name, len_bits + 8,
            len_bits + nof_unread_bits);
        }
        return -TTCN_EncDec::ET_INCOMPL_MSG;
      }
      
      // extract the next length octet (or partial length octet)
      buff.get_b(8, &len_data, cp, top_bit_ord);
      unsigned char mask = 0x80;
      do {
        ++len_bits;
        if (len_data & mask) {
          mask >>= 1;
        }
        else {
          // the first zero signals the end of the length
          // the rest of the bits in the octet are part of the value
          partial_octet_bits = (8 - len_bits % 8) % 8;
          
          // decode_length only stores the amount of bits in full octets needed
          // by the value, the bits in the partial octet are stored by len_data
          decode_length = 8 * (len_bits - 1);
          break;
        }
      }
      while (len_bits % 8 != 0);
    }
    while (decode_length == 0 && partial_octet_bits == 0);
  }
  else {
    // not IntX, use the static field length
    decode_length = p_td.raw->fieldlength;
  }
  if (decode_length > limit) {
    if (!no_err) {
      TTCN_EncDec_ErrorContext::error(TTCN_EncDec::ET_LEN_ERR,
        "There are not enough bits in the buffer to decode%s type %s (needed: %d, "
        "found: %d).", p_td.raw->fieldlength == RAW_INTX ? " the value of IntX" : "",
        p_td.name, decode_length, limit);
    }
    if (no_err || p_td.raw->fieldlength == RAW_INTX) {
      return -TTCN_EncDec::ET_LEN_ERR;
    }
    decode_length = limit;
  }
  int nof_unread_bits = buff.unread_len_bit();
  if (decode_length > nof_unread_bits) {
    if (!no_err) {
      TTCN_EncDec_ErrorContext::error(TTCN_EncDec::ET_INCOMPL_MSG,
        "There are not enough bits in the buffer to decode%s type %s (needed: %d, "
        "found: %d).", p_td.raw->fieldlength == RAW_INTX ? " the value of IntX" : "",
        p_td.name, decode_length, nof_unread_bits);
    }
    if (no_err || p_td.raw->fieldlength == RAW_INTX) {
      return -TTCN_EncDec::ET_INCOMPL_MSG;
    }
    decode_length = nof_unread_bits;
  }
  clean_up();
  if (decode_length < 0) return -1;
  else if (decode_length == 0 && partial_octet_bits == 0) {
    native_flag = TRUE;
    val.native = 0;
  }
  else {
    int tmp = 0;
    int twos_compl = 0;
    unsigned char *data = (unsigned char *) Malloc(
      (decode_length + partial_octet_bits + 7) / 8);
    buff.get_b((size_t) decode_length, data, cp, top_bit_ord);
    if (partial_octet_bits != 0) {
      // in case there are value bits in the last length octet (only for IntX),
      // these need to be appended to the extracted data
      data[decode_length / 8] = len_data;
      decode_length += partial_octet_bits;
    }
    int end_pos = decode_length;
    int idx = (end_pos - 1) / 8;
    boolean negativ_num = FALSE;
    switch (p_td.raw->comp) {
    case SG_2COMPL:
      if (data[idx] >> ((end_pos - 1) % 8) & 0x01) {
        tmp = -1;
        twos_compl = 1;
      }
      break;
    case SG_NO:
      break;
    case SG_SG_BIT:
      negativ_num = (data[idx] >> ((end_pos - 1) % 8)) & 0x01;
      end_pos--;
      break;
    default:
      break;
    }
    if (end_pos < 9) {
      tmp <<= end_pos;
      tmp |= data[0] & BitMaskTable[end_pos];
    }
    else {
      idx = (end_pos - 1) / 8;
      tmp <<= (end_pos - 1) % 8 + 1;
      tmp |= data[idx--] & BitMaskTable[(end_pos - 1) % 8 + 1];
      if (decode_length > (RInt) sizeof(RInt) * 8 - 1) {
        BIGNUM *D = BN_new();
        BN_set_word(D, tmp);
        int pad = tmp == 0 ? 1 : 0;
        for (; idx >= 0; idx--) {
          if (pad && data[idx] != 0) {
            BN_set_word(D, data[idx] & 0xff);
            pad = 0;
            continue;
          }
          if (pad) continue;
          BN_lshift(D, D, 8);
          BN_add_word(D, data[idx] & 0xff);
        }
        if (twos_compl) {
          BIGNUM *D_tmp = BN_new();
          BN_set_bit(D_tmp, BN_num_bits(D));
          BN_sub(D, D, D_tmp);
          BN_free(D_tmp);
        }
        else if (negativ_num) {
          BN_set_negative(D, 1);
        }
        // Back to native.  "BN_num_bits(D) + BN_is_negative(D) >
        // (RInt)sizeof(RInt) * 8 - !BN_is_negative(D)" was an over-complicated
        // condition.
        if (BN_num_bits(D) > (RInt) sizeof(RInt) * 8 - 1) {
          native_flag = FALSE;
          val.openssl = D;
        }
        else {
          native_flag = TRUE;
          val.native = BN_is_negative(D) ? -BN_get_word(D) : BN_get_word(D);
          BN_free(D);
        }
        Free(data);
        goto end;
      }
      else {
        for (; idx >= 0; idx--) {
          tmp <<= 8;
          tmp |= data[idx] & 0xff;
        }
      }
    }
    Free(data);
    native_flag = TRUE;
    val.native = negativ_num ? (RInt) -tmp : (RInt) tmp;
  }
  end: decode_length += buff.increase_pos_padd(p_td.raw->padding);
  bound_flag = TRUE;
  return decode_length + prepaddlength + len_bits;
}

int INTEGER::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 integer value.");
  }
  int encoded_length = (int) p_buf.get_len();

  flavor |= SIMPLE_TYPE;
  flavor &= ~XER_RECOF; // integer doesn't care
  if (begin_xml(p_td, p_buf, flavor, indent, FALSE) == -1) --encoded_length;

  char *tmp_str;
  if (native_flag)
    tmp_str = mprintf("%d", val.native);
  else
    tmp_str = BN_bn2dec(val.openssl);
  CHARSTRING value(tmp_str);
  if (native_flag)
    Free(tmp_str);
  else
    OPENSSL_free(tmp_str);
  p_buf.put_string(value);

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

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

int INTEGER::XER_decode(const XERdescriptor_t& p_td, XmlReaderWrap& reader,
  unsigned int flavor, unsigned int /*flavor2*/, embed_values_dec_struct_t*)
{
  const boolean exer = is_exer(flavor);
  const char * value = 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:
    value = (const char *)reader.Value();

    for (; *value && isspace(*value); ++value) {}
    from_string(value);
    if (get_nof_digits() != (int)strlen(value) - (value[0] == '-') ? 1 : 0) {
      clean_up();
    }
    // Let the caller do reader.AdvanceAttribute();
  }
  else {
    int depth = -1, success = reader.Ok(), type;
    for (; success == 1; success = reader.Read()) {
      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 INTEGER*> (p_td.dfeValue);
          }
          reader.Read();
          break;
        }
        depth = reader.Depth();
      }
      else if (XML_READER_TYPE_TEXT == type && depth != -1) {
        value = (const char*) reader.Value();
        for (; *value && isspace(*value); ++value) {}
        from_string(value);
        if (get_nof_digits() != (int)strlen(value) - (value[0] == '-') ? 1 : 0) {
          clean_up();
        }
      }
      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 INTEGER*> (p_td.dfeValue);
        }
        reader.Read();
        break;
      }
    } // next read
  } // if not attribute

  return 1;
}
#endif

int INTEGER::get_nof_digits()
{
  int digits = 0;
  if (native_flag) {
    RInt x = val.native;
    if (x == 0) return 1;
    if (x < 0) x = -x;
    while (x > 0) {
      ++digits;
      x /= 10;
    }
  } else {
    BIGNUM *x = BN_new();
    BN_copy(x, val.openssl);
    if (BN_is_zero(x)) return 1;
    BN_set_negative(x, 1);
    while (!BN_is_zero(x)) {
      ++digits;
      BN_div_word(x, 10);
    }
    BN_free(x);
  }
  return digits;
}

int INTEGER::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 integer value.");
    return -1;
  }
  char* tmp_str = 0;
  if (native_flag) {
    tmp_str = mprintf("%d", val.native);
  } else {
    tmp_str = BN_bn2dec(val.openssl);
  }
  
  int enc_len = p_tok.put_next_token(JSON_TOKEN_NUMBER, tmp_str);
  
  if (native_flag) {
    Free(tmp_str);
  } else {
    OPENSSL_free(tmp_str);
  }
  
  return enc_len;
}

int INTEGER::JSON_decode(const TTCN_Typedescriptor_t& p_td, JSON_Tokenizer& p_tok, boolean p_silent, boolean, int)
{
  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 INTEGER*>(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_NUMBER == token || use_default) {
    char* number = mcopystrn(value, value_len);
    clean_up();
    if (from_string(number) && (int)value_len == get_nof_digits() + ('-' == value[0] ? 1 : 0)) {
      bound_flag = TRUE;
    } else {
      JSON_ERROR(TTCN_EncDec::ET_INVAL_MSG, JSON_DEC_FORMAT_ERROR, "number", "integer");
      bound_flag = FALSE;
      dec_len = JSON_ERROR_FATAL;
    }
    Free(number);
  } else {
    bound_flag = FALSE;
    return JSON_ERROR_INVALID_TOKEN;
  }
  return (int)dec_len;
}


int INTEGER::OER_encode(const TTCN_Typedescriptor_t& p_td, TTCN_Buffer& p_buf) const
{
  if (!is_bound()) {
    TTCN_EncDec_ErrorContext::error(TTCN_EncDec::ET_UNBOUND,
      "Encoding an unbound integer value.");
    return -1;
  }
  
  // Most of the encoding is copied from the integer BER encoding.
  if (native_flag) {
    RInt value = val.native;
    // Determine the number of octets to be used in the encoding.

    unsigned long ulong_val = value >= 0
      ? static_cast<unsigned long>(value):
      ~ static_cast<unsigned long>(value);
    
    size_t len = 1;
    // No length restriction on integer
    if (p_td.oer->bytes == -1) {
      if (p_td.oer->signed_) {
        ulong_val >>= 7;
      } else {
        ulong_val >>= 8;
      }
      while (ulong_val != 0) {
        len++;
        ulong_val >>= 8;
      }
      if (len < 128) {
        p_buf.put_c(len);
      } else {
        // Impossible
        TTCN_EncDec_ErrorContext::error(TTCN_EncDec::ET_UNDEF,
          "Encoding very big native integer");
      }
    } else {
      len = p_td.oer->bytes;
    }
    
    // Already in 2's complement encoding.
    ulong_val=static_cast<unsigned long>(value);
    p_buf.increase_length(len);
    size_t size = p_buf.get_len();
    unsigned char* uc = const_cast<unsigned char*>(p_buf.get_data()) + size - 1;
    for (size_t i = 0; i < len; i++) {
      unsigned char* u = uc - i;
      *u = static_cast<unsigned char>(ulong_val);
      ulong_val >>= 8;
    }
  } else {
    const BIGNUM* const D = val.openssl;
    size_t num_bytes = BN_num_bytes(D);
    unsigned char* bn_as_bin = (unsigned char*) Malloc(num_bytes);
    BN_bn2bin(D, bn_as_bin);

    boolean pad = FALSE;
    if (BN_is_negative(D)) {
      for(size_t i = 0; i < num_bytes; ++i){
        bn_as_bin[i] = ~bn_as_bin[i];
      }

      // add one
      boolean stop = FALSE;
      for (int i = num_bytes - 1; i >= 0 && !stop; --i) {
        for (int j = 0; j < 8 && !stop; ++j) {
          unsigned char mask = (0x1 << j);
          if (!(bn_as_bin[i] & mask)) {
            bn_as_bin[i] |= mask;
            stop = TRUE;
          } else {
            bn_as_bin[i] ^= mask;
          }
        }
      }

      pad = p_td.oer->signed_ && (!(bn_as_bin[0] & 0x80));
    } else {
      pad = p_td.oer->signed_ && (bn_as_bin[0] & 0x80);
    }
    
    if (p_td.oer->bytes == -1) {
      if (pad)   {
          char pad_ch = BN_is_negative(D) ? 0xFF : 0;
          encode_oer_length(num_bytes + 1, p_buf, FALSE);
          p_buf.put_c(pad_ch);
      } else  {
          encode_oer_length(num_bytes, p_buf, FALSE);
      }
    } else {
      int rem_bytes = p_td.oer->bytes - num_bytes;
      char pad_ch = BN_is_negative(D) ? 0xFF : 0;
      for (int i = 0; i < rem_bytes; i++) {
        p_buf.put_c(pad_ch);
      }
    }
    p_buf.put_s(num_bytes, bn_as_bin);
    Free(bn_as_bin);
  }
  return 0;
}

int INTEGER::OER_decode(const TTCN_Typedescriptor_t& p_td, TTCN_Buffer& p_buf, OER_struct&)
{
  // Most of the decoding is copied from the integer BER decoding.
  size_t num_bytes = 0;
  
  // Get the number of bytes that the integer is encoded on
  if (p_td.oer->bytes == -1) {
    num_bytes = decode_oer_length(p_buf, FALSE);
  } else {
    num_bytes = p_td.oer->bytes;
  }
  const unsigned char* const ucstr = p_buf.get_read_data();
  if ((num_bytes > sizeof(RInt)) || (num_bytes >= 4 && p_td.oer->signed_ == FALSE)) { // Bignum
    const boolean negative = ucstr[0] & 0x80 && p_td.oer->signed_ == TRUE;
    BIGNUM *D = BN_new();
    if (negative) {
      unsigned char* const tmp = (unsigned char*) Malloc(num_bytes);
      memcpy(tmp, ucstr, num_bytes);
      // -1 
      boolean stop = FALSE;
      for (int i = num_bytes - 1; i >= 0 && !stop; --i) {
        for(int j = 0; j < 8 && !stop; ++j) {
          unsigned char mask = (0x1 << j);
          if (tmp[i] & mask) {
            tmp[i] ^= mask;
            stop = TRUE;
          } else {
            tmp[i] |= mask;
          }
        }
      }
      for (size_t i = 0; i < num_bytes; ++i) {
        tmp[i] = ~tmp[i];
      }

      BN_bin2bn(tmp, num_bytes, D);
      Free(tmp);
    } else { // positive case
      BN_bin2bn(ucstr, num_bytes, D);
    }

    BN_set_negative(D, negative);
    val.openssl = D;
    native_flag = FALSE;
    bound_flag = TRUE;
  } else {
    // Native integer
    RInt int_val = 0; 
    if (ucstr[0] & 0x80 && p_td.oer->signed_ == TRUE) { // negative 
      for (size_t i = 0; i < sizeof(RInt) - num_bytes; ++i) {
        int_val |= 0xFF;
        int_val <<= 8;
      }
    }

    int_val |= ucstr[0];
    for (size_t i = 1; i < num_bytes; ++i) {
      int_val <<= 8;
      int_val |= ucstr[i];
    }

    val.native = int_val;
    native_flag = TRUE;
    bound_flag = TRUE;
  }
  p_buf.increase_pos(num_bytes);
  return 0;
}

int INTEGER::PER_min_bits(boolean is_range, boolean is_signed) const
{
  if (*this == 0) {
    // need at least one bit to encode a '0' in PER
    return 1;
  }
  if (is_range) {
    // we are actually looking for the amount of bits needed to encode all numbers
    // from 0 to *this - 1, which is equal to the bits needed to encode the largest number (*this - 1)
    return (*this - 1).PER_min_bits(FALSE, is_signed);
  }
  int ret_val = native_flag ? min_bits(val.native) : min_bits(val.openssl);
  if (is_signed && *this > 0) {
    // signed positive numbers can't start with '1', they need at least one leading '0'
    ++ret_val;
  }
  return ret_val;
}

void INTEGER::PER_encode_int(TTCN_Buffer& p_buf, int p_value, int p_bits_used)
{
  int bytes_used = (p_bits_used + 7) / 8;
  int shift_count = 0;
  unsigned char* raw_data = new unsigned char[bytes_used];
  for (int i = bytes_used - 1; i >= 0; --i) {
    // the data buffer is filled up in reverse order, because the least significant octet of the 'int' is extracted first
    if (i == bytes_used - 1 && (p_bits_used % 8 != 0)) {
      shift_count = p_bits_used % 8;
      raw_data[i] = (p_value & BackBitMask[shift_count]) << (8 - shift_count);
    }
    else {
      raw_data[i] = p_value & 0xFF;
      shift_count = 8;
    }
    p_value >>= shift_count;
  }
  p_buf.PER_put_bits(p_bits_used, raw_data);
  delete[] raw_data;
}

void INTEGER::PER_encode_bignum(TTCN_Buffer& p_buf, BIGNUM* p_value, int p_bits_used)
{
  int bytes_used = (p_bits_used + 7) / 8;
  // bits needed to encode the value
  int bits_needed = min_bits(p_value);
  int bytes_needed = (bits_needed + 7) / 8;
  unsigned char* raw_data = new unsigned char[bytes_used];
  unsigned char* bn_data = new unsigned char[bytes_needed];
  boolean negative = BN_is_negative(p_value);
  if (negative) {
    BN_add_word(p_value, 1);
  }
  BN_bn2bin(p_value, bn_data);
  // 'BN_bn2bin' encodes the number into the minimum number of bytes needed, with the LSB being the last bit of the last octet;
  // these bits need to be shifted to the right or left
  for (int i = 1; i <= bytes_needed; ++i) {
    int first_half_bits = p_bits_used % 8;
    if (first_half_bits == 0) {
      first_half_bits = 8;
    }
    int second_half_bits = 8 - first_half_bits;
    raw_data[bytes_used - i] = (bn_data[bytes_needed - i] & BackBitMask[first_half_bits]) << second_half_bits;
    if (i != 1) {
      raw_data[bytes_used - i] |= (bn_data[bytes_needed - i + 1] & FrontBitMask[second_half_bits]) >> first_half_bits;
    }
  }
  for (int i = 0; i < bytes_used - bytes_needed; ++i) {
    if (i == bytes_used - bytes_needed - 1 && ((p_bits_used - bits_needed) % 8 != 0)) {
      int zero_bits = (p_bits_used - bits_needed) % 8;
      int useful_bits = 8 - zero_bits;
      int useful_bits_start = bytes_needed * 8 - bits_needed;
      if (useful_bits + useful_bits_start <= 8) {
        raw_data[i] = (bn_data[0] & MiddleBitMask[useful_bits_start][useful_bits]) >> (zero_bits - useful_bits_start);
      }
      else { // need bits from the 2nd octet of the number's encoding, too
        int useful_bits_1 = (useful_bits + useful_bits_start) % 8;
        int useful_bits_0 = useful_bits - useful_bits_1;
        raw_data[i] = (bn_data[0] & BackBitMask[useful_bits_0]) << useful_bits_1;
        raw_data[i] |= (bn_data[1] & FrontBitMask[useful_bits_1]) >> (8 - useful_bits_1);
      }
    }
    else {
      raw_data[i] = 0;
    }
  }
  delete[] bn_data;
  if (negative) {
    for (int i = 0; i < bytes_used; ++i) {
      raw_data[i] = ~raw_data[i];
    }
  }
  p_buf.PER_put_bits(p_bits_used, raw_data);
  delete[] raw_data;
}

void INTEGER::PER_encode_unaligned_constrained(TTCN_Buffer& p_buf, int p_bits_used) const
{
  if (native_flag) {
    PER_encode_int(p_buf, val.native, p_bits_used);
  }
  else {
    PER_encode_bignum(p_buf, val.openssl, p_bits_used);
  }
}

void INTEGER::PER_encode_aligned_constrained(TTCN_Buffer& p_buf, const INTEGER& p_range) const
{
  // only called if p_range < 64k
  if (p_range < PER_OCTET) { // bit-field case
    PER_encode_unaligned_constrained(p_buf, p_range.PER_min_bits(TRUE, FALSE));
  }
  else if (p_range == PER_OCTET) { // one-octet case
    p_buf.PER_octet_align(TRUE);
    PER_encode_unaligned_constrained(p_buf, 8);
  }
  else { // two-octet case
    p_buf.PER_octet_align(TRUE);
    PER_encode_unaligned_constrained(p_buf, 16);
  }
}

int INTEGER::PER_encode_length(TTCN_Buffer& p_buf, int p_options, const INTEGER& p_range,
                               const INTEGER& p_lb, const INTEGER& p_ub, boolean p_normally_small) const
{
  if (p_normally_small) {
    if (*this < 64) {
      p_buf.PER_put_bit(FALSE); // 0
      PER_encode_int(p_buf, val.native - 1, 6);
      return 0;
    }
    else {
      p_buf.PER_put_bit(TRUE); // 1
      // continue on as if it was an unconstrained length
    }
  }
  else {
    if (p_range == 1 && *this < PER_64K) {
      return 0; // single value, nothing to do
    }
    if (p_range > 0 && p_ub < PER_64K) {
      // encode as a constrained whole number
      INTEGER enc_val = *this - p_lb;
      if (p_options & PER_ALIGNED) {
        enc_val.PER_encode_aligned_constrained(p_buf, p_range);
        return 0;
      }
      else {
        enc_val.PER_encode_unaligned_constrained(p_buf, p_range.PER_min_bits(TRUE, FALSE));
        return 0;
      }
    }
  }
  // otherwise the length is treated as unconstrained
  if (p_options & PER_ALIGNED) {
    p_buf.PER_octet_align(TRUE);
  }
  if (*this < 128) {
    p_buf.PER_put_bit(FALSE); // 0
    PER_encode_int(p_buf, val.native, 7);
    return 0;
  }
  else if (*this < PER_16K) {
    unsigned char one_zero = 0x80; // 10
    p_buf.PER_put_bits(2, &one_zero);
    PER_encode_int(p_buf, val.native, 14);
    return 0;
  }
  else { // >16K
    unsigned char one_one = 0xC0; // 11
    p_buf.PER_put_bits(2, &one_one);
    // in this case only a fraction of the length is encoded now, as a mulitple of 16K,
    // followed by that many elements, and the rest of the length/data will be encoded later
    int mul_16k = (*this < PER_64K) ? (val.native / PER_16K) : 4;
    PER_encode_int(p_buf, mul_16k, 6);
    return mul_16k;
  }
}

int INTEGER::PER_encode_length(TTCN_Buffer& p_buf, int p_options, boolean p_normally_small) const
{
  INTEGER zero(0);
  return PER_encode_length(p_buf, p_options, zero, zero, zero, p_normally_small);
}

void INTEGER::PER_encode_unconstrained(TTCN_Buffer& p_buf, int p_options,
                                       const INTEGER& p_range, boolean p_semi_cons) const
{
  // todo: use INTEGERs instead of ints, in case someone encodes a number that doesn't fit in 2 billion bits ...
  int bits_needed = PER_min_bits(FALSE, !p_semi_cons);
  INTEGER length = (bits_needed + 7) / 8;
  int length_lb = p_range > 0 ? 1 : 0;
  int length_ub = p_range > 0 ? ((p_range.PER_min_bits(TRUE, FALSE) + 7) / 8) : -1;
  int length_range = p_range > 0 ? (length_ub - length_lb + 1) : 0;

  int mul_16k = length.PER_encode_length(p_buf, p_options, length_range, length_lb, length_ub);
  if (mul_16k == 0) {
    if (p_range > 0) {
      p_buf.PER_octet_align(TRUE);
    }
    PER_encode_unaligned_constrained(p_buf, length * 8);
  }
  else {
    // split the encoding up into parts
    // the length of each part is a multiple of 16K (up to 64K), and the final part is the remainder
    TTCN_Buffer tmp_buf;
    PER_encode_unaligned_constrained(tmp_buf, length * 8);
    int partial_len = mul_16k * PER_16K;
    p_buf.PER_put_bits(partial_len * 8, tmp_buf.get_data());
    int buf_pos = partial_len;
    length = length - partial_len;
    length_range = 0; // make sure future partial lengths are not encoded as constrained integers
    while (mul_16k > 0) {
      mul_16k = length.PER_encode_length(p_buf, p_options, length_range, length_lb, length_ub);
      if (length > 0) { // the length of the final part might be zero, in which case nothing more is added
        partial_len = mul_16k > 0 ? mul_16k * PER_16K : (int)length;
        p_buf.PER_put_bits(partial_len * 8, tmp_buf.get_data() + buf_pos);
        buf_pos += partial_len;
        length = length - partial_len;
      }
    }
  }
}

void INTEGER::PER_encode_normally_small(TTCN_Buffer& p_buf, int p_options) const
{
  if (*this < 64) {
    p_buf.PER_put_bit(FALSE);
    PER_encode_int(p_buf, val.native, 6);
  }
  else {
    p_buf.PER_put_bit(TRUE);
    PER_encode_unconstrained(p_buf, p_options, INTEGER(-1), TRUE);
  }
}

void INTEGER::PER_encode(const TTCN_Typedescriptor_t& p_td, TTCN_Buffer& p_buf, int p_options) const
{
  if (!is_bound()) {
    TTCN_EncDec_ErrorContext::error(TTCN_EncDec::ET_UNBOUND,
      "Encoding an unbound integer value.");
    return;
  }
  const Per_Integer_Constraint* cons = dynamic_cast<const Per_Integer_Constraint*>(p_td.per->c);
  if (cons == NULL) {
    TTCN_EncDec_ErrorContext::error(TTCN_EncDec::ET_INTERNAL,
      "Internal error: Invalid constraint in PER descriptor.");
    return;
  }
  boolean extendable = cons->has_extension_marker();
  boolean within_ext_root = cons->is_within_extension_root(*this);
  if (!within_ext_root && !extendable) {
    TTCN_EncDec_ErrorContext::error(TTCN_EncDec::ET_CONSTRAINT,
      "Encoding an invalid integer value (does not match PER-visible constraints).");
    return;
  }
  boolean ext_bit = extendable && !within_ext_root;
  if (extendable) {
    p_buf.PER_put_bit(ext_bit);
  }
  INTEGER range = ext_bit ? INTEGER(0) : cons->get_nof_values();
  // bits needed to encode all possible values
  int range_bits = range.PER_min_bits(TRUE, FALSE);
  if (range == 1) {
    return; // single value, nothing to do
  }
  if (range > 0) { // constrained integer
    // we are actually encoding this number minus the lower bound
    INTEGER enc_val = *this - cons->get_lower_bound();
    if (p_options & PER_ALIGNED) {
      if (range <= PER_64K) {
        enc_val.PER_encode_aligned_constrained(p_buf, range.val.native);
        return;
      }
      // otherwise, (the indefinite length case) use a length determinant (below)
    }
    else { // unaligned
      enc_val.PER_encode_unaligned_constrained(p_buf, range_bits);
      return;
    }
  }

  // semi-constrained or unconstrained integer, or constrained & aligned with range > 64k
  INTEGER enc_val = (!ext_bit && cons->has_lower_bound()) ? (*this - cons->get_lower_bound()) : *this;
  enc_val.PER_encode_unconstrained(p_buf, p_options, range, cons->has_lower_bound());
}

void INTEGER::PER_decode_int(TTCN_Buffer& p_buf, int p_bits_used, boolean p_signed, int& p_value)
{
  if (p_bits_used == 0) {
    p_value = 0;
    return;
  }
  int bytes_used = (p_bits_used + 7) / 8;
  Smart_Buffer per_buf(bytes_used);
  p_buf.PER_get_bits(p_bits_used, per_buf.data);
  p_value = (p_signed && (per_buf.data[0] & 0x80) != 0) ? -1 : 0;
  for (int i = 0; i < bytes_used; ++i) {
    if (i == bytes_used - 1 && (p_bits_used % 8 != 0)) {
      int shift_count = p_bits_used % 8;
      p_value <<= shift_count;
      p_value |= per_buf.data[i] >> (8 - shift_count);
    }
    else {
      p_value <<= 8;
      p_value |= per_buf.data[i];
    }
  }
}

void INTEGER::PER_decode_bignum(TTCN_Buffer& p_buf, int p_bits_used, boolean p_signed, BIGNUM*& p_value)
{
  int bytes_used = (p_bits_used + 7) / 8;
  Smart_Buffer per_buf(bytes_used);
  p_buf.PER_get_bits(p_bits_used, per_buf.data);
  boolean negative = p_signed && (per_buf.data[0] & 0x80) != 0;
  if (negative) {
    for (int i = 0; i < bytes_used; ++i) {
      per_buf.data[i] = ~per_buf.data[i];
    }
  }
  p_value = BN_new();
  BN_bin2bn(per_buf.data, bytes_used, p_value);
  if (p_bits_used % 8 != 0) {
    BN_rshift(p_value, p_value, 8 - (p_bits_used % 8));
  }
  if (negative) {
    BN_add_word(p_value, 1);
    BN_set_negative(p_value, 1);
  }
}

void INTEGER::PER_decode_unaligned_constrained(TTCN_Buffer& p_buf, int p_bits_used, boolean p_signed)
{
  int native_bits = static_cast<int>(sizeof(RInt) * 8);
  if (p_bits_used < (p_signed ? native_bits - 1 : native_bits)) {
    native_flag = TRUE;
    PER_decode_int(p_buf, p_bits_used, p_signed, val.native);
  }
  else {
    native_flag = FALSE;
    PER_decode_bignum(p_buf, p_bits_used, p_signed, val.openssl);
  }
  bound_flag = TRUE;
}

void INTEGER::PER_decode_aligned_constrained(TTCN_Buffer& p_buf, const INTEGER& p_range, boolean p_signed)
{
  // only called if p_range < 64k
  if (p_range < PER_OCTET) { // bit-field case
    PER_decode_unaligned_constrained(p_buf, p_range.PER_min_bits(TRUE, FALSE), p_signed);
  }
  else if (p_range == PER_OCTET) { // one-octet case
    p_buf.PER_octet_align(FALSE);
    PER_decode_unaligned_constrained(p_buf, 8, p_signed);
  }
  else { // two-octet case
    p_buf.PER_octet_align(FALSE);
    PER_decode_unaligned_constrained(p_buf, 16, p_signed);
  }
}

int INTEGER::PER_decode_length(TTCN_Buffer& p_buf, int p_options, const INTEGER& p_range,
                               const INTEGER& p_lb, const INTEGER& p_ub, boolean p_normally_small)
{
  if (p_normally_small) {
    if (!p_buf.PER_get_bit()) {
      bound_flag = TRUE;
      native_flag = TRUE;
      PER_decode_int(p_buf, 6, FALSE, val.native);
      ++val.native;
      return 0;
    }
    // otherwise continue on as if it was an unconstrained length
  }
  else {
    if (p_range == 1 && p_ub < PER_64K) {
      *this = p_ub;
      return 0; // single value, nothing to do
    }
    if (p_range > 0 && p_ub < PER_64K) {
      // decode as a constrained whole number
      if (p_options & PER_ALIGNED) {
        PER_decode_aligned_constrained(p_buf, p_range, FALSE);
      }
      else {
        PER_decode_unaligned_constrained(p_buf, p_range.PER_min_bits(TRUE, FALSE), FALSE);
      }
      *this = *this + p_lb;
      return 0;
    }
  }
  // otherwise the length is treated as unconstrained
  if (p_options & PER_ALIGNED) {
    p_buf.PER_octet_align(FALSE);
  }
  bound_flag = TRUE;
  native_flag = TRUE;
  if (p_buf.PER_get_bit()) {
    if (p_buf.PER_get_bit()) { // length starts with 11 -> length is a multiple of 16K
      int mul_16k;
      PER_decode_int(p_buf, 6, FALSE, mul_16k);
      if (mul_16k == 0 || mul_16k > 4) {
        TTCN_EncDec_ErrorContext::error(TTCN_EncDec::ET_INVAL_MSG,
          "Decoded invalid length multiplier for large lengths.");
      }
      val.native = mul_16k * PER_16K;
      return mul_16k;
    }
    else { // length starts with 10 -> 14-bit length
      PER_decode_int(p_buf, 14, FALSE, val.native);
      return 0;
    }
  }
  else { // length starts with 0 -> 7-bit length
    PER_decode_int(p_buf, 7, FALSE, val.native);
    return 0;
  }
}

int INTEGER::PER_decode_length(TTCN_Buffer& p_buf, int p_options, boolean p_normally_small)
{
  INTEGER zero(0);
  return PER_decode_length(p_buf, p_options, zero, zero, zero, p_normally_small);
}

void INTEGER::PER_decode_unconstrained(TTCN_Buffer& p_buf, int p_options, const INTEGER& p_range, boolean p_signed)
{
  // todo: use INTEGERs instead of ints, in case someone encodes a number that doesn't fit in 2 billion bits ...
  INTEGER length;
  int length_lb = p_range > 0 ? 1 : 0;
  int length_ub = p_range > 0 ? ((p_range.PER_min_bits(TRUE, FALSE) + 7) / 8) : -1;
  int length_range = p_range > 0 ? (length_ub - length_lb + 1) : 0;
  int mul_16k;
  boolean first_length = TRUE;

  do {
    mul_16k = length.PER_decode_length(p_buf, p_options, length_range, length_lb, length_ub);
    if (mul_16k > 0) {
      // decode mul * 16k octets (partial value)
      int bits_decoded = mul_16k * PER_16K * 8;
      if (first_length) {
        PER_decode_unaligned_constrained(p_buf, bits_decoded, p_signed);
        first_length = FALSE;
      }
      else {
        INTEGER partial_val;
        partial_val.PER_decode_unaligned_constrained(p_buf, bits_decoded, FALSE);
        BN_lshift(val.openssl, val.openssl, bits_decoded); // *this = *this << bits_decoded
        *this = *this + partial_val;
      }
      length_range = 0; // make sure future partial lengths are not encoded as constrained integers
    }
    else {
      // decode the remaining value
      if (p_range > 0) {
        p_buf.PER_octet_align(FALSE);
      }
      if (first_length) {
        PER_decode_unaligned_constrained(p_buf, length * 8, p_signed);
      }
      else if (length != 0) {
        INTEGER partial_val;
        partial_val.PER_decode_unaligned_constrained(p_buf, length * 8, FALSE);
        BN_lshift(val.openssl, val.openssl, length * 8); // *this = *this << (length * 8)
        *this = *this + partial_val;
      }
    }
  }
  while (mul_16k > 0);
}

void INTEGER::PER_decode_normally_small(TTCN_Buffer& p_buf, int p_options)
{
  if (p_buf.PER_get_bit()) {
    // start bit is 1
    PER_decode_unconstrained(p_buf, p_options, INTEGER(-1), FALSE);
  }
  else {
    // start bit is 0
    bound_flag = TRUE;
    native_flag = TRUE;
    PER_decode_int(p_buf, 6, FALSE, val.native);
  }
}

void INTEGER::PER_decode(const TTCN_Typedescriptor_t& p_td, TTCN_Buffer& p_buf, int p_options)
{
  const Per_Integer_Constraint* cons = dynamic_cast<const Per_Integer_Constraint*>(p_td.per->c);
  if (cons == NULL) {
    TTCN_EncDec_ErrorContext::error(TTCN_EncDec::ET_INTERNAL,
      "Internal error: Invalid constraint in PER descriptor.");
    return;
  }
  boolean extendable = cons->has_extension_marker();
  boolean ext_bit = extendable ? p_buf.PER_get_bit() : FALSE;
  INTEGER range = ext_bit ? INTEGER(0) : cons->get_nof_values();
  // bits needed to encode all possible values
  if (range == 1) { // single value
    *this = cons->get_lower_bound();
    return;
  }
  if (range > 0) { // constrained integer
    boolean completed = TRUE;
    if (p_options & PER_ALIGNED) {
      if (range <= PER_64K) {
        PER_decode_aligned_constrained(p_buf, range.val.native, FALSE);
      }
      else {
        completed = FALSE; // indefinite length case: use a length determinant (below)
      }
      
    }
    else { // unaligned
      PER_decode_unaligned_constrained(p_buf, range.PER_min_bits(TRUE, FALSE), FALSE);
    }
    if (completed) {
      // we've actually decoded the original number minus the lower bound
      *this = *this + cons->get_lower_bound();
      if (!cons->is_within_extension_root(*this)) {
        TTCN_EncDec_ErrorContext::error(TTCN_EncDec::ET_CONSTRAINT,
          "Decoded integer value does not match PER-visible constraints.");
      }
      return;
    }
  }

  // semi-constrained or unconstrained integer, or constrained & aligned with range > 64k
  boolean signed_ = ext_bit || !cons->has_lower_bound();
  PER_decode_unconstrained(p_buf, p_options, range, signed_);
  if (!signed_) {
    *this = *this + cons->get_lower_bound();
  }

  if (!ext_bit && !cons->is_within_extension_root(*this)) {
    TTCN_EncDec_ErrorContext::error(TTCN_EncDec::ET_CONSTRAINT,
      "Decoded integer value does not match PER-visible constraints.");
  }
}

// Global functions.

INTEGER operator+(int int_value, const INTEGER& other_value)
{
  return INTEGER(int_value) + other_value;
}

INTEGER operator-(int int_value, const INTEGER& other_value)
{
  return INTEGER(int_value) - other_value;
}

INTEGER operator*(int int_value, const INTEGER& other_value)
{
  return INTEGER(int_value) * other_value;
}

INTEGER operator/(int int_value, const INTEGER& other_value)
{
  return INTEGER(int_value) / other_value;
}

INTEGER rem(int left_value, int right_value)
{
  if (right_value == 0)
    TTCN_error("The right operand of rem operator is zero.");
  return INTEGER(left_value - right_value * (left_value / right_value));
}

INTEGER rem(const INTEGER& left_value, const INTEGER& right_value)
{
  left_value.must_bound("Unbound left operand of rem operator.");
  right_value.must_bound("Unbound right operand of rem operator.");
  return left_value - right_value * (left_value / right_value);
}

INTEGER rem(const INTEGER& left_value, int right_value)
{
  return rem(left_value, INTEGER(right_value));
}

INTEGER rem(int left_value, const INTEGER& right_value)
{
  return rem(INTEGER(left_value), right_value);
}

INTEGER mod(int left_value, int right_value)
{
  if (right_value < 0) right_value = -right_value;
  else if (right_value == 0)
    TTCN_error("The right operand of mod operator is zero.");
  if (left_value > 0) return rem(left_value, right_value);
  else {
    int result = rem(left_value, right_value);
    if (result == 0) return 0;
    else return INTEGER(right_value + result);
  }
}

INTEGER mod(const INTEGER& left_value, const INTEGER& right_value)
{
  left_value.must_bound("Unbound left operand of mod operator.");
  right_value.must_bound("Unbound right operand of mod operator.");
  INTEGER right_value_abs(right_value);
  if (right_value < 0) right_value_abs = -right_value_abs;
  else if (right_value == 0)
    TTCN_error("The right operand of mod operator is zero.");
  if (left_value > 0) {
    return rem(left_value, right_value_abs);
  } else {
    INTEGER result = rem(left_value, right_value_abs);
    if (result == 0) return INTEGER((int)0);
    else return INTEGER(right_value_abs + result);
  }
}

INTEGER mod(const INTEGER& left_value, int right_value)
{
  return mod(left_value, INTEGER(right_value));
}

INTEGER mod(int left_value, const INTEGER& right_value)
{
  return mod(INTEGER(left_value), right_value);
}

boolean operator==(int int_value, const INTEGER& other_value)
{
  return INTEGER(int_value) == other_value;
}

boolean operator<(int int_value, const INTEGER& other_value)
{
  return INTEGER(int_value) < other_value;
}

boolean operator>(int int_value, const INTEGER& other_value)
{
  return INTEGER(int_value) > other_value;
}

// Integer template class.

#ifndef TITAN_ASN1ENC
void INTEGER_template::clean_up()
{
  switch (template_selection) {
    case SPECIFIC_VALUE:
      if (unlikely(!int_val.native_flag)) BN_free(int_val.val.openssl);
      break;
    case VALUE_LIST:
    case COMPLEMENTED_LIST:
    case CONJUNCTION_MATCH:
      delete [] value_list.list_value;
      break;
    case VALUE_RANGE:
      if (value_range.min_is_present && unlikely(!value_range.min_value.native_flag))
        BN_free(value_range.min_value.val.openssl);
      if (value_range.max_is_present && unlikely(!value_range.max_value.native_flag))
        BN_free(value_range.max_value.val.openssl);
      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 INTEGER_template::copy_template(const INTEGER_template& other_value)
{
  switch (other_value.template_selection) {
  case SPECIFIC_VALUE:
    int_val.native_flag = other_value.int_val.native_flag;
    if (likely(int_val.native_flag))
      int_val.val.native = other_value.int_val.val.native;
    else int_val.val.openssl = BN_dup(other_value.int_val.val.openssl);
    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 INTEGER_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.min_is_present = other_value.value_range.min_is_present;
    value_range.min_is_exclusive = other_value.value_range.min_is_exclusive;
    if (value_range.min_is_present) {
      value_range.min_value.native_flag = other_value.value_range.min_value.native_flag;
      if (likely(value_range.min_value.native_flag))
        value_range.min_value.val.native =
          other_value.value_range.min_value.val.native;
      else
        value_range.min_value.val.openssl =
          BN_dup(other_value.value_range.min_value.val.openssl);
    }
    value_range.max_is_present = other_value.value_range.max_is_present;
    value_range.max_is_exclusive = other_value.value_range.max_is_exclusive;
    if (value_range.max_is_present) {
      value_range.max_value.native_flag = other_value.value_range.max_value.native_flag;
      if (likely(value_range.max_value.native_flag))
        value_range.max_value.val.native =
          other_value.value_range.max_value.val.native;
      else
        value_range.max_value.val.openssl =
          BN_dup(other_value.value_range.max_value.val.openssl);
    }
    break;
  case IMPLICATION_MATCH:
    implication_.precondition = new INTEGER_template(*other_value.implication_.precondition);
    implication_.implied_template = new INTEGER_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 integer template.");
  }
  set_selection(other_value);
}

INTEGER_template::INTEGER_template()
{
}

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

INTEGER_template::INTEGER_template(int other_value)
  : Base_Template(SPECIFIC_VALUE)
{
  int_val.native_flag = TRUE;
  int_val.val.native = other_value;
}

INTEGER_template::INTEGER_template(const INTEGER& other_value)
  : Base_Template(SPECIFIC_VALUE)
{
  other_value.must_bound("Creating a template from an unbound integer "
    "value.");
  int_val_t other_value_int = other_value.get_val();
  int_val.native_flag = other_value_int.native_flag;
  if (likely(int_val.native_flag))
    int_val.val.native = other_value_int.val.native;
  else int_val.val.openssl = BN_dup(other_value_int.val.openssl);
}

INTEGER_template::INTEGER_template(const OPTIONAL<INTEGER>& other_value)
{
  switch (other_value.get_selection()) {
  case OPTIONAL_PRESENT: {
    set_selection(SPECIFIC_VALUE);
    int_val_t other_value_int = ((const INTEGER &)other_value).get_val();
    int_val.native_flag = other_value_int.native_flag;
    if (likely(int_val.native_flag))
      int_val.val.native = other_value_int.val.native;
    else int_val.val.openssl = BN_dup(other_value_int.val.openssl);
    break; }
  case OPTIONAL_OMIT:
    set_selection(OMIT_VALUE);
    break;
  case OPTIONAL_UNBOUND:
    TTCN_error("Creating an integer template from an unbound optional field.");
  }
}

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

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

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

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

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

INTEGER_template& INTEGER_template::operator=(int other_value)
{
  clean_up();
  set_selection(SPECIFIC_VALUE);
  int_val.native_flag = TRUE;
  int_val.val.native = other_value;
  return *this;
}

INTEGER_template& INTEGER_template::operator=(const INTEGER& other_value)
{
  other_value.must_bound("Assignment of an unbound integer value to a "
    "template.");
  clean_up();
  set_selection(SPECIFIC_VALUE);
  int_val_t other_value_int = other_value.get_val();
  int_val.native_flag = other_value_int.native_flag;
  if (likely(int_val.native_flag))
    int_val.val.native = other_value_int.val.native;
  else int_val.val.openssl = BN_dup(other_value_int.val.openssl);
  return *this;
}

INTEGER_template& INTEGER_template::operator=
  (const OPTIONAL<INTEGER>& other_value)
{
  clean_up();
  switch (other_value.get_selection()) {
  case OPTIONAL_PRESENT: {
    set_selection(SPECIFIC_VALUE);
    int_val_t other_value_int = ((const INTEGER &)other_value).get_val();
    int_val.native_flag = other_value_int.native_flag;
    if (likely(int_val.native_flag))
      int_val.val.native = other_value_int.val.native;
    else int_val.val.openssl = BN_dup(other_value_int.val.openssl);
    break; }
  case OPTIONAL_OMIT:
    set_selection(OMIT_VALUE);
    break;
  case OPTIONAL_UNBOUND:
    TTCN_error("Assignment of an unbound optional field to an integer "
      "template.");
  }
  return *this;
}

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

boolean INTEGER_template::match(int other_value, boolean /* legacy */) const
{
  switch (template_selection) {
  case SPECIFIC_VALUE:
    if (likely(int_val.native_flag)) return int_val.val.native == other_value;
    return int_val_t(BN_dup(int_val.val.openssl)) == 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: {
    boolean lower_boundary = !value_range.min_is_present;
    boolean upper_boundary = !value_range.max_is_present;
    // Lower boundary is set.
    if (!lower_boundary) {
      if (!value_range.min_is_exclusive) {
      lower_boundary = (likely(value_range.min_value.native_flag) ?
        int_val_t(value_range.min_value.val.native) :
          int_val_t(BN_dup(value_range.min_value.val.openssl))) <= other_value;
      } else {
        lower_boundary = (likely(value_range.min_value.native_flag) ?
        int_val_t(value_range.min_value.val.native) :
          int_val_t(BN_dup(value_range.min_value.val.openssl))) < other_value;
      }
    }
    // Upper boundary is set.
    if (!upper_boundary) {
      if (!value_range.max_is_exclusive) {
      upper_boundary = (likely(value_range.max_value.native_flag) ?
        int_val_t(value_range.max_value.val.native) :
          int_val_t(BN_dup(value_range.max_value.val.openssl))) >= other_value;
      } else {
        upper_boundary = (likely(value_range.max_value.native_flag) ?
        int_val_t(value_range.max_value.val.native) :
          int_val_t(BN_dup(value_range.max_value.val.openssl))) > other_value;
      }
    }
    return lower_boundary && upper_boundary; }
  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 integer "
      "template.");
  }
  return FALSE;
}

boolean INTEGER_template::match(const INTEGER& other_value,
                                boolean /* legacy */) const
{
  if (!other_value.is_bound()) return FALSE;
  switch (template_selection) {
    case SPECIFIC_VALUE: {
      int_val_t int_val_int = likely(int_val.native_flag) ?
        int_val_t(int_val.val.native) : int_val_t(BN_dup(int_val.val.openssl));
      return int_val_int == other_value.get_val(); }
    case OMIT_VALUE:
      return FALSE;
    case ANY_VALUE:
    case ANY_OR_OMIT:
      return TRUE;
    case VALUE_LIST:
    case COMPLEMENTED_LIST: // Merged cases.
      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: {
      boolean lower_boundary = !value_range.min_is_present;
      boolean upper_boundary = !value_range.max_is_present;
      // Lower boundary is set.
      if (!lower_boundary) {
        if (!value_range.min_is_exclusive) {
          lower_boundary = (likely(value_range.min_value.native_flag) ?
            int_val_t(value_range.min_value.val.native) :
              int_val_t(BN_dup(value_range.min_value.val.openssl))) <= other_value.get_val();
        } else {
          lower_boundary = (likely(value_range.min_value.native_flag) ?
            int_val_t(value_range.min_value.val.native) :
              int_val_t(BN_dup(value_range.min_value.val.openssl))) < other_value.get_val();
        }
      }
      // Upper boundary is set.
      if (!upper_boundary) {
        if (value_range.max_is_exclusive) {
        upper_boundary = (likely(value_range.max_value.native_flag) ?
          int_val_t(value_range.max_value.val.native) :
            int_val_t(BN_dup(value_range.max_value.val.openssl))) > other_value.get_val();
        } else {
          upper_boundary = (likely(value_range.max_value.native_flag) ?
            int_val_t(value_range.max_value.val.native) :
            int_val_t(BN_dup(value_range.max_value.val.openssl))) >= other_value.get_val();
      }
      }
      return lower_boundary && upper_boundary; }
    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 integer "
        "template.");
  }
  return FALSE;
}

INTEGER INTEGER_template::valueof() const
{
  if (template_selection != SPECIFIC_VALUE || is_ifpresent)
    TTCN_error("Performing a valueof or send operation on a non-specific "
      "integer template.");
  if (likely(int_val.native_flag)) return INTEGER(int_val.val.native);
  else return INTEGER(BN_dup(int_val.val.openssl));
}

void INTEGER_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 INTEGER_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 an integer template.");
  }
}

INTEGER_template& INTEGER_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 integer template.");
  if (list_index >= value_list.n_values)
    TTCN_error("Index overflow in an integer value list template.");
  return value_list.list_value[list_index];
}

void INTEGER_template::set_min(int min_value)
{
  if (template_selection != VALUE_RANGE)
    TTCN_error("Integer template is not range when setting lower limit.");
  if (value_range.max_is_present) {
    int_val_t max_value_int = likely(value_range.max_value.native_flag) ?
      int_val_t(value_range.max_value.val.native) :
        int_val_t(BN_dup(value_range.max_value.val.openssl));
    if (max_value_int < min_value)
      TTCN_error("The lower limit of the range is greater than the upper "
        "limit in an integer template.");
  }
  value_range.min_is_present = TRUE;
  value_range.min_is_exclusive = FALSE;
  value_range.min_value.native_flag = TRUE;
  value_range.min_value.val.native = min_value;
}

void INTEGER_template::set_min(const INTEGER& min_value)
{
  // Redundant, but performace matters.  :)
  min_value.must_bound("Using an unbound value when setting the lower bound "
    "in an integer range template.");
  if (template_selection != VALUE_RANGE)
    TTCN_error("Integer template is not range when setting lower limit.");
  int_val_t min_value_int = min_value.get_val();
  if (value_range.max_is_present) {
    int_val_t max_value_int = likely(value_range.max_value.native_flag) ?
      int_val_t(value_range.max_value.val.native) :
        int_val_t(BN_dup(value_range.max_value.val.openssl));
    if (max_value_int < min_value_int)
      TTCN_error("The lower limit of the range is greater than the upper "
        "limit in an integer template.");
  }
  value_range.min_is_present = TRUE;
  value_range.min_is_exclusive = FALSE;
  value_range.min_value.native_flag = min_value_int.native_flag;
  if (likely(value_range.min_value.native_flag))
    value_range.min_value.val.native = min_value_int.val.native;
  else value_range.min_value.val.openssl = BN_dup(min_value_int.val.openssl);
}

void INTEGER_template::set_max(int max_value)
{
  if (template_selection != VALUE_RANGE)
    TTCN_error("Integer template is not range when setting upper limit.");
  if (value_range.min_is_present) {
    int_val_t min_value_int = likely(value_range.min_value.native_flag) ?
      int_val_t(value_range.min_value.val.native) :
        int_val_t(BN_dup(value_range.min_value.val.openssl));
    if (min_value_int > max_value)
      TTCN_error("The upper limit of the range is smaller than the lower "
        "limit in an integer template.");
  }
  value_range.max_is_present = TRUE;
  value_range.max_is_exclusive = FALSE;
  value_range.max_value.native_flag = TRUE;
  value_range.max_value.val.native = max_value;
}

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

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

void INTEGER_template::set_max(const INTEGER& max_value)
{
  max_value.must_bound("Using an unbound value when setting the upper bound "
    "in an integer range template.");
  if (template_selection != VALUE_RANGE)
    TTCN_error("Integer template is not range when setting upper limit.");
  int_val_t max_value_int = max_value.get_val();
  if (value_range.min_is_present) {
    int_val_t min_value_int = likely(value_range.min_value.native_flag) ?
      int_val_t(value_range.min_value.val.native) :
        int_val_t(BN_dup(value_range.min_value.val.openssl));
    if (min_value_int > max_value_int)
      TTCN_error("The upper limit of the range is smaller than the lower "
        "limit in an integer template.");
  }
  value_range.max_is_present = TRUE;
  value_range.max_is_exclusive = FALSE;
  value_range.max_value.native_flag = max_value_int.native_flag;
  if (likely(value_range.max_value.native_flag))
    value_range.max_value.val.native = max_value_int.val.native;
  else value_range.max_value.val.openssl = BN_dup(max_value_int.val.openssl);
}

void INTEGER_template::log() const
{
  switch (template_selection) {
  case SPECIFIC_VALUE: {
    int_val_t int_val_int = likely(int_val.native_flag) ?
      int_val_t(int_val.val.native) : int_val_t(BN_dup(int_val.val.openssl));
    char *tmp_str = int_val_int.as_string();
    TTCN_Logger::log_event("%s", tmp_str);
    Free(tmp_str);
    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) {
      int_val_t min_value_int = likely(value_range.min_value.native_flag) ?
        int_val_t(value_range.min_value.val.native) :
          int_val_t(BN_dup(value_range.min_value.val.openssl));
      char *min_str = min_value_int.as_string();
      TTCN_Logger::log_event("%s", min_str);
      Free(min_str);
    } 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) {
      int_val_t max_value_int = likely(value_range.max_value.native_flag) ?
        int_val_t(value_range.max_value.val.native) :
          int_val_t(BN_dup(value_range.max_value.val.openssl));
      char *max_str = max_value_int.as_string();
      TTCN_Logger::log_event("%s", max_str);
      Free(max_str);
    } 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 INTEGER_template::log_match(const INTEGER& 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 INTEGER_template::set_param(Module_Param& param) {
  param.basic_check(Module_Param::BC_TEMPLATE, "integer 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: {
    INTEGER_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_Integer: {
    INTEGER tmp;
    tmp.set_val(*mp->get_integer());
    *this = tmp;
  } break;
  case Module_Param::MP_IntRange: {
    set_type(VALUE_RANGE);
    if (mp->get_lower_int()!=NULL) {
      INTEGER tmp;
      tmp.set_val(*mp->get_lower_int());
      set_min(tmp);
    }
    set_min_exclusive(mp->get_is_min_exclusive());
    if (mp->get_upper_int()!=NULL) {
      INTEGER tmp;
      tmp.set_val(*mp->get_upper_int());
      set_max(tmp);
    }
    set_max_exclusive(mp->get_is_max_exclusive());
  } break;
  case Module_Param::MP_Implication_Template: {
    INTEGER_template* precondition = new INTEGER_template;
    precondition->set_param(*mp->get_elem(0));
    INTEGER_template* implied_template = new INTEGER_template;
    implied_template->set_param(*mp->get_elem(1));
    *this = INTEGER_template(precondition, implied_template);
  } break;
  case Module_Param::MP_Expression:
    switch (mp->get_expr_type()) {
    case Module_Param::EXPR_NEGATE: {
      INTEGER operand;
      operand.set_param(*mp->get_operand1());
      *this = - operand;
      break; }
    case Module_Param::EXPR_ADD: {
      INTEGER operand1, operand2;
      operand1.set_param(*mp->get_operand1());
      operand2.set_param(*mp->get_operand2());
      *this = operand1 + operand2;
      break; }
    case Module_Param::EXPR_SUBTRACT: {
      INTEGER operand1, operand2;
      operand1.set_param(*mp->get_operand1());
      operand2.set_param(*mp->get_operand2());
      *this = operand1 - operand2;
      break; }
    case Module_Param::EXPR_MULTIPLY: {
      INTEGER operand1, operand2;
      operand1.set_param(*mp->get_operand1());
      operand2.set_param(*mp->get_operand2());
      *this = operand1 * operand2;
      break; }
    case Module_Param::EXPR_DIVIDE: {
      INTEGER operand1, operand2;
      operand1.set_param(*mp->get_operand1());
      operand2.set_param(*mp->get_operand2());
      if (operand2 == 0) {
        param.error("Integer division by zero.");
      }
      *this = operand1 / operand2;
      break; }
    default:
      param.expr_type_error("an integer");
      break;
    }
    break;    
  default:
    param.type_error("integer template");
  }
  is_ifpresent = param.get_ifpresent() || mp->get_ifpresent();
}

#ifdef TITAN_RUNTIME_2
Module_Param* INTEGER_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:
    if (likely(int_val.native_flag)) {
      mp = new Module_Param_Integer(new int_val_t(int_val.val.native));
    }
    else {
      mp = new Module_Param_Integer(new int_val_t(BN_dup(int_val.val.openssl)));
    }
    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: {
    int_val_t* lower_bound = NULL;
    int_val_t* upper_bound = NULL;
    if (value_range.min_is_present) {
      if (value_range.min_value.native_flag) {
        lower_bound = new int_val_t(value_range.min_value.val.native);
      }
      else {
        lower_bound = new int_val_t(BN_dup(value_range.min_value.val.openssl));
      }
    }
    if (value_range.max_is_present) {
      if (value_range.max_value.native_flag) {
        upper_bound = new int_val_t(value_range.max_value.val.native);
      }
      else {
        upper_bound = new int_val_t(BN_dup(value_range.max_value.val.openssl));
      }
    }
    mp = new Module_Param_IntRange(lower_bound, upper_bound, 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 integer template.");
    break;
  }
  if (is_ifpresent) {
    mp->set_ifpresent();
  }
  return mp;
}
#endif

void INTEGER_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_int(likely(int_val.native_flag) ? int_val_t(int_val.val.native)
      : int_val_t(BN_dup(int_val.val.openssl)));
    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_int(likely(value_range.min_value.native_flag) ?
        int_val_t(value_range.min_value.val.native) :
          int_val_t(BN_dup(value_range.min_value.val.openssl)));
    text_buf.push_int(value_range.max_is_present ? 1 : 0);
    if (value_range.max_is_present)
      text_buf.push_int(likely(value_range.max_value.native_flag) ?
        int_val_t(value_range.max_value.val.native) :
          int_val_t(BN_dup(value_range.max_value.val.openssl)));
    break;
  default:
    TTCN_error("Text encoder: Encoding an uninitialized/unsupported integer "
      "template.");
  }
}

void INTEGER_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: {
    int_val_t int_val_int = text_buf.pull_int();
    int_val.native_flag = int_val_int.native_flag;
    if (likely(int_val.native_flag)) int_val.val.native = int_val_int.val.native;
    else int_val.val.openssl = BN_dup(int_val_int.val.openssl);
    break; }
  case VALUE_LIST:
  case COMPLEMENTED_LIST:
    value_list.n_values = text_buf.pull_int().get_val();
    value_list.list_value = new INTEGER_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) {
      int_val_t min_value_int = text_buf.pull_int();
      value_range.min_value.native_flag = min_value_int.native_flag;
      if (likely(value_range.min_value.native_flag))
        value_range.min_value.val.native = min_value_int.val.native;
      else value_range.min_value.val.openssl = BN_dup(min_value_int.val.openssl);
    }
    value_range.max_is_present = text_buf.pull_int() != 0;
    if (value_range.max_is_present) {
      int_val_t max_value_int = text_buf.pull_int();
      value_range.max_value.native_flag = max_value_int.native_flag;
      if (likely(value_range.max_value.native_flag))
        value_range.max_value.val.native = max_value_int.val.native;
      else value_range.max_value.val.openssl = BN_dup(max_value_int.val.openssl);
    }
    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 an integer template.");
  }
}

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

boolean INTEGER_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 INTEGER_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 : "integer");
}
#endif

#endif // #ifndef TITAN_ASN1ENC