/******************************************************************************
 * 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:
 *   Baranyi, Botond – initial implementation
 *
 ******************************************************************************/
#include "../../common/memory.h"
#include "ttcn3_map_gen.h"

#include "../main.hh"

/** code generation for original runtime */
//static void defTtcn3MapClass1(const struct_of_def *sdef, output_struct *output);
//static void defTtcn3MapTemplate1(const struct_of_def *sdef, output_struct *output);
/** code generation for alternative runtime (TITAN_RUNTIME_2) */
//static void defTtcn3MapClass2(const struct_of_def *sdef, output_struct *output);
//static void defTtcn3MapTemplate2(const struct_of_def *sdef, output_struct *output);

/*void defTtcn3MapClass(const struct_of_def *sdef, output_struct *output)
{
  if (use_runtime_2 && !sdef->containsClass) defTtcn3MapClass2(sdef, output);
  else defTtcn3MapClass1(sdef, output);
}

void defTtcn3MapTemplate(const struct_of_def *sdef, output_struct *output)
{
  if (use_runtime_2 && !sdef->containsClass) defTtcn3MapTemplate2(sdef, output);
  else defTtcn3MapTemplate1(sdef, output);
}*/

/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */

void defTtcn3MapClass(const ttcn3_map_def* sdef, output_struct* output)
{
  char* def = NULL;
  char* src = NULL;
  const char* name = sdef->name;
  const char* dispname = sdef->dispname;
  const char* key_name = sdef->key_name;
  const char* value_name = sdef->value_name;
  /* true if the key type is not comparable, so extra integer keys are used and cached */
  boolean int_keys = sdef->key_to_int_converter != NULL;

  /* Class definition and private data members */
  def = mputprintf(def,
#ifndef NDEBUG
    "// written by %s in " __FILE__ " at %d\n"
#endif
    "class %s : public Base_Type {\n"
    "struct ttcn3_map_struct {\n"
    "int ref_count;\n"
    "int n_elements;\n"
    "int max_elements;\n"
    "%s** keys;%s\n"
    "%s** values;\n"
    "} *val_ptr;\n"
#ifndef NDEBUG
      , __FUNCTION__, __LINE__
#endif
    , name, key_name, int_keys ? "INTEGER** int_keys;\n" : "", value_name);

  /* private member functions */
  def = mputprintf(def,
    "private:\n"
    "friend boolean operator==(null_type null_value, const %s& other_value);\n"
    "friend class %s_template;\n", name, name);

  if (int_keys) {
    def = mputprintf(def, "static INTEGER* get_int_from_key(const %s& key_value);\n",
      key_name);
    src = mputprintf(src,
      "INTEGER* %s::get_int_from_key(const %s& key_value)\n"
      "{\n"
      "return new INTEGER(%s);\n"
      "}\n\n", name, key_name, sdef->key_to_int_converter);
  }

  /* public member functions */

  /* constructors */
  def = mputprintf(def,
    "\npublic:\n"
    "%s();\n", name);
  src = mputprintf(src,
    "%s::%s()\n"
    "{\n"
    "val_ptr = NULL;\n"
    "}\n\n", name, name);

  def = mputprintf(def, "%s(null_type other_value);\n", name);
  src = mputprintf(src,
    "%s::%s(null_type)\n"
    "{\n"
    "val_ptr = new ttcn3_map_struct;\n"
    "val_ptr->ref_count = 1;\n"
    "val_ptr->n_elements = 0;\n"
    "val_ptr->max_elements = 0;\n"
    "val_ptr->keys = NULL;\n%s"
    "val_ptr->values = NULL;\n"
    "}\n\n", name, name, int_keys ? "val_ptr->int_keys = NULL;\n" : "");

  /* copy constructor */
  def = mputprintf(def, "%s(const %s& other_value);\n", name, name);
  src = mputprintf(src,
     "%s::%s(const %s& other_value)\n"
     "{\n"
     "if (!other_value.is_bound()) TTCN_error(\"Copying an unbound value of type %s.\");\n"
     "val_ptr = other_value.val_ptr;\n"
     "val_ptr->ref_count++;\n"
     "}\n\n", name, name, name, dispname);

  /* destructor */
  def = mputprintf(def, "~%s();\n\n", name);
  src = mputprintf(src,
    "%s::~%s()\n"
    "{\n"
    "clean_up();\n"
    "if (val_ptr != NULL) val_ptr = NULL;\n"
    "}\n\n", name, name);

  /* clean_up function */
  def = mputstr(def, "void clean_up();\n");
  src = mputprintf(src,
     "void %s::clean_up()\n"
     "{\n"
     "if (val_ptr != NULL) {\n"
     "if (val_ptr->ref_count > 1) {\n"
     "val_ptr->ref_count--;\n"
     "val_ptr = NULL;\n"
     "}\n"
     "else if (val_ptr->ref_count == 1) {\n"
     "for (int elem_count = 0; elem_count < val_ptr->n_elements; elem_count++) {\n"
     "delete val_ptr->keys[elem_count];\n%s"
     "delete val_ptr->values[elem_count];\n"
     "}\n"
     "free_pointers((void**)val_ptr->keys);\n%s"
     "free_pointers((void**)val_ptr->values);\n"
     "delete val_ptr;\n"
     "val_ptr = NULL;\n"
     "}\n"
     "else TTCN_error(\"Internal error: Invalid reference counter in a map value.\");\n"
     "}\n"
     "}\n\n", name,
     int_keys ? "delete val_ptr->int_keys[elem_count];\n" : "",
     int_keys ? "free_pointers((void**)val_ptr->int_keys);\n" : "");

  /* assignment operators */
  def = mputprintf(def, "%s& operator=(null_type other_value);\n", name);
  src = mputprintf(src,
    "%s& %s::operator=(null_type)\n"
    "{\n"
    "clean_up();\n"
    "val_ptr = new ttcn3_map_struct;\n"
    "val_ptr->ref_count = 1;\n"
    "val_ptr->n_elements = 0;\n"
    "val_ptr->max_elements = 0;\n"
    "val_ptr->keys = NULL;\n%s"
    "val_ptr->values = NULL;\n"
    "return *this;\n"
    "}\n\n", name, name, int_keys ? "val_ptr->int_keys = NULL;\n" : "");

  def = mputprintf(def, "%s& operator=(const %s& other_value);\n\n", name, name);
  src = mputprintf(src,
    "%s& %s::operator=(const %s& other_value)\n"
    "{\n"
    "if (other_value.val_ptr == NULL) "
    "TTCN_error(\"Assigning an unbound value of type %s.\");\n"
    "if (this != &other_value) {\n"
    "clean_up();\n"
    "val_ptr = other_value.val_ptr;\n"
    "val_ptr->ref_count++;\n"
    "}\n"
    "return *this;\n"
    "}\n\n", name, name, name, dispname);

  /* comparison operators */
  def = mputstr(def, "boolean operator==(null_type other_value) const;\n");
  src = mputprintf(src,
    "boolean %s::operator==(null_type) const\n"
    "{\n"
    "if (val_ptr == NULL)\n"
    "TTCN_error(\"The left operand of comparison is an unbound value of type %s.\");\n"
    "return val_ptr->n_elements == 0;\n"
    "}\n\n", name, dispname);

  def = mputprintf(def, "boolean operator==(const %s& other_value) const;\n",
                   name);
  src = mputprintf(src,
    "boolean %s::operator==(const %s& other_value) const\n"
    "{\n"
    "if (val_ptr == NULL) "
    "TTCN_error(\"The left operand of comparison is an unbound value of type %s.\");\n"
    "if (other_value.val_ptr == NULL) "
    "TTCN_error(\"The right operand of comparison is an unbound value of type %s.\");\n"
    "if (val_ptr == other_value.val_ptr) return TRUE;\n"
    "if (val_ptr->n_elements != (other_value.val_ptr)->n_elements)\n"
    "return FALSE;\n"
    "for (int elem_count = 0; elem_count < val_ptr->n_elements; elem_count++) {\n"
    "if (!other_value.has_key(*val_ptr->keys[elem_count]) || "
    "*val_ptr->values[elem_count] != other_value[*val_ptr->keys[elem_count]]) "
    "return FALSE;\n"
    "}\n"
    "return TRUE;\n"
    "}\n\n", name, name, dispname, dispname);

  def = mputstr(def, "inline boolean operator!=(null_type other_value) const "
                "{ return !(*this == other_value); }\n");
  def = mputprintf(def, "inline boolean operator!=(const %s& other_value) "
                   "const { return !(*this == other_value); }\n\n", name);

  /* indexing operators */
  /* Non-const operator[] is allowed to extend the map */
  def = mputprintf(def, "%s& operator[](const %s& key_value);\n",
    value_name, key_name);
  src = mputprintf(src,
    "%s& %s::operator[](const %s& key_value)\n"
    "{\n"
    "key_value.must_bound(\"Indexing with an unbound key in a value of type %s.\");\n"
    "int key_idx = find_key(key_value);\n"
    "boolean new_key = val_ptr == NULL || key_idx == val_ptr->n_elements;\n"
    "if (val_ptr == NULL) {\n"
    "val_ptr = new ttcn3_map_struct;\n"
    "val_ptr->ref_count = 1;\n"
    "val_ptr->n_elements = 0;\n"
    "val_ptr->max_elements = 0;\n"
    "val_ptr->keys = NULL;\n%s" /* 1 */
    "val_ptr->values = NULL;\n"
    "} else if (val_ptr->ref_count > 1) {\n"
    "struct ttcn3_map_struct* new_val_ptr = new ttcn3_map_struct;\n"
    "new_val_ptr->ref_count = 1;\n"
    "new_val_ptr->n_elements = val_ptr->n_elements + (new_key ? 1 : 0);\n"
    "new_val_ptr->max_elements = ttcn3_map_get_max_elements(new_val_ptr->n_elements);\n"
    "new_val_ptr->keys = (%s**)allocate_pointers(new_val_ptr->max_elements);\n%s" /* 2 */
    "new_val_ptr->values = (%s**)allocate_pointers(new_val_ptr->max_elements);\n"
    "for (int elem_count = 0; elem_count < val_ptr->n_elements; elem_count++) {\n"
    "new_val_ptr->keys[elem_count] = new %s(*(val_ptr->keys[elem_count]));\n%s" /* 3 */
    "new_val_ptr->values[elem_count] = new %s(*(val_ptr->values[elem_count]));\n"
    "}\n"
    "clean_up();\n"
    "val_ptr = new_val_ptr;\n"
    "}\n"
    "if (key_idx == val_ptr->n_elements) set_size(key_idx + 1);\n"
    "if (new_key) {\n"
    "key_idx = 0;\n%s" /* 4 */
    "if (val_ptr->n_elements > 1) {\n"
    "int right_idx = val_ptr->n_elements - 2;\n"
    "while (key_idx < right_idx) {\n"
    "int middle_index = key_idx + (right_idx - key_idx) / 2;\n"
    "if (%s < %s) key_idx = middle_index + 1;\n" /* 5-6 */
    "else right_idx = middle_index;\n"
    "}\n"
    "if (%s < %s) ++key_idx;\n" /* 7-8 */
    "memmove(val_ptr->keys + key_idx + 1, val_ptr->keys + key_idx, "
      "(val_ptr->n_elements - key_idx - 1) * sizeof(*val_ptr->keys));\n%s" /* 9 */
    "memmove(val_ptr->values + key_idx + 1, val_ptr->values + key_idx, "
      "(val_ptr->n_elements - key_idx - 1) * sizeof(*val_ptr->values));\n"
    "}\n"
    "val_ptr->keys[key_idx] = new %s(key_value);\n%s" /* 10 */
    "val_ptr->values[key_idx] = new %s;\n"
    "}\n"
    "return *val_ptr->values[key_idx];\n"
    "}\n\n", value_name, name, key_name, dispname,
    int_keys ? "val_ptr->int_keys = NULL;\n" : "" /* 1 */, key_name,
    int_keys ? "new_val_ptr->int_keys = (INTEGER**)"
      "allocate_pointers(new_val_ptr->max_elements);\n" : "" /* 2 */,
    value_name, key_name, 
    int_keys ? "new_val_ptr->int_keys[elem_count] = "
      "new INTEGER(*(val_ptr->int_keys[elem_count]));\n" : "" /* 3 */,
    value_name,
    int_keys ? "INTEGER* key_value_int = get_int_from_key(key_value);\n" : "" /* 4 */,
    int_keys ? "*val_ptr->int_keys[middle_index]" : "*val_ptr->keys[middle_index]" /* 5 */,
    int_keys ? "*key_value_int" : "key_value" /* 6 */,
    int_keys ? "*val_ptr->int_keys[key_idx]" : "*val_ptr->keys[key_idx]" /* 7 */,
    int_keys ? "*key_value_int" : "key_value" /* 8 */,
    int_keys ? "memmove(val_ptr->int_keys + key_idx + 1, val_ptr->int_keys + key_idx, "
      "(val_ptr->n_elements - key_idx - 1) * sizeof(*val_ptr->int_keys));\n" : "" /* 9 */,
    key_name, int_keys ? "val_ptr->int_keys[key_idx] = key_value_int;\n" : "" /* 10 */,
    value_name);

  /* Const operator[] throws an error if over-indexing */
  def = mputprintf(def, "const %s& operator[](const %s& key_value) const;\n",
    value_name, key_name);
  src = mputprintf(src,
    "const %s& %s::operator[](const %s& key_value) const\n"
    "{\n"
    "if (val_ptr == NULL)\n"
    "TTCN_error(\"Indexing an unbound value of type %s.\");\n"
    "key_value.must_bound(\"Indexing with an unbound key in a value of type %s.\");\n"
    "int key_idx = find_key(key_value);\n"
    "if (key_idx == val_ptr->n_elements) {\n"
    "TTCN_error_begin(\"Key \");\n"
    "key_value.log();\n"
    "TTCN_Logger::log_event_str(\" not found in a value of type %s.\");\n"
    "TTCN_error_end();\n"
    "}\n"
    "return *val_ptr->values[key_idx];\n"
    "}\n\n", value_name, name, key_name, dispname, dispname, dispname);

  /* Helper function, that returns the index of a key, or the number of elements if not found */
  def = mputprintf(def, "int find_key(const %s& key_value) const;\n", key_name);
  src = mputprintf(src,
    "int %s::find_key(const %s& key_value) const\n"
    "{\n"
    "if (val_ptr == NULL || val_ptr->n_elements == 0) return 0;\n"
    "int left_idx = 0;\n"
    "int right_idx = val_ptr->n_elements - 1;\n%s"
    "while (left_idx < right_idx) {\n"
    "int middle_index = left_idx + (right_idx - left_idx) / 2;\n"
    "if (%s < %s) left_idx = middle_index + 1;\n"
    "else right_idx = middle_index;\n"
    "}\n%s"
    "if (*val_ptr->keys[left_idx] == key_value) return left_idx;\n"
    "return val_ptr->n_elements;\n"
    "}\n\n", name, key_name,
    int_keys ? "INTEGER* key_value_int = get_int_from_key(key_value);\n" : "",
    int_keys ? "*val_ptr->int_keys[middle_index]" : "*val_ptr->keys[middle_index]",
    int_keys ? "*key_value_int" : "key_value",
    int_keys ? "delete key_value_int;\n" : "");
  
  /* Helper function, that returns whether a key is present in the map
     (used by ispresent(mapvar[key]), isbound(mapvar[key]) and isvalue(mapvar[key])) */
  def = mputprintf(def, "boolean has_key(const %s& key_value) const;\n", key_name);
  src = mputprintf(src,
    "boolean %s::has_key(const %s& key_value) const\n"
    "{\n"
    "if (val_ptr == NULL) return FALSE;\n"
    "return find_key(key_value) != val_ptr->n_elements;\n"
    "}\n\n", name, key_name);
  
  /* unmap function (used by the unmap statement) */
  def = mputprintf(def, "void unmap(const %s& key_value);\n\n", key_name);
  src = mputprintf(src,
    "void %s::unmap(const %s& key_value)\n"
    "{\n"
    "if (val_ptr == NULL) TTCN_error(\"Unmapping a key of an unbound value of type %s.\");\n"
    "key_value.must_bound(\"Unmapping an unbound key in a value of type %s.\");\n"
    "int key_idx = find_key(key_value);\n"
    "if (key_idx != val_ptr->n_elements) {\n"
    "if (val_ptr->ref_count > 1) {\n"
    "struct ttcn3_map_struct *new_val_ptr = new ttcn3_map_struct;\n"
    "new_val_ptr->ref_count = 1;\n"
    "new_val_ptr->n_elements = val_ptr->n_elements - 1;\n"
    "new_val_ptr->max_elements = ttcn3_map_get_max_elements(new_val_ptr->n_elements);\n"
    "new_val_ptr->keys = (%s**)allocate_pointers(new_val_ptr->max_elements);\n%s"
    "new_val_ptr->values = (%s**)allocate_pointers(new_val_ptr->max_elements);\n"
    "for (int elem_count = 0; elem_count < key_idx; elem_count++) {\n"
    "new_val_ptr->keys[elem_count] = new %s(*(val_ptr->keys[elem_count]));\n%s"
    "new_val_ptr->values[elem_count] = new %s(*(val_ptr->values[elem_count]));\n"
    "}\n"
    "for (int elem_count = key_idx + 1; elem_count < val_ptr->n_elements; elem_count++) {\n"
    "new_val_ptr->keys[elem_count - 1] = new %s(*(val_ptr->keys[elem_count]));\n%s"
    "new_val_ptr->values[elem_count - 1] = new %s(*(val_ptr->values[elem_count]));\n"
    "}\n"
    "clean_up();\n"
    "val_ptr = new_val_ptr;\n"
    "}\n"
    "else {\n"
    "delete val_ptr->keys[key_idx];\n%s"
    "delete val_ptr->values[key_idx];\n"
    "--val_ptr->n_elements;\n"
    "memmove(val_ptr->keys + key_idx, val_ptr->keys + key_idx + 1, "
      "(val_ptr->n_elements - key_idx) * sizeof(*val_ptr->keys));\n%s"
    "memmove(val_ptr->values + key_idx, val_ptr->values + key_idx + 1, "
      "(val_ptr->n_elements - key_idx) * sizeof(*val_ptr->values));\n"
    "}\n"
    "}\n"
    "}\n\n", name, key_name, dispname, dispname, key_name,
    int_keys ? "new_val_ptr->int_keys = (INTEGER**)"
      "allocate_pointers(new_val_ptr->max_elements);\n" : "",
    value_name, key_name,
    int_keys ? "new_val_ptr->int_keys[elem_count] = "
      "new INTEGER(*(val_ptr->int_keys[elem_count]));\n" : "",
    value_name, key_name,
    int_keys ? "new_val_ptr->int_keys[elem_count - 1] = "
      "new INTEGER(*(val_ptr->int_keys[elem_count]));\n" : "",
    value_name, int_keys ? "delete val_ptr->int_keys[key_idx];\n" : "",
    int_keys ? "memmove(val_ptr->int_keys + key_idx, val_ptr->int_keys + key_idx + 1, "
      "(val_ptr->n_elements - key_idx) * sizeof(*val_ptr->int_keys));\n" : "");
  
  /* function that returns the current set of keys */
  def = mputprintf(def, "%s_from from();\n", name);
  src = mputprintf(src,
    "%s_from %s::from()\n"
    "{\n"
    "if (val_ptr == NULL) "
      "TTCN_error(\"Retrieving the set of keys from an unbound value of type %s.\");\n"
    "%s_from ret_val;\n"
    "ret_val.set_size(val_ptr->n_elements);\n"
    "for (int elem_count = 0; elem_count < val_ptr->n_elements; elem_count++)\n"
    "ret_val[elem_count] = *val_ptr->keys[elem_count];\n"
    "return ret_val;\n"
    "}\n\n", name, name, dispname, name);
  
  /* function that returns the current set of values */
  def = mputprintf(def, "%s_to to();\n", name);
  src = mputprintf(src,
    "%s_to %s::to()\n"
    "{\n"
    "if (val_ptr == NULL) "
      "TTCN_error(\"Retrieving the set of values from an unbound value of type %s.\");\n"
    "%s_to ret_val;\n"
    "ret_val.set_size(val_ptr->n_elements);\n"
    "for (int elem_count = 0; elem_count < val_ptr->n_elements; elem_count++)\n"
    "ret_val[elem_count] = *val_ptr->values[elem_count];\n"
    "return ret_val;\n"
    "}\n\n", name, name, dispname, name);

  /* set_size function */
  def = mputstr(def, "void set_size(int new_size);\n");
  src = mputprintf(src,
    "void %s::set_size(int new_size)\n"
    "{\n"
    "if (new_size < 0) "
    "TTCN_error(\"Internal error: Setting a negative size for a value of type %s.\");\n"
    "if (val_ptr != NULL && new_size < val_ptr->n_elements) clean_up();\n"
    "if (val_ptr == NULL) {\n"
    "val_ptr = new ttcn3_map_struct;\n"
    "val_ptr->ref_count = 1;\n"
    "val_ptr->n_elements = 0;\n"
    "val_ptr->max_elements = 0;\n"
    "val_ptr->keys = NULL;\n%s"
    "val_ptr->values = NULL;\n"
    "} else if (val_ptr->ref_count > 1) {\n"
    "struct ttcn3_map_struct *new_val_ptr = new ttcn3_map_struct;\n"
    "new_val_ptr->ref_count = 1;\n"
    "new_val_ptr->n_elements = val_ptr->n_elements;\n"
    "new_val_ptr->max_elements = val_ptr->max_elements;\n"
    "new_val_ptr->keys = (%s**)allocate_pointers(new_val_ptr->max_elements);\n%s"
    "new_val_ptr->values = (%s**)allocate_pointers(new_val_ptr->max_elements);\n"
    "for (int elem_count = 0; elem_count < new_val_ptr->n_elements; elem_count++) {\n"
    "new_val_ptr->keys[elem_count] = new %s(*(val_ptr->keys[elem_count]));\n%s"
    "new_val_ptr->values[elem_count] = new %s(*(val_ptr->values[elem_count]));\n"
    "}\n"
    "clean_up();\n"
    "val_ptr = new_val_ptr;\n"
    "}\n"
    "if (new_size > val_ptr->n_elements) {\n"
    "int old_max_elements = val_ptr->max_elements;\n"
    "val_ptr->max_elements = ttcn3_map_get_max_elements(new_size);\n"
    "if (val_ptr->max_elements > old_max_elements) {\n"
    "val_ptr->keys = (%s**)reallocate_pointers((void**)val_ptr->keys, "
      "old_max_elements, val_ptr->max_elements);\n%s"
    "val_ptr->values = (%s**)reallocate_pointers((void**)val_ptr->values, "
      "old_max_elements, val_ptr->max_elements);\n"
    "}\n"
    "val_ptr->n_elements = new_size;\n"
    "}\n"
    "}\n\n", name, dispname,
    int_keys ? "val_ptr->int_keys = NULL;\n" : "", key_name,
    int_keys ? "new_val_ptr->int_keys = (INTEGER**)"
      "allocate_pointers(new_val_ptr->max_elements);\n" : "",
    value_name, key_name,
    int_keys ? "new_val_ptr->int_keys[elem_count] = "
      "new INTEGER(*(val_ptr->int_keys[elem_count]));\n" : "",
    value_name, key_name,
    int_keys ? "val_ptr->int_keys = (INTEGER**)reallocate_pointers("
      "(void**)val_ptr->int_keys, old_max_elements, val_ptr->max_elements);\n" : "",
    value_name);

  /* is_bound function */
  def = mputstr(def,
    "inline boolean is_bound() const { return val_ptr != NULL; }\n");

  /* is_present function */
  def = mputstr(def,
    "inline boolean is_present() const { return is_bound(); }\n");

  /* is_value function */
  def = mputstr(def,
    "boolean is_value() const;\n");
  src = mputprintf(src,
    "boolean %s::is_value() const\n"
    "{\n"
    "if (val_ptr == NULL) return FALSE;\n"
    "for (int i = 0; i < val_ptr->n_elements; ++i) {\n"
    "if (!val_ptr->values[i]->is_value()) return FALSE;\n"
    "}\n"
    "return TRUE;\n"
    "}\n\n", name);

  /* sizeof operation */
  def = mputstr(def,
    "int size_of() const;\n"
    "int n_elem() const { return size_of(); }\n");
  src = mputprintf(src,
    "int %s::size_of() const\n"
    "{\n"
    "if (val_ptr == NULL) "
    "TTCN_error(\"Performing sizeof operation on an unbound value of type %s.\");\n"
    "return val_ptr->n_elements;\n"
    "}\n\n", name, dispname);

  /* lengthof operation */
  def = mputstr(def, "int lengthof() const;\n");
  src = mputprintf(src,
    "int %s::lengthof() const\n"
    "{\n"
    "if (val_ptr == NULL) "
    "TTCN_error(\"Performing lengthof operation on an unbound value of type %s.\");\n"
    "return val_ptr->n_elements;\n"
    "}\n\n", name, dispname);

  /* log function */
  def = mputstr(def, "void log() const;\n");
  src = mputprintf(src,
    "void %s::log() const\n"
    "{\n"
    "if (val_ptr == NULL) {\n"
    "TTCN_Logger::log_event_unbound();\n"
    "return;\n"
    "}\n"
    "if (val_ptr->n_elements == 0) {\n"
    "TTCN_Logger::log_event_str(\"{ }\");\n"
    "}\n"
    "else {\n"
    "TTCN_Logger::log_event_str(\"{ \");\n"
    "for (int elem_count = 0; elem_count < val_ptr->n_elements; elem_count++) {\n"
    "if (elem_count > 0) TTCN_Logger::log_event_str(\", \");\n"
    "TTCN_Logger::log_event_str(\"[ \");\n"
    "val_ptr->keys[elem_count]->log();\n" /* todo: sensitive data? */
    "TTCN_Logger::log_event_str(\" ] := \");\n"
    "val_ptr->values[elem_count]->log();\n" /* todo: sensitive data? */
    "}\n"
    "TTCN_Logger::log_event_str(\" }\");\n"
    "}\n"
    "}\n\n", name);

  /* set implicit omit function, recursive */
  def = mputstr(def, "void set_implicit_omit();\n");
  src = mputprintf(src,
    "void %s::set_implicit_omit()\n"
    "{\n"
    "if (val_ptr == NULL) return;\n"
    "for (int i = 0; i < val_ptr->n_elements; i++) {\n"
    "val_ptr->values[i]->set_implicit_omit();\n" /* todo: implicit omit for keys? */
    "}\n"
    "}\n\n", name);

  if (!sdef->containsClass) {
    /* set_param function */
    def = mputstr(def, "void set_param(Module_Param& param);\n");
    src = mputprintf(src,
      "void %s::set_param(Module_Param& param)\n"
      "{\n"
      /* todo */
      "}\n\n", name);

    /* encoding / decoding functions */
    def = mputstr(def, "void encode_text(Text_Buf& text_buf) const;\n");
    src = mputprintf(src,
      "void %s::encode_text(Text_Buf& text_buf) const\n"
      "{\n"
      "if (val_ptr == NULL) "
      "TTCN_error(\"Text encoder: Encoding an unbound value of type %s.\");\n"
      "text_buf.push_int(val_ptr->n_elements);\n"
      "for (int elem_count = 0; elem_count < val_ptr->n_elements; elem_count++) {\n"
      "val_ptr->keys[elem_count]->encode_text(text_buf);\n"
      "val_ptr->values[elem_count]->encode_text(text_buf);\n"
      "}\n"
      "}\n\n", name, dispname);

    def = mputstr(def, "void decode_text(Text_Buf& text_buf);\n");
    src = mputprintf(src,
      "void %s::decode_text(Text_Buf& text_buf)\n"
      "{\n"
      "clean_up();\n"
      "val_ptr = new ttcn3_map_struct;\n"
      "val_ptr->ref_count = 1;\n"
      "val_ptr->n_elements = text_buf.pull_int().get_val();\n"
      "if (val_ptr->n_elements < 0) TTCN_error(\"Text decoder: Negative size "
      "was received for a value of type %s.\");\n"
      "val_ptr->max_elements = ttcn3_map_get_max_elements(val_ptr->n_elements);\n"
      "val_ptr->keys = (%s**)allocate_pointers(val_ptr->max_elements);\n"
      "val_ptr->values = (%s**)allocate_pointers(val_ptr->max_elements);\n"
      "for (int elem_count = 0; elem_count < val_ptr->n_elements; elem_count++) {\n"
      "val_ptr->keys[elem_count] = new %s;\n"
      "val_ptr->keys[elem_count]->decode_text(text_buf);\n"
      "val_ptr->values[elem_count] = new %s;\n"
      "val_ptr->values[elem_count]->decode_text(text_buf);\n"
      "}\n"
      "}\n\n", name, dispname, key_name, value_name, key_name, value_name);
  }
  if (use_runtime_2) {
    // implement abstract functions inherited by Base_Type (todo)
    def = mputstr(def,
      "  Module_Param* get_param(Module_Param_Name&) const { return NULL; }\n"
      "  void encode_text(Text_Buf&) const { }\n"
      "  void decode_text(Text_Buf&) { }\n"
      "  boolean is_equal(const Base_Type*) const { return FALSE; }\n"
      "  void set_value(const Base_Type*) { }\n"
      "  Base_Type* clone() const { return NULL; }\n"
      "  const TTCN_Typedescriptor_t* get_descriptor() const { return NULL; }\n\n");
  }

  /* end of class */
  def = mputstr(def, "};\n\n");

  output->header.class_decls = mputprintf(output->header.class_decls,
                                          "class %s;\n", name);
  output->header.class_defs = mputstr(output->header.class_defs, def);
  Free(def);
  output->source.methods = mputstr(output->source.methods, src);
  Free(src);
  /* Copied from record.c.  */
  output->header.function_prototypes =
    mputprintf(output->header.function_prototypes,
    "extern boolean operator==(null_type null_value, const %s& "
      "other_value);\n", name);
  output->source.function_bodies =
    mputprintf(output->source.function_bodies,
    "boolean operator==(null_type, const %s& other_value)\n"
    "{\n"
    "if (other_value.val_ptr == NULL)\n"
    "TTCN_error(\"The right operand of comparison is an unbound value of type %s.\");\n"
    "return other_value.val_ptr->n_elements == 0;\n"
    "}\n\n", name, dispname);

  output->header.function_prototypes =
    mputprintf(output->header.function_prototypes,
    "inline boolean operator!=(null_type null_value, const %s& other_value) "
    "{ return !(null_value == other_value); }\n", name);
}

/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */

void defTtcn3MapTemplate(const ttcn3_map_def *sdef, output_struct *output)
{
  char* def = NULL;
  char* src = NULL;
  const char* name = sdef->name;
  const char* dispname = sdef->dispname;
  const char* key_name = sdef->key_name;
  const char* value_name_temp = sdef->value_name_temp;
  /* true if the key type is not comparable, so extra integer keys are used and cached */
  boolean int_keys = sdef->key_to_int_converter != NULL; 

  /* Class definition and private data members */
  def = mputprintf(def,
    "class %s_template : public Restricted_Length_Template {\n"
    "union {\n"
    "struct {\n"
    "int n_elements;\n"
    "int max_elements;\n"
    "%s** keys;\n%s"
    "%s** values;\n"
    "BOOLEAN allow_others;\n" /* indicates that there's a * at the start of the template list */
    "} single_value;\n"
    "struct {\n"
    "unsigned int n_values;\n"
    "%s_template *list_value;\n"
    "} value_list;\n"
    "struct {\n"
    "%s_template* precondition;\n"
    "%s_template* implied_template;\n"
    "} implication_;\n"
    "dynmatch_struct<%s>* dyn_match;\n"
    "};\n", name, key_name, int_keys ? "INTEGER** int_keys;\n" : "",
    value_name_temp, name, name, name, name);

  /* private member functions */

  if (!sdef->containsClass) {
    /* copy_value function */
    def = mputprintf(def, "void copy_value(const %s& other_value);\n", name);
    src = mputprintf(src,
      "void %s_template::copy_value(const %s& other_value)\n"
      "{\n"
      "if (!other_value.is_bound()) "
      "TTCN_error(\"Initialization of a template of type %s with an unbound value.\");\n"
      "single_value.n_elements = other_value.size_of();\n"
      "single_value.max_elements = ttcn3_map_get_max_elements(single_value.n_elements);\n"
      "single_value.keys = (%s**)allocate_pointers(single_value.max_elements);\n%s"
      "single_value.values = (%s**)allocate_pointers(single_value.max_elements);\n"
      "for (int elem_count = 0; elem_count < single_value.n_elements; elem_count++) {\n"
      "single_value.keys[elem_count] = new %s(*other_value.val_ptr->keys[elem_count]);\n%s"
      "single_value.values[elem_count] = new %s(*other_value.val_ptr->values[elem_count]);\n"
      "}\n"
      "single_value.allow_others = FALSE;\n"
      "set_selection(SPECIFIC_VALUE);\n"
      "}\n\n", name, name, dispname, key_name,
      int_keys ? "single_value.int_keys = (INTEGER**)"
        "allocate_pointers(single_value.max_elements);\n" : "",
      value_name_temp, key_name,
      int_keys ? "single_value.int_keys[elem_count] = "
        "new INTEGER(*other_value.val_ptr->int_keys[elem_count]);\n" : "",
      value_name_temp);
  }

  /* copy_template function */
  def = mputprintf(def, "void copy_template(const %s_template& other_value);\n", name);
  src = mputprintf(src,
    "void %s_template::copy_template(const %s_template& other_value)\n"
    "{\n"
    "switch (other_value.template_selection) {\n"
    "case SPECIFIC_VALUE:\n"
    "single_value.n_elements = other_value.single_value.n_elements;\n"
    "single_value.max_elements = other_value.single_value.max_elements;\n"
    "single_value.keys = (%s**)allocate_pointers(single_value.max_elements);\n%s"
    "single_value.values = (%s**)allocate_pointers(single_value.max_elements);\n"
    "for (int elem_count = 0; elem_count < single_value.n_elements; elem_count++) {\n"
    "single_value.keys[elem_count] = new %s(*other_value.single_value.keys[elem_count]);\n%s"
    "single_value.values[elem_count] = new %s(*other_value.single_value.values[elem_count]);\n"
    "}\n"
    "single_value.allow_others = other_value.single_value.allow_others;\n"
    "break;\n"
    "case OMIT_VALUE:\n"
    "case ANY_VALUE:\n"
    "case ANY_OR_OMIT:\n"
    "break;\n"
    "case VALUE_LIST:\n"
    "case COMPLEMENTED_LIST:\n"
    "case CONJUNCTION_MATCH:\n"
    "value_list.n_values = other_value.value_list.n_values;\n"
    "value_list.list_value = new %s_template[value_list.n_values];\n"
    "for (unsigned int list_count = 0; list_count < value_list.n_values; list_count++)\n"
    "value_list.list_value[list_count].copy_template(other_value.value_list.list_value[list_count]);\n"
    "break;\n"
    "case IMPLICATION_MATCH:\n"
    "implication_.precondition = new %s_template(*other_value.implication_.precondition);\n"
    "implication_.implied_template = new %s_template(*other_value.implication_.implied_template);\n"
    "break;\n"
    "case DYNAMIC_MATCH:\n"
    "dyn_match = other_value.dyn_match;\n"
    "dyn_match->ref_count++;\n"
    "break;\n"
    "default:\n"
    "TTCN_error(\"Copying an uninitialized/unsupported template of type %s.\");\n"
    "break;\n"
    "}\n"
    "set_selection(other_value);\n"
    "}\n\n", name, name, key_name,
    int_keys ? "single_value.int_keys = (INTEGER**)"
      "allocate_pointers(single_value.max_elements);\n" : "",
    value_name_temp, key_name,
    int_keys ? "single_value.int_keys[elem_count] = "
        "new INTEGER(*other_value.single_value.int_keys[elem_count]);\n" : "",
    value_name_temp, name, name, name, dispname);
  
  /* public member functions */
  def = mputstr(def, "\npublic:\n");

  /* constructors */
  def = mputprintf(def, "%s_template();\n", name);
  src = mputprintf(src, "%s_template::%s_template()\n"
    "{\n"
    "}\n\n", name, name);

  def = mputprintf(def, "%s_template(template_sel other_value);\n", name);
  src = mputprintf(src, "%s_template::%s_template(template_sel other_value)\n"
    ": Restricted_Length_Template(other_value)\n"
    "{\n"
    "check_single_selection(other_value);\n"
    "}\n\n", name, name);

  def = mputprintf(def, "%s_template(null_type other_value);\n", name);
  src = mputprintf(src, "%s_template::%s_template(null_type)\n"
    ": Restricted_Length_Template(SPECIFIC_VALUE)\n"
    "{\n"
    "single_value.n_elements = 0;\n"
    "single_value.max_elements = 0;\n"
    "single_value.keys = NULL;\n%s"
    "single_value.values = NULL;\n"
    "single_value.allow_others = FALSE;\n"
    "}\n\n", name, name, int_keys ? "single_value.int_keys = NULL;\n" : "");

  if (!sdef->containsClass) {
    def = mputprintf(def, "%s_template(const %s& other_value);\n", name, name);
    src = mputprintf(src, "%s_template::%s_template(const %s& other_value)\n"
      "{\n"
      "copy_value(other_value);\n"
      "}\n\n", name, name, name);

    def = mputprintf(def, "%s_template(const OPTIONAL<%s>& other_value);\n",
      name, name);
    src = mputprintf(src,
      "%s_template::%s_template(const OPTIONAL<%s>& other_value)\n"
      "{\n"
      "switch (other_value.get_selection()) {\n"
      "case OPTIONAL_PRESENT:\n"
      "copy_value((const %s&)other_value);\n"
      "break;\n"
      "case OPTIONAL_OMIT:\n"
      "set_selection(OMIT_VALUE);\n"
      "break;\n"
      "default:\n"
      "TTCN_error(\"Creating a template of type %s from an unbound optional field.\");\n"
      "}\n"
      "}\n\n", name, name, name, name, dispname);
  }

  def = mputprintf(def, "%s_template(%s_template* p_precondition, "
    "%s_template* p_implied_template);\n", name, name, name);
  src = mputprintf(src, "%s_template::%s_template(%s_template* p_precondition, "
    "%s_template* p_implied_template)\n"
    ": Restricted_Length_Template(IMPLICATION_MATCH)\n"
    "{\n"
    "implication_.precondition = p_precondition;\n"
    "implication_.implied_template = p_implied_template;\n"
    "}\n\n", name, name, name, name);

  def = mputprintf(def, "%s_template(Dynamic_Match_Interface<%s>* p_dyn_match);\n", name, name);
  src = mputprintf(src, "%s_template::%s_template(Dynamic_Match_Interface<%s>* p_dyn_match)\n"
    ": Restricted_Length_Template(DYNAMIC_MATCH)\n"
    "{\n"
    "dyn_match = new dynmatch_struct<%s>;\n"
    "dyn_match->ptr = p_dyn_match;\n"
    "dyn_match->ref_count = 1;\n"
    "}\n\n", name, name, name, name);

  /* copy constructor */
  def = mputprintf(def, "%s_template(const %s_template& other_value);\n", name, name);
  src = mputprintf(src,
    "%s_template::%s_template(const %s_template& other_value)\n"
    ": Restricted_Length_Template()\n"
    "{\n"
    "copy_template(other_value);\n"
    "}\n\n", name, name, name);

  /* destructor */
  def = mputprintf(def, "~%s_template();\n\n", name);
  src = mputprintf(src,
    "%s_template::~%s_template()\n"
    "{\n"
    "clean_up();\n"
    "}\n\n", name, name);

  /* clean_up function */
  def = mputstr(def, "void clean_up();\n");
  src = mputprintf(src,
    "void %s_template::clean_up()\n"
    "{\n"
    "switch (template_selection) {\n"
    "case SPECIFIC_VALUE:\n"
    "for (int elem_count = 0; elem_count < single_value.n_elements; elem_count++) {\n"
    "delete single_value.keys[elem_count];\n%s"
    "delete single_value.values[elem_count];\n"
    "}\n"
    "free_pointers((void**)single_value.keys);\n%s"
    "free_pointers((void**)single_value.values);\n"
    "break;\n"
    "case VALUE_LIST:\n"
    "case COMPLEMENTED_LIST:\n"
    "case CONJUNCTION_MATCH:\n"
    "delete [] value_list.list_value;\n"
    "break;\n"
    "case IMPLICATION_MATCH:\n"
    "delete implication_.precondition;\n"
    "delete implication_.implied_template;\n"
    "break;\n"
    "case DYNAMIC_MATCH:\n"
    "dyn_match->ref_count--;\n"
    "if (dyn_match->ref_count == 0) {\n"
    "delete dyn_match->ptr;\n"
    "delete dyn_match;\n"
    "}\n"
    "break;\n"
    "default:\n"
    "break;\n"
    "}\n"
    "template_selection = UNINITIALIZED_TEMPLATE;\n"
    "}\n\n", name, int_keys ? "delete single_value.int_keys[elem_count];\n" : "",
    int_keys ? "free_pointers((void**)single_value.int_keys);\n" : "");

  /* assignment operators */
  def = mputprintf(def, "%s_template& operator=(template_sel other_value);\n",
    name);
  src = mputprintf(src,
    "%s_template& %s_template::operator=(template_sel other_value)\n"
    "{\n"
    "check_single_selection(other_value);\n"
    "clean_up();\n"
    "set_selection(other_value);\n"
    "return *this;\n"
    "}\n\n", name, name);

  def = mputprintf(def, "%s_template& operator=(null_type other_value);\n",
    name);
  src = mputprintf(src,
    "%s_template& %s_template::operator=(null_type)\n"
    "{\n"
    "clean_up();\n"
    "set_selection(SPECIFIC_VALUE);\n"
    "single_value.n_elements = 0;\n"
    "single_value.max_elements = 0;\n"
    "single_value.keys = NULL;\n%s"
    "single_value.values = NULL;\n"
    "single_value.allow_others = FALSE;\n"
    "return *this;\n"
    "}\n\n", name, name, int_keys ? "single_value.int_keys = NULL;\n" : "");

  if (!sdef->containsClass) {
    def = mputprintf(def, "%s_template& operator=(const %s& other_value);\n",
      name, name);
    src = mputprintf(src,
      "%s_template& %s_template::operator=(const %s& other_value)\n"
      "{\n"
      "clean_up();\n"
      "copy_value(other_value);\n"
      "return *this;\n"
      "}\n\n", name, name, name);

    def = mputprintf(def, "%s_template& operator=(const OPTIONAL<%s>& other_value);\n",
      name, name);
    src = mputprintf(src,
      "%s_template& %s_template::operator=(const OPTIONAL<%s>& other_value)\n"
      "{\n"
      "clean_up();\n"
      "switch (other_value.get_selection()) {\n"
      "case OPTIONAL_PRESENT:\n"
      "copy_value((const %s&)other_value);\n"
      "break;\n"
      "case OPTIONAL_OMIT:\n"
      "set_selection(OMIT_VALUE);\n"
      "break;\n"
      "default:\n"
      "TTCN_error(\"Assignment of an unbound optional field to a template of type %s.\");\n"
      "}\n"
      "return *this;\n"
      "}\n\n", name, name, name, name, dispname);
  }

  def = mputprintf(def, "%s_template& operator=(const %s_template& other_value);\n\n",
    name, name);
  src = mputprintf(src,
    "%s_template& %s_template::operator=(const %s_template& other_value)\n"
    "{\n"
    "if (&other_value != this) {\n"
    "clean_up();\n"
    "copy_template(other_value);\n"
    "}\n"
    "return *this;\n"
    "}\n\n", name, name, name);

  /* indexing operators */
  /* Non-const operator[] is allowed to extend the map or set the template to specific value */
  def = mputprintf(def, "%s& operator[](const %s& key_value);\n",
    value_name_temp, key_name);
  src = mputprintf(src,
    "%s& %s_template::operator[](const %s& key_value)\n"
    "{\n"
    "key_value.must_bound(\"Using an unbound key for indexing a template of type %s.\");\n"
    "int key_idx;\n"
    "switch (template_selection)\n"
    "{\n"
    "case SPECIFIC_VALUE:\n"
    "key_idx = find_key(key_value);\n"
    "if (key_idx == single_value.n_elements) {\n"
    "set_size(single_value.n_elements + 1);\n"
    "key_idx = 0;\n%s%s%s" /* 1-3 */
    "if (single_value.n_elements > 1) {\n"
    "int right_idx = single_value.n_elements - 2;\n"
    "while (key_idx < right_idx) {\n"
    "int middle_index = key_idx + (right_idx - key_idx) / 2;\n"
    "if (%s < %s) key_idx = middle_index + 1;\n" /* 4-5 */
    "else right_idx = middle_index;\n"
    "}\n"
    "if (%s < %s) ++key_idx;\n" /* 6-7 */
    "memmove(single_value.keys + key_idx + 1, single_value.keys + key_idx, "
      "(single_value.n_elements - key_idx - 1) * sizeof(*single_value.keys));\n%s" /* 8 */
    "memmove(single_value.values + key_idx + 1, single_value.values + key_idx, "
      "(single_value.n_elements - key_idx - 1) * sizeof(*single_value.values));\n"
    "}\n"
    "single_value.keys[key_idx] = new %s(key_value);\n%s" /* 9 */
    "single_value.values[key_idx] = new %s;\n"
    "}\n"
    "break;\n"
    "case OMIT_VALUE:\n"
    "case ANY_VALUE:\n"
    "case ANY_OR_OMIT:\n"
    "case UNINITIALIZED_TEMPLATE:\n"
    "key_idx = 0;\n"
    "set_size(1);\n"
    "single_value.keys[0] = new %s(key_value);\n%s%s%s" /* 10-12 */
    "single_value.values[0] = new %s;\n"
    /* current plan:
    ["a"] := { 1, "a" } will result in:
    { ["a"] := { 1, "a" } }, if the template was uninitialized or omit
    { *, ["a"] := { 1, "a" } }, if the template was ? or * (i.e. we're restricting the template) */
    "single_value.allow_others = template_selection == ANY_VALUE || template_selection == ANY_OR_OMIT;\n"
    "break;\n"
    "default:\n"
    "TTCN_error(\"Accessing an element of a non-specific template for type %s.\");\n"
    "break;\n"
    "}\n"
    "return *single_value.values[key_idx];\n"
    "}\n\n", value_name_temp, name, key_name, dispname,
    int_keys ? "INTEGER* key_value_int = " : "" /* 1 */,
    int_keys ? name : "" /* 2 */,
    int_keys ? "::get_int_from_key(key_value);\n" : "" /* 3 */,
    int_keys ? "*single_value.int_keys[middle_index]" : "*single_value.keys[middle_index]" /* 4 */,
    int_keys ? "*key_value_int" : "key_value" /* 5 */,
    int_keys ? "*single_value.int_keys[key_idx]" : "single_value.keys[key_idx]" /* 6 */,
    int_keys ? "*key_value_int" : "key_value" /* 7 */,
    int_keys ? "memmove(single_value.int_keys + key_idx + 1, single_value.int_keys + key_idx, "
      "(single_value.n_elements - key_idx - 1) * sizeof(*single_value.int_keys));\n" : "" /* 8 */,
    key_name, int_keys ? "single_value.int_keys[key_idx] = key_value_int;\n" : "" /* 9 */,
    value_name_temp, key_name,
    int_keys ? "single_value.int_keys[0] = " : "" /* 10 */,
    int_keys ? name : "" /* 11 */,
    int_keys ? "::get_int_from_key(key_value);\n" : "" /* 12 */,
    value_name_temp, dispname);

  /* Const operator[] throws an error if over-indexing or if the template is not a specific value */
  def = mputprintf(def, "const %s& operator[](const %s& key_value) const;\n",
    value_name_temp, key_name);
  src = mputprintf(src,
    "const %s& %s_template::operator[](const %s& key_value) const\n"
    "{\n"
    "key_value.must_bound(\"Using an unbound key for indexing a template of type %s.\");\n"
    "if (template_selection != SPECIFIC_VALUE) TTCN_error(\"Indexing a non-specific template of type %s.\");\n"
    "int key_idx = find_key(key_value);\n"
    "if (key_idx == single_value.n_elements) {\n"
    "TTCN_error_begin(\"Key \");\n"
    "key_value.log();\n"
    "TTCN_Logger::log_event_str(\" not found in a template of type %s.\");\n"
    "TTCN_error_end();\n"
    "}\n"
    "return *single_value.values[key_idx];\n"
    "}\n\n", value_name_temp, name, key_name, dispname, dispname, dispname);
  
  /* Non-const allow others flag is allowed to set the template to specific value
     (this is the C++ equivalent of 'maptemp[*]') */
  def = mputstr(def, "BOOLEAN& allow_others();\n");
  src = mputprintf(src,
    "BOOLEAN& %s_template::allow_others()\n"
    "{\n"
    "switch (template_selection)\n"
    "{\n"
    "case SPECIFIC_VALUE:\n"
    "break;\n"
    "case OMIT_VALUE:\n"
    "case ANY_VALUE:\n"
    "case ANY_OR_OMIT:\n"
    "case UNINITIALIZED_TEMPLATE:\n"
    "*this = NULL_VALUE;\n"
    "break;\n"
    "default:\n"
    "TTCN_error(\"Accessing an element of a non-specific template for type %s.\");\n"
    "break;\n"
    "}\n"
    "return single_value.allow_others;\n"
    "}\n\n", name, dispname);
  
  /* Const allow others flag throws an error if the template is not a specific value
     (this is the C++ equivalent of 'maptemp[*]') */
  def = mputstr(def, "const BOOLEAN& allow_others() const;\n");
  src = mputprintf(src,
    "const BOOLEAN& %s_template::allow_others() const\n"
    "{\n"
    "if (template_selection != SPECIFIC_VALUE) "
      "TTCN_error(\"Indexing a non-specific template of type %s.\");\n"
    "return single_value.allow_others;\n"
    "}\n\n", name, dispname);
  
  /* Helper function, that returns the index of a key, or the number of elements if not found */
  def = mputprintf(def, "int find_key(const %s& key_value) const;\n", key_name);
  src = mputprintf(src,
    "int %s_template::find_key(const %s& key_value) const\n"
    "{\n"
    "if (template_selection != SPECIFIC_VALUE) "
      "TTCN_error(\"Internal error: searching for a key in a non-specific template of type %s.\");\n"
    "int left_idx = 0;\n"
    "int right_idx = single_value.n_elements - 1;\n%s%s%s"
    "while (left_idx < right_idx) {\n"
    "int middle_index = left_idx + (right_idx - left_idx) / 2;\n"
    "if (%s < %s) left_idx = middle_index + 1;\n"
    "else right_idx = middle_index;\n"
    "}\n%s"
    "if (*single_value.keys[left_idx] == key_value) return left_idx;\n"
    "return single_value.n_elements;\n"
    "}\n\n", name, key_name, dispname,
    int_keys ? "INTEGER* key_value_int = " : "",
    int_keys ? name : "",
    int_keys ? "::get_int_from_key(key_value);\n" : "",
    int_keys ? "*single_value.int_keys[middle_index]" : "*single_value.keys[middle_index]",
    int_keys ? "*key_value_int" : "key_value",
    int_keys ? "delete key_value_int;\n" : "");
  
  /* Helper function, that returns whether a key is present in the map
     (used by ispresent(mapvar[key]), isbound(mapvar[key]) and isvalue(mapvar[key])) */
  def = mputprintf(def, "boolean has_key(const %s& key_value) const;\n", key_name);
  src = mputprintf(src,
    "boolean %s_template::has_key(const %s& key_value) const\n"
    "{\n"
    "if (template_selection != SPECIFIC_VALUE) return FALSE;\n"
    "return find_key(key_value) != single_value.n_elements;\n"
    "}\n\n", name, key_name);
  
  /* unmap function (used by the unmap statement) */
  def = mputprintf(def, "void unmap(const %s& key_value);\n\n", key_name);
  src = mputprintf(src,
    "void %s_template::unmap(const %s& key_value)\n"
    "{\n"
    "if (template_selection != SPECIFIC_VALUE) "
      "TTCN_error(\"Unmapping a key of an non-specific template of type %s.\");\n"
    "key_value.must_bound(\"Unmapping an unbound key in a value of type %s.\");\n"
    "int key_idx = find_key(key_value);\n"
    "if (key_idx != single_value.n_elements) {\n"
    "delete single_value.keys[key_idx];\n%s"
    "delete single_value.values[key_idx];\n"
    "--single_value.n_elements;\n"
    "memmove(single_value.keys + key_idx, single_value.keys + key_idx + 1, "
      "(single_value.n_elements - key_idx) * sizeof(*single_value.keys));\n%s"
    "memmove(single_value.values + key_idx, single_value.values + key_idx + 1, "
      "(single_value.n_elements - key_idx) * sizeof(*single_value.values));\n"
    "}\n"
    "}\n\n", name, key_name, dispname, dispname,
    int_keys ? "delete single_value.int_keys[key_idx];\n" : "",
    int_keys ? "memmove(single_value.int_keys + key_idx, single_value.int_keys + key_idx + 1, "
      "(single_value.n_elements - key_idx) * sizeof(*single_value.int_keys));\n" : "");
  
  /* function that returns the current set of keys */
  def = mputprintf(def, "%s_from from();\n", name);
  src = mputprintf(src,
    "%s_from %s_template::from()\n"
    "{\n"
    "if (template_selection != SPECIFIC_VALUE) "
      "TTCN_error(\"Retrieving the set of keys from a non-specific template of type %s.\");\n"
    "%s_from ret_val;\n"
    "ret_val.set_size(single_value.n_elements);\n"
    "for (int elem_count = 0; elem_count < single_value.n_elements; elem_count++)\n"
    "ret_val[elem_count] = *single_value.keys[elem_count];\n"
    "return ret_val;\n"
    "}\n\n", name, name, dispname, name);
  
  /* function that returns the current set of values */
  def = mputprintf(def, "%s_to_template to();\n", name);
  src = mputprintf(src,
    "%s_to_template %s_template::to()\n"
    "{\n"
    "if (template_selection != SPECIFIC_VALUE) "
      "TTCN_error(\"Retrieving the set of values from a non-specific template of type %s.\");\n"
    "%s_to_template ret_val;\n"
    "ret_val.set_size(single_value.n_elements);\n"
    "for (int elem_count = 0; elem_count < single_value.n_elements; elem_count++)\n"
    "ret_val[elem_count] = *single_value.values[elem_count];\n"
    "return ret_val;\n"
    "}\n\n", name, name, dispname, name);

  /* set_size function */
  def = mputstr(def, "void set_size(int new_size);\n");
  src = mputprintf(src,
    "void %s_template::set_size(int new_size)\n"
    "{\n"
    "if (new_size < 0) TTCN_error(\"Internal error: Setting a negative size "
      "for a template of type %s.\");\n"
    "template_sel old_selection = template_selection;\n"
    "if (old_selection != SPECIFIC_VALUE || new_size < single_value.n_elements) {\n"
    "clean_up();\n"
    "set_selection(SPECIFIC_VALUE);\n"
    "single_value.n_elements = 0;\n"
    "single_value.max_elements = 0;\n"
    "single_value.keys = NULL;\n%s"
    "single_value.values = NULL;\n"
    "}\n"
    "if (new_size > single_value.n_elements) {\n"
    "int old_max_elements = single_value.max_elements;\n"
    "single_value.max_elements = ttcn3_map_get_max_elements(new_size);\n"
    "if (single_value.max_elements > old_max_elements) {\n"
    "single_value.keys = (%s**)reallocate_pointers((void**)single_value.keys, "
      "old_max_elements, single_value.max_elements);\n%s"
    "single_value.values = (%s**)reallocate_pointers((void**)single_value.values, "
      "old_max_elements, single_value.max_elements);\n"
    "}\n"
    "single_value.n_elements = new_size;\n"
    "}\n"
    "}\n\n", name, dispname,
    int_keys ? "single_value.int_keys = NULL;\n" : "", key_name,
    int_keys ? "single_value.int_keys = (INTEGER**)reallocate_pointers("
      "(void**)single_value.int_keys, old_max_elements, single_value.max_elements);\n" : "",
    value_name_temp);

  /* raw length */
  def = mputstr(def, "int n_elem() const;\n");
  src = mputprintf(src,
    "int %s_template::n_elem() const\n"
    "{\n"
    "  switch (template_selection) {\n"
    "  case SPECIFIC_VALUE:\n"
    "    return single_value.n_elements;\n"
    "    break;\n"
    "  case VALUE_LIST:\n"
    "    return value_list.n_values;\n"
    "    break;\n"
    "  default:\n"
    "    TTCN_error(\"Performing n_elem\");\n"
    "  }\n"
    "}\n\n", name);

  /* sizeof operation */
  def = mputstr(def,
    "int size_of(boolean is_size) const;\n"
    "inline int size_of() const { return size_of(TRUE); }\n"
    "inline int lengthof() const { return size_of(FALSE); }\n"
  );
  src = mputprintf(src,
    "int %s_template::size_of(boolean is_size) const\n"
    "{\n"
    /* this might work differently for map templates... (mostly copied from a record of template) */
    "const char* op_name = is_size ? \"size\" : \"length\";\n"
    "int min_size;\n"
    "boolean has_any_or_none;\n"
    "if (is_ifpresent) TTCN_error(\"Performing %%sof() operation on a "
      "template of type %s which has an ifpresent attribute.\", op_name);\n"
    "switch (template_selection)\n"
    "{\n"
    "case SPECIFIC_VALUE: {\n"
    "  min_size = 0;\n"
    "  has_any_or_none = single_value.allow_others;\n"
    "  for (int i = 0; i < single_value.n_elements; i++) {\n"
    "    if (single_value.values[i]->get_selection() == OMIT_VALUE)\n"
    "      TTCN_error(\"Performing %%sof() operation on a template of type %s "
      "containing omit element.\", op_name);\n"
    "    else min_size++;\n"
    "  }\n"
    "} break;\n"
    "case OMIT_VALUE:\n"
    "  TTCN_error(\"Performing %%sof() operation on a template of type %s "
      "containing omit value.\", op_name);\n"
    "case ANY_VALUE:\n"
    "case ANY_OR_OMIT:\n"
    "  min_size = 0;\n"
    "  has_any_or_none = TRUE;\n"
    "  break;\n"
    "case VALUE_LIST:\n"
    "{\n"
    "  if (value_list.n_values < 1)\n"
    "    TTCN_error(\"Performing %%sof() operation on a "
      "template of type %s containing an empty list.\", op_name);\n"
    "  int item_size = value_list.list_value[0].size_of(is_size);\n"
    "  for (unsigned int i = 1; i < value_list.n_values; i++) {\n"
    "    if (value_list.list_value[i].size_of(is_size)!=item_size)\n"
    "      TTCN_error(\"Performing %%sof() operation on a template of type "
      "%s containing a value list with different sizes.\", op_name);\n"
    "  }\n"
    "  min_size = item_size;\n"
    "  has_any_or_none = FALSE;\n"
    "  break;\n"
    "}\n"
    "case COMPLEMENTED_LIST:\n"
    "  TTCN_error(\"Performing %%sof() operation on a template of type %s "
      "containing complemented list.\", op_name);\n"
    "default:\n"
    "  TTCN_error(\"Performing %%sof() operation on an "
      "uninitialized/unsupported template of type %s.\", op_name);\n"
    "}\n"
    "return check_section_is_single(min_size, has_any_or_none, "
      "op_name, \"a\", \"template of type %s\");\n"
    "}\n\n",
    name, dispname, dispname, dispname, dispname, dispname, dispname, dispname, dispname);

  /* match operation */
  def = mputprintf(def, "boolean match(const %s& other_value, boolean legacy "
    "= FALSE) const;\n", name);
  src = mputprintf(src,
    "boolean %s_template::match(const %s& other_value, boolean legacy) const\n"
    "{\n"
    "if (!other_value.is_bound()) return FALSE;\n"
    "int value_length = other_value.size_of();\n"
    "if (!match_length(value_length)) return FALSE;\n"
    "switch (template_selection) {\n"
    "case SPECIFIC_VALUE:\n {"
    /* both the map value and map template are ordered */
    "int i_v = 0;\n"
    "int i_t = 0;\n"
    "while (i_v < other_value.val_ptr->n_elements && i_t < single_value.n_elements) {\n"
    /* at least one index is incremented in each cycle, always the lesser one */
    "if (*single_value.keys[i_t] == *other_value.val_ptr->keys[i_v]) {\n"
    /* equal keys -> the value must match the template */
    "if (!single_value.values[i_t]->match(*other_value.val_ptr->values[i_v], legacy)) return FALSE;\n"
    "++i_v;\n"
    "++i_t;\n"
    "}\n"
    "else if (%s < %s) {\n"
    /* template key is less than value key -> omit must match the template */
    "if (!single_value.values[i_t]->match_omit(legacy)) return FALSE;\n"
    "++i_t;\n"
    "}\n"
    "else {\n"
    /* value key is less than template key -> allow others flag must be set
       (i.e. there must be a * at the start of the template list) */
    "if (!single_value.allow_others) return FALSE;\n"
    "++i_v;\n"
    "}\n"
    "}\n"
    "while (i_t < single_value.n_elements) {\n"
    /* omit must match all remaining templates */
    "if (!single_value.values[i_t]->match_omit(legacy)) return FALSE;\n"
    "++i_t;\n"
    "}\n"
    /* if there are value keys remaining, then the allow others flag must be set */
    "return (i_v == other_value.val_ptr->n_elements) || single_value.allow_others;\n"
    "}\n"
    "case OMIT_VALUE:\n"
    "return FALSE;\n"
    "case ANY_VALUE:\n"
    "case ANY_OR_OMIT:\n"
    "return TRUE;\n"
    "case VALUE_LIST:\n"
    "case COMPLEMENTED_LIST:\n"
    "for (unsigned int list_count = 0; list_count < value_list.n_values; list_count++)\n"
    "if (value_list.list_value[list_count].match(other_value, legacy)) return template_selection == VALUE_LIST;\n"
    "return template_selection == COMPLEMENTED_LIST;\n"
    "case CONJUNCTION_MATCH:\n"
    "for (unsigned int i = 0; i < value_list.n_values; i++) {\n"
    "if (!value_list.list_value[i].match(other_value)) {\n"
    "return FALSE;\n"
    "}\n"
    "}\n"
    "return TRUE;\n"
    "case IMPLICATION_MATCH:\n"
    "return !implication_.precondition->match(other_value) || implication_.implied_template->match(other_value);\n"
    "case DYNAMIC_MATCH:\n"
    "return dyn_match->ptr->match(other_value);\n"
    "default:\n"
    "TTCN_error(\"Matching with an uninitialized/unsupported template of type %s.\");\n"
    "}\n"
    "return FALSE;\n"
    "}\n\n", name, name,
    int_keys ? "*single_value.int_keys[i_t]" : "*single_value.keys[i_t]",
    int_keys ? "*other_value.val_ptr->int_keys[i_v]" : "*other_value.val_ptr->keys[i_v]",
    dispname);

    /* is_bound function */
  def = mputstr(def,
    "inline boolean is_bound() const \n"
    "  { return template_selection != UNINITIALIZED_TEMPLATE; }\n");

  /* is_value operation */
  def = mputstr(def, "boolean is_value() const;\n");
  src = mputprintf(src,
    "boolean %s_template::is_value() const\n"
    "{\n"
    "if (template_selection != SPECIFIC_VALUE || is_ifpresent || single_value.allow_others) return FALSE;\n"
    "for (int elem_count = 0; elem_count < single_value.n_elements; elem_count++)\n"
    "if (!single_value.values[elem_count]->is_value()) return FALSE;\n"
    "return TRUE;\n"
    "}\n\n", name);

  if (!sdef->containsClass) {
    /* valueof operation */
    def = mputprintf(def, "%s valueof() const;\n", name);
    src = mputprintf(src,
      "%s %s_template::valueof() const\n"
      "{\n"
      "if (template_selection != SPECIFIC_VALUE || is_ifpresent || single_value.allow_others) "
        "TTCN_error(\"Performing a valueof or send operation on a non-specific template of type %s.\");\n"
      "%s ret_val;\n"
      "for (int elem_count = 0; elem_count < single_value.n_elements; elem_count++)\n"
      "ret_val[*single_value.keys[elem_count]] = single_value.values[elem_count]->valueof();\n"
      "return ret_val;\n"
      "}\n\n", name, name, dispname, name);
   }

  /* value list and set handling operators */
  def = mputstr(def,
    "void set_type(template_sel template_type, unsigned int list_length);\n");
  src = mputprintf(src,
    "void %s_template::set_type(template_sel template_type, "
    "unsigned int list_length)\n"
    "{\n"
    "clean_up();\n"
    "switch (template_type) {\n"
    "case VALUE_LIST:\n"
    "case COMPLEMENTED_LIST:\n"
    "case CONJUNCTION_MATCH:\n"
    "value_list.n_values = list_length;\n"
    "value_list.list_value = new %s_template[list_length];\n"
    "break;\n"
    "default:\n"
    "TTCN_error(\"Internal error: Setting an invalid type for a template of type %s.\");\n"
    "}\n"
    "set_selection(template_type);\n"
    "}\n\n", name, name, dispname);

  def = mputprintf(def,
    "%s_template& list_item(unsigned int list_index);\n", name);
  src = mputprintf(src,
    "%s_template& %s_template::list_item(unsigned int list_index)\n"
    "{\n"
    "if (template_selection != VALUE_LIST && "
      "template_selection != COMPLEMENTED_LIST && "
      "template_selection != CONJUNCTION_MATCH) "
      "TTCN_error(\"Internal error: Accessing a list element of a non-list template of type %s.\");\n"
    "if (list_index >= value_list.n_values) "
      "TTCN_error(\"Internal error: Index overflow in a value list template of type %s.\");\n"
    "return value_list.list_value[list_index];\n"
    "}\n\n", name, name, dispname, dispname);

  /* logging functions */
  def = mputstr(def, "void log() const;\n");
  src = mputprintf(src,
    "void %s_template::log() const\n"
    "{\n"
    "switch (template_selection) {\n"
    "case SPECIFIC_VALUE:\n"
    "TTCN_Logger::log_event_str(\"{ \");\n"
    "if (single_value.allow_others) TTCN_Logger::log_char('*');\n"
    "for (int elem_count = 0; elem_count < single_value.n_elements; elem_count++) {\n"
    "if (elem_count > 0 || single_value.allow_others) TTCN_Logger::log_event_str(\", \");\n"
    "TTCN_Logger::log_event_str(\"[ \");\n"
    "single_value.keys[elem_count]->log();\n"
    "TTCN_Logger::log_event_str(\" ] := \");\n"
    "single_value.values[elem_count]->log();\n"
    "}\n"
    "TTCN_Logger::log_event_str(\" }\");\n"
    "break;\n"
    "case COMPLEMENTED_LIST:\n"
    "TTCN_Logger::log_event_str(\"complement\");\n"
    "case CONJUNCTION_MATCH:\n"
    "if (template_selection == CONJUNCTION_MATCH) {\n"
    "TTCN_Logger::log_event_str(\"conjunct\");\n"
    "}\n"
    "case VALUE_LIST:\n"
    "TTCN_Logger::log_char('(');\n"
    "for (unsigned int list_count = 0; list_count < value_list.n_values; list_count++) {\n"
    "if (list_count > 0) TTCN_Logger::log_event_str(\", \");\n"
    "value_list.list_value[list_count].log();\n"
    "}\n"
    "TTCN_Logger::log_char(')');\n"
    "break;\n"
    "case IMPLICATION_MATCH:\n"
    "implication_.precondition->log();\n"
    "TTCN_Logger::log_event_str(\" implies \");\n"
    "implication_.implied_template->log();\n"
    "break;\n"
    "case DYNAMIC_MATCH:\n"
    "TTCN_Logger::log_event_str(\"@dynamic template\");\n"
    "break;\n"
    "default:\n"
    "log_generic();\n"
    "}\n"
    "log_restricted();\n"
    "log_ifpresent();\n"
    "}\n\n", name);

  def = mputprintf(def, "void log_match(const %s& match_value, "
    "boolean legacy = FALSE) const;\n", name);
  src = mputprintf(src,
    "void %s_template::log_match(const %s& match_value, "
    "boolean legacy) const\n"
    "{\n"
    "if(TTCN_Logger::VERBOSITY_COMPACT == TTCN_Logger::get_matching_verbosity()){\n"
    "if(match(match_value, legacy)){\n"
    "TTCN_Logger::print_logmatch_buffer();\n"
    "TTCN_Logger::log_event_str(\" matched\");\n"
    "} else {\n"
    "TTCN_Logger::print_logmatch_buffer();\n"
    "match_value.log();\n"
    "TTCN_Logger::log_event_str(\" with \");\n"
    "log();\n"
    "TTCN_Logger::log_event_str(\" unmatched\");\n"
    "}\n"
    "return;\n"
    "}\n"
    "match_value.log();\n"
    "TTCN_Logger::log_event_str(\" with \");\n"
    "log();\n"
    "if (match(match_value, legacy)) TTCN_Logger::log_event_str(\" matched\");\n"
    "else TTCN_Logger::log_event_str(\" unmatched\");\n"
    "}\n\n", name, name);

  if (!sdef->containsClass) {
    /* encoding/decoding functions */
    def = mputstr(def, "void encode_text(Text_Buf& text_buf) const;\n");
    src = mputprintf(src,
      "void %s_template::encode_text(Text_Buf& text_buf) const\n"
      "{\n"
      "encode_text_restricted(text_buf);\n"
      "switch (template_selection) {\n"
      "case SPECIFIC_VALUE:\n"
      "text_buf.push_int(single_value.allow_others ? 1 : 0);\n"
      "text_buf.push_int(single_value.n_elements);\n"
      "for (int elem_count = 0; elem_count < single_value.n_elements; elem_count++) {\n"
      "single_value.keys[elem_count]->encode_text(text_buf);\n"
      "single_value.values[elem_count]->encode_text(text_buf);\n"
      "}\n"
      "break;\n"
      "case OMIT_VALUE:\n"
      "case ANY_VALUE:\n"
      "case ANY_OR_OMIT:\n"
      "break;\n"
      "case VALUE_LIST:\n"
      "case COMPLEMENTED_LIST:\n"
      "text_buf.push_int(value_list.n_values);\n"
      "for (unsigned int list_count = 0; list_count < value_list.n_values; list_count++)\n"
      "value_list.list_value[list_count].encode_text(text_buf);\n"
      "break;\n"
      "default:\n"
      "TTCN_error(\"Text encoder: Encoding an uninitialized/unsupported template of type %s.\");\n"
      "}\n"
      "}\n\n", name, dispname);

    def = mputstr(def, "void decode_text(Text_Buf& text_buf);\n");
    src = mputprintf(src,
      "void %s_template::decode_text(Text_Buf& text_buf)\n"
      "{\n"
      "clean_up();\n"
      "decode_text_restricted(text_buf);\n"
      "switch (template_selection) {\n"
      "case SPECIFIC_VALUE: {\n"
      "single_value.allow_others = text_buf.pull_int().get_val();\n"
      "single_value.n_elements = text_buf.pull_int().get_val();\n"
      "single_value.max_elements = ttcn3_map_get_max_elements(single_value.n_elements);\n"
      "if (single_value.n_elements < 0) "
        "TTCN_error(\"Text decoder: Negative size was received for a template of type %s.\");\n"
      "single_value.keys = (%s**)allocate_pointers(single_value.max_elements);\n%s"
      "single_value.values = (%s**)allocate_pointers(single_value.max_elements);\n"
      "for (int elem_count = 0; elem_count < single_value.n_elements; elem_count++) {\n"
      "single_value.keys[elem_count] = new %s;\n"
      "single_value.keys[elem_count]->decode_text(text_buf);\n"
      "single_value.values[elem_count] = new %s;\n"
      "single_value.values[elem_count]->decode_text(text_buf);\n"
      "}\n"
      "break; }\n"
      "case OMIT_VALUE:\n"
      "case ANY_VALUE:\n"
      "case ANY_OR_OMIT:\n"
      "break;\n"
      "case VALUE_LIST:\n"
      "case COMPLEMENTED_LIST:\n"
      "value_list.n_values = text_buf.pull_int().get_val();\n"
      "value_list.list_value = new %s_template[value_list.n_values];\n"
      "for (unsigned int list_count = 0; list_count < value_list.n_values; "
        "list_count++)\n"
      "value_list.list_value[list_count].decode_text(text_buf);\n"
      "break;\n"
      "default:\n"
      "TTCN_error(\"Text decoder: An unknown/unsupported selection was "
      "received for a template of type %s.\");\n"
      "}\n"
      "}\n\n", name, dispname, key_name,
      int_keys ? "single_value.int_keys = "
        "(INTEGER**)allocate_pointers(single_value.max_elements);\n" : "",
      value_name_temp, key_name, value_name_temp, name, dispname);
  }

  /* TTCN-3 ispresent() function */
  def = mputstr(def, "boolean is_present(boolean legacy = FALSE) const;\n");
  src = mputprintf(src,
    "boolean %s_template::is_present(boolean legacy) const\n"
    "{\n"
    "if (template_selection == UNINITIALIZED_TEMPLATE) return FALSE;\n"
    "return !match_omit(legacy);\n"
    "}\n\n", name);

  /* match_omit() */
  def = mputstr(def, "boolean match_omit(boolean legacy = FALSE) const;\n");
  src = mputprintf(src,
    "boolean %s_template::match_omit(boolean legacy) const\n"
    "{\n"
    "if (is_ifpresent) return TRUE;\n"
    "switch (template_selection) {\n"
    "case OMIT_VALUE:\n"
    "case ANY_OR_OMIT:\n"
    "return TRUE;\n"
    "case IMPLICATION_MATCH:\n"
    "return !implication_.precondition->match_omit() || implication_.implied_template->match_omit();\n"
    "case VALUE_LIST:\n"
    "case COMPLEMENTED_LIST:\n"
    "if (legacy) {\n"
    "for (unsigned int i=0; i<value_list.n_values; i++)\n"
    "if (value_list.list_value[i].match_omit())\n"
    "return template_selection==VALUE_LIST;\n"
    "return template_selection==COMPLEMENTED_LIST;\n"
    "} // else fall through\n"
    "default:\n"
    "return FALSE;\n"
    "}\n"
    "return FALSE;\n"
    "}\n\n", name);

  if (!sdef->containsClass) {
    /* set_param() */
    def = mputstr(def, "void set_param(Module_Param& param);\n");
    src = mputprintf(src,
      "void %s_template::set_param(Module_Param& param)\n"
      "{\n"
      /* todo */
      "}\n\n", name);
  }

  /* check template restriction */
  def = mputstr(def, "void check_restriction(template_res t_res, "
    "const char* t_name=NULL, boolean legacy = FALSE) const;\n");
  src = mputprintf(src,
    "void %s_template::check_restriction("
      "template_res t_res, const char* t_name, boolean legacy) const\n"
    "{\n"
    "if (template_selection == UNINITIALIZED_TEMPLATE) return;\n"
    "switch ((t_name && (t_res == TR_VALUE)) ? TR_OMIT : t_res) {\n"
    "case TR_OMIT:\n"
    "if (template_selection == OMIT_VALUE) return;\n"
    "case TR_VALUE:\n"
    "if (template_selection != SPECIFIC_VALUE || is_ifpresent || single_value.allow_others) break;\n"
    "for (int i = 0; i < single_value.n_elements; i++) "
    "single_value.values[i]->check_restriction(t_res, t_name ? t_name : \"%s\");\n"
    "return;\n"
    "case TR_PRESENT:\n"
    "if (!match_omit(legacy)) return;\n"
    "break;\n"
    "default:\n"
    "return;\n"
    "}\n"
    "TTCN_error(\"Restriction `%%s' on template of type %%s violated.\", "
      "get_res_name(t_res), t_name ? t_name : \"%s\");\n"
    "}\n\n", name, dispname, dispname);

  /* istemplatekind function */
  def = mputstr(def, "boolean get_istemplate_kind(const char* type) const;\n");
  src = mputprintf(src, "boolean %s_template::get_istemplate_kind(const char* type) const {\n"
    "if (!strcmp(type, \"AnyElement\")) {\n"
    "  if (template_selection != SPECIFIC_VALUE) {\n"
    "    return FALSE;\n"
    "  }\n"
    "  for (int i = 0; i < single_value.n_elements; i++) {\n"
    "    if (single_value.values[i]->get_selection() == ANY_VALUE) {\n"
    "      return TRUE;\n"
    "    }\n"
    "  }\n"
    "  return FALSE;\n"
    "} else if (!strcmp(type, \"AnyElementsOrNone\")) {\n"
    "  if (template_selection != SPECIFIC_VALUE) {\n"
    "    return FALSE;\n"
    "  }\n"
    "  for (int i = 0; i < single_value.n_elements; i++) {\n"
    "    if (single_value.values[i]->get_selection() == ANY_OR_OMIT) {\n"
    "      return TRUE;\n"
    "    }\n"
    "  }\n"
    "  return FALSE;\n"
    "} else if (!strcmp(type, \"length\")) {\n"
    "  return length_restriction_type != NO_LENGTH_RESTRICTION;\n"
    "} else {\n"
    "  return Base_Template::get_istemplate_kind(type);\n"
    "}\n"
    "}\n\n", name);
  
  /* end of class */
  def = mputstr(def, "};\n\n");

  output->header.class_decls = mputprintf(output->header.class_decls,
                                          "class %s_template;\n", name);
  output->header.class_defs = mputstr(output->header.class_defs, def);
  Free(def);
  output->source.methods = mputstr(output->source.methods, src);
  Free(src);
}

