/******************************************************************************/
// @copyright   Copyright Notification
//   No part may be reproduced except as authorized by written permission.
//   The copyright and the foregoing restriction extend to reproduction in all media.
//   (c) 2023, 3GPP Organizational Partners (ARIB, ATIS, CCSA, ETSI, TSDSI, TTA, TTC).
//   All rights reserved.
// @version: IWD_23wk37
// $Date: 2022-11-29 17:53:31 +0100 (Tue, 29 Nov 2022) $
// $Rev: 34751 $
/******************************************************************************/

module NAS_AuxiliaryDefsAndFunctions {

  import from CommonDefs all;
  import from Parameters all;
  import from NAS_CommonTypeDefs all;
  import from NAS_CommonTemplates all;
  import from CommonIP all;

  template (value) ExtdEmergNum cs_ExtdEmergNum (octetstring p_Digits,
                                                 octetstring p_SubServices := ''O) :=
  { /* @status    APPROVED (NR5GC) */
    len         := int2oct(lengthof(p_Digits), 1),
    digits      := p_Digits,                 // BCD numbers
    lengthOfSubService := int2oct(lengthof(p_SubServices), 1),
    subServices := p_SubServices
  };

  //============================================================================
  // Protocol Configuration Options (PCO)
  //----------------------------------------------------------------------------
  /*
   * @desc      Function used to check whether the UE requests in its PCO an address allocation via NAS signalling
   *            If the UE does not send PCO, then address assignment shall be via DHCP. TS 36.508 table 4.7.3-6.
   * @param     p_Pco
   * @return    boolean
   * @status    APPROVED (ENDC, IMS, IMS_IRAT, LTE, LTE_A_IRAT, LTE_A_PRO, LTE_A_R10_R11, LTE_A_R12, LTE_IRAT, NR5GC_IRAT, POS)
   */
  function f_CheckPCOforIPallocationViaNas(template (omit) ProtocolConfigOptions p_Pco) return boolean
  {
    var NAS_ProtocolConfigOptions_Type v_ProtocolContainerList;
    var integer i;

    if (isvalue(p_Pco.pco)) {      /* @sic R5s170030 change 8 sic@ */
      v_ProtocolContainerList := valueof (p_Pco.pco);
      for (i := 0; i < lengthof(v_ProtocolContainerList); i := i + 1) {
        if (v_ProtocolContainerList[i].protocolID == '000B'O) { // TS 24.008 clause 10.5.6.3  // @sic R5s090322 sic@
          return false;  // @sic R5s090322 sic@
        }
      }
    }
    return true;  // @sic R5s090322 sic@
  }

  //----------------------------------------------------------------------------
  /*
   * @desc      Function used to check whether the UE requests in its PCO an address allocation via NAS signalling
   *            If the UE does not send PCO, then address assignment shall be via DHCP. TS 36.508 table 4.7.3-6.
   * @param     p_Pco
   * @return    boolean
   * @status    APPROVED (ENDC, IMS, NBIOT, NR5GC, NR5GC_IRAT, POS)
   */
  function f_CheckExtdPCOforIPallocationViaNas(template (omit) ExtdProtocolConfigOptions p_Pco) return boolean
  {
    var ExtdProtocolConfigOptions v_Pco;
    var integer i;

    if (isvalue(p_Pco)) {
      v_Pco := valueof (p_Pco);
      if (ispresent(v_Pco.pco)) { // @sic R5s170233 sic@
        for (i := 0; i < lengthof(v_Pco.pco); i := i + 1 ) {
          if(v_Pco.pco[i].protocolID == '000B'O) { // TS 24.008 clause 10.5.6.3
            return false;
          }
        }
      }
    }
    return true;
  }

  //------------------------------------
  /*
   * @desc      return
   *              omit when there is no protocol config option with the given id in the given list
   *              content of the protocol config option (which can be empty or omit) otherwise
   * @param     p_Pco
   * @param     p_ProtocolID
   * @return    template (omit) octetstring
   * @status    APPROVED (ENDC, IMS, IMS_IRAT, LTE, LTE_A_IRAT, LTE_A_PRO, LTE_A_R10_R11, LTE_A_R12, LTE_IRAT, NR5GC_IRAT, POS)
   */
  function f_ProtocolConfigOptions_Get(template (omit) ProtocolConfigOptions p_Pco,
                                       O2_Type p_ProtocolID) return template (omit) octetstring
  {
    var NAS_ProtocolConfigOptions_Type v_ProtocolConfigOptions;
    var integer i;

    if (isvalue(p_Pco.pco)) {
      v_ProtocolConfigOptions := valueof(p_Pco.pco);
      for (i := 0; i < lengthof(v_ProtocolConfigOptions); i := i + 1) {
        if (v_ProtocolConfigOptions[i].protocolID == p_ProtocolID) { // TS 24.008 clause 10.5.6.3
          return v_ProtocolConfigOptions[i].content;
        }
      }
    }
    return omit;
  }

  /*
   * @desc      return
   *              omit when there is no protocol config option with the given id in the given list
   *              content of the protocol config option (which can be empty or omit) otherwise
   * @param     p_Pco
   * @param     p_ProtocolID
   * @return    template (omit) octetstring
   * @status    APPROVED (ENDC, IMS, IMS_IRAT, LTE, LTE_A_IRAT, LTE_A_PRO, LTE_A_R10_R11, LTE_A_R12, LTE_IRAT, NR5GC, NR5GC_IRAT, POS)
   */
  function f_ExtdProtocolConfigOptions_Get(template (omit) ExtdProtocolConfigOptions p_Pco,
                                       O2_Type p_ProtocolID) return template (omit) octetstring
  {
    var NAS_ExtdProtocolConfigOptions_Type v_ProtocolConfigOptions;
    var integer i;

    if (isvalue(p_Pco.pco)) {
      v_ProtocolConfigOptions := valueof(p_Pco.pco);
      for (i := 0; i < lengthof(v_ProtocolConfigOptions); i := i + 1) {
        if (v_ProtocolConfigOptions[i].protocolID == p_ProtocolID) { // TS 24.008 clause 10.5.6.3
          return v_ProtocolConfigOptions[i].content;
        }
      }
    }
    return omit;
  }

  /*
   * @desc      return SessionId if included in the given list or omit otherwise
   * @param     p_Pco
   * @return    template (omit) integer
   * @status    APPROVED (ENDC, IMS, IMS_IRAT, LTE, LTE_A_IRAT, LTE_A_PRO, LTE_A_R10_R11, LTE_A_R12, LTE_IRAT, NR5GC_IRAT, POS)
   */
  function f_ProtocolConfigOptions_GetSessionId(template (omit) ProtocolConfigOptions p_Pco) return template (omit) integer
  {
    var template (omit) octetstring v_Content := f_ProtocolConfigOptions_Get(p_Pco, '001A'O);
    var octetstring v_Octetstring;
    if (isvalue(v_Content)) {
      v_Octetstring := valueof(v_Content);
      if (lengthof(v_Octetstring) > 0) {
        return oct2int(v_Octetstring);
      }
    }
    return omit;
  }

  /*
   * @desc      return QosFlowId if included in the given list or omit otherwise
   * @param     p_Pco
   * @return    template (omit) integer
   * @status    APPROVED (ENDC, IMS, IMS_IRAT, LTE, LTE_A_IRAT, LTE_A_PRO, LTE_A_R10_R11, LTE_A_R12, LTE_IRAT, NR5GC_IRAT, POS)
   */
  function f_ProtocolConfigOptions_GetQosFlowId(template (omit) ProtocolConfigOptions p_Pco) return template (omit) integer
  {
    var template (omit) octetstring v_Content := f_ProtocolConfigOptions_Get(p_Pco, '001F'O);
    var octetstring v_Octetstring;
    if (isvalue(v_Content)) {
      v_Octetstring := valueof(v_Content);
      if (lengthof(v_Octetstring) > 0) {
        return (oct2int(v_Octetstring[0]) mod 64);   // QFI of the 1st QoS flow is in the lower 6 bits of the 1st octet
      }
    }
    return omit;
  }

  /*
   * @desc      return SessionId if included in the given list or omit otherwise
   * @param     p_Pco
   * @return    template (omit) integer
   * @status    APPROVED (ENDC, IMS, IMS_IRAT, LTE, LTE_A_IRAT, LTE_A_PRO, LTE_A_R10_R11, LTE_A_R12, LTE_IRAT, NR5GC_IRAT, POS)
   */
  function f_ExtdProtocolConfigOptions_GetSessionId(template (omit) ExtdProtocolConfigOptions p_Pco) return template (omit) integer
  {
    var template (omit) octetstring v_Content := f_ExtdProtocolConfigOptions_Get(p_Pco, '001A'O);
    var octetstring v_Octetstring;
    if (isvalue(v_Content)) {
      v_Octetstring := valueof(v_Content);
      if (lengthof(v_Octetstring) > 0) {
        return oct2int(v_Octetstring);
      }
    }
    return omit;
  }

  /*
   * @desc      return QosFlowId if included in the given list or omit otherwise
   * @param     p_Pco
   * @return    template (omit) integer
   * @status    APPROVED (ENDC, IMS, IMS_IRAT, LTE, LTE_A_IRAT, LTE_A_PRO, LTE_A_R10_R11, LTE_A_R12, LTE_IRAT, NR5GC_IRAT, POS)
   */
  function f_ExtdProtocolConfigOptions_GetQosFlowId(template (omit) ExtdProtocolConfigOptions p_Pco) return template (omit) integer
  {
    var template (omit) octetstring v_Content := f_ExtdProtocolConfigOptions_Get(p_Pco, '001F'O);
    var octetstring v_Octetstring;
    if (isvalue(v_Content)) {
      v_Octetstring := valueof(v_Content);
      if (lengthof(v_Octetstring) > 0) {
        return (oct2int(v_Octetstring[0]) mod 64);   // QFI of the 1st QoS flow is in the lower 6 bits of the 1st octet
      }
    }
    return omit;
  }
  //------------------------------------
  /*
   * @desc      Function used to check whether the UE requests in its PCO a specific protocol
   *            (SIP  signalling)
   * @param     p_Pco
   * @param     p_ProtocolID
   * @return    boolean
   * @status    APPROVED (ENDC, IMS, IMS_IRAT, LTE, LTE_A_IRAT, LTE_A_PRO, LTE_A_R10_R11, LTE_A_R12, LTE_IRAT, POS)
   */
  function f_CheckPCOforProtocolID(template (omit) ProtocolConfigOptions p_Pco,
                                   O2_Type p_ProtocolID) return boolean
  {
    var ProtocolConfigOptions v_Pco;
    var integer i;

    if (isvalue(p_Pco)) {
      v_Pco := valueof (p_Pco);
      for (i := 0; i < lengthof(v_Pco.pco); i := i + 1) {
        if(v_Pco.pco[i].protocolID == p_ProtocolID) { // TS 24.008 clause 10.5.6.3
        return true;
        }
      }
    }
    return false;
  }

  /*
   * @desc      Function used to check whether the UE requests in its PCO a specific protocol
   *            (SIP  signalling)
   * @param     p_Pco
   * @param     p_ProtocolID
   * @return    boolean
   * @status    APPROVED (ENDC, IMS, IMS_IRAT, LTE, LTE_A_IRAT, LTE_A_PRO, LTE_A_R10_R11, LTE_A_R12, LTE_IRAT, POS)
   */
  function f_CheckExtdPCOforProtocolID(template ExtdProtocolConfigOptions p_Pco,
                                    O2_Type p_ProtocolID) return boolean
  {
    var ExtdProtocolConfigOptions v_Pco;
    var integer i;

    if (isvalue(p_Pco)) {
      v_Pco := valueof (p_Pco);
      for (i := 0; i < lengthof(v_Pco.pco); i := i + 1) {
        if(v_Pco.pco[i].protocolID == p_ProtocolID) { // TS 24.008 clause 10.5.6.3
        return true;
      }
    }
    }
    return false;
  }

  //------------------------------------
  /*
   * @desc      Function used to check whether the UE requests a P-CSCF address or DNS server address in its PCO
   *            If the UE does not support IMS, or doesn't request a P-CSCF address, the function returns the default PCO, PPP
   * @param     p_ConfigOptionsRX
   * @param     p_PdnIndex          (default value: PDN_1)
   * @param     p_AdditionalProtocolConfigOptions      (default value: {})
   * @param     p_IgnoreIM_CN_SubsystemSignalingFlag   (default value: true)
   * @return    template (value) NAS_ProtocolConfigOptions_Type
   * @status    APPROVED (ENDC, IMS, IMS_IRAT, LTE, LTE_A_IRAT, LTE_A_PRO, LTE_A_R10_R11, LTE_A_R12, LTE_IRAT, NR5GC_IRAT, POS)
   */
  function f_NAS_GetProtocolConfigOptionList(NAS_ProtocolConfigOptions_Type p_ConfigOptionsRX,
                                             PDN_Index_Type p_PdnIndex := PDN_1,
                                             template (value) NAS_ProtocolConfigOptions_Type p_AdditionalProtocolConfigOptions := {},
                                             boolean p_IgnoreIM_CN_SubsystemSignalingFlag := true) return template (value) NAS_ProtocolConfigOptions_Type
  { /* @sic R5-198996: p_AdditionalProtocolConfigOptions for 5GC options sic@ */
    var PDN_AddressInfo_Type v_PDN_AddressInfo := f_PDN_AddressInfo_Get(p_PdnIndex);
    var template (value) NAS_ProtocolConfigOptions_Type v_ProtocolContainerList := {};
    var integer v_PcoCnt := 0;
    var O2_Type v_ProtocolId;
    var octetstring v_Contents;
    var integer i;

    for (i := 0; i < lengthof(p_ConfigOptionsRX); i := i + 1) {

      v_ProtocolId := p_ConfigOptionsRX[i].protocolID;
      v_Contents := ''O;

      select (v_ProtocolId) {  // See 24.008 Table 10.5.154
        case ('0001'O) {       // P-CSCF IPv6 address
          if (pc_IMS) {
            v_Contents := f_Convert_IPv6Addr2OctString(v_PDN_AddressInfo.PCSCF_IPAddressIPv6);
          }
        }
        case ('0002'O) {            // IM CN Subsystem Signalling Flag, reply with an empty container
          if (not p_IgnoreIM_CN_SubsystemSignalingFlag) {
            v_ProtocolContainerList[v_PcoCnt] := cs_ProtocolContainer_Common(v_ProtocolId);
            v_PcoCnt := v_PcoCnt + 1;
            continue;
          }
        }
        case ('0003'O) {       // DNS Server IPv6 address
          v_Contents := f_Convert_IPv6Addr2OctString(v_PDN_AddressInfo.DNS_ServerAddressIPv6);
        }
        case ('000C'O) {       // P-CSCF IPv4 address
          if (pc_IMS) {
            v_Contents := f_Convert_IPv4Addr2OctString(v_PDN_AddressInfo.PCSCF_IPAddressIPv4);
          }
        }
        case ('000D'O) {       // DNS Server IPv4 address
          v_Contents := f_Convert_IPv4Addr2OctString(v_PDN_AddressInfo.DNS_ServerAddressIPv4);
        }
      }

      if (lengthof(v_Contents) > 0) {
        v_ProtocolContainerList[v_PcoCnt] := cs_ProtocolContainer(v_ProtocolId, v_Contents);
        v_PcoCnt := v_PcoCnt + 1;
      }
    }
    return valueof(v_ProtocolContainerList) & valueof(p_AdditionalProtocolConfigOptions);
  }

  /*
   * @desc      wrapper for f_GetDefaultProtocolConfigOptionList
   * @param     p_Pco
   * @param     p_PdnIndex          (default value: PDN_1)
   * @param     p_AdditionalProtocolConfigOptions      (default value: {})
   * @return    template (value) ProtocolConfigOptions
   * @status    APPROVED (ENDC, IMS, IMS_IRAT, LTE, LTE_A_IRAT, LTE_A_PRO, LTE_A_R10_R11, LTE_A_R12, LTE_IRAT, NR5GC_IRAT, POS)
   */
  function f_GetDefaultProtocolConfigOptions(template (omit) ProtocolConfigOptions p_Pco,
                                             PDN_Index_Type p_PdnIndex := PDN_1,
                                             template (value) NAS_ProtocolConfigOptions_Type p_AdditionalProtocolConfigOptions := {}) return template (value) ProtocolConfigOptions
  { /* @sic R5s130362 - MCC160 Implementation sic@ */
    /* @sic R5s141127 change 4 - MCC160 Comments: split into f_GetDefaultProtocolConfigOptions and f_GetDefaultProtocolConfigOptionList sic@ */
    /* @sic R5-198996: p_AdditionalProtocolConfigOptions for 5GC options sic@ */
    var template (value) NAS_ProtocolConfigOptions_Type v_ProtocolContainerListTX := {};
    var NAS_ProtocolConfigOptions_Type v_ProtocolContainerListRX;

    if (isvalue(p_Pco.pco)) { /* @sic R5s170030 change 8 sic@ */
      v_ProtocolContainerListRX := valueof(p_Pco.pco);
      v_ProtocolContainerListTX := f_NAS_GetProtocolConfigOptionList(v_ProtocolContainerListRX, p_PdnIndex, p_AdditionalProtocolConfigOptions);
    }

    return f_NAS_ProtocolConfigOptionsTX(v_ProtocolContainerListTX);
  }

  //----------------------------------------------------------------------------
  /*
   * @desc      Common function to build up ProtocolConfigOptions
   * @param     p_ProtocolContainers
   * @return    template (value) ExtdProtocolConfigOptions
   * @status    APPROVED (ENDC, IMS, IMS_IRAT, LTE, LTE_A_IRAT, LTE_A_PRO, LTE_A_R10_R11, LTE_A_R12, LTE_IRAT, NBIOT, NR5GC, NR5GC_IRAT, POS)
   */
  function f_NAS_ExtdProtocolConfigOptionsTX(template (omit) NAS_ExtdProtocolConfigOptions_Type  p_ProtocolContainers) return template (value) ExtdProtocolConfigOptions
  {
    var integer v_Length := 1;   // there are at least ext, spare, configProtocol
    var integer i;

    if (isvalue(p_ProtocolContainers)) { // @sic R5s170233 sic@
      for (i:=0; i < lengthof(p_ProtocolContainers); i:=i+1) {
        v_Length := v_Length + oct2int(valueof(p_ProtocolContainers[i].protocolLength)) + 3; // 2 octets for protocolID and 1 octet for protocolLength
      }
    }
    return cs_ExtdPCO(int2oct(v_Length, 2), p_ProtocolContainers);
  }

  /*
   * @desc      wrapper for f_GetDefaultExtdProtocolConfigOptionList
   * @param     p_Pco
   * @param     p_PdnIndex          (default value: PDN_1)
   * @return    template (value) ExtdProtocolConfigOptions
   * @status    APPROVED (NBIOT)
   */
  function f_GetDefaultExtdProtocolConfigOptions(template (omit) ExtdProtocolConfigOptions p_Pco,
                                             PDN_Index_Type p_PdnIndex := PDN_1) return template (value) ExtdProtocolConfigOptions
  {
    var template (omit) NAS_ExtdProtocolConfigOptions_Type v_ProtocolContainerList := omit;  // @sic R5s170233 sic@
    var ExtdProtocolConfigOptions v_ProtocolConfigOptionsRX;

    if (isvalue(p_Pco)) {
      v_ProtocolConfigOptionsRX := valueof(p_Pco);
      if (ispresent(p_Pco.pco)) { // @sic R5s170233 sic@
        v_ProtocolContainerList := f_NAS_GetExtdProtocolConfigOptionList(v_ProtocolConfigOptionsRX.pco, -, p_PdnIndex); // @sic R5-200649, R5s200643 sic@
      }
    }

    return f_NAS_ExtdProtocolConfigOptionsTX(v_ProtocolContainerList);
  }

  /*
   * @desc      Function used to check whether the UE requests a P-CSCF address or DNS server address in its PCO
   *            If the UE does not support IMS, or doesn't request a P-CSCF address, the function returns the default PCO, PPP
   * @param     p_ConfigOptionsRX
   * @param     p_IgnoreIM_CN_SubsystemSignalingFlag (default value: true)
   * @param     p_PdnIndex          (default value: PDN_1)
   * @return    template (omit) NAS_ExtdProtocolConfigOptions_Type
   * @status    APPROVED (NBIOT)
   */
  function f_NAS_GetExtdProtocolConfigOptionList(NAS_ExtdProtocolConfigOptions_Type p_ConfigOptionsRX,
                                                 boolean p_IgnoreIM_CN_SubsystemSignalingFlag := true,
                                                 PDN_Index_Type p_PdnIndex := PDN_1)
    return template (omit) NAS_ExtdProtocolConfigOptions_Type
  { /* @sic R5s170515 change 1: "template (omit)" for return type sic@ */
    var PDN_AddressInfo_Type v_PDN_AddressInfo := f_PDN_AddressInfo_Get(p_PdnIndex);   // @sic R5-200649 sic@
    var template (omit) NAS_ExtdProtocolConfigOptions_Type v_ProtocolContainerList := omit;
    var integer v_PcoCnt := 0;
    var O2_Type v_ProtocolId;
    var octetstring v_Contents;
    var integer i;

    for (i := 0; i < lengthof(p_ConfigOptionsRX); i := i + 1) {

      v_ProtocolId := p_ConfigOptionsRX[i].protocolID;
      v_Contents := ''O;

      select (v_ProtocolId) {  // See 24.008 Table 10.5.154
        case ('0002'O) {            // IM CN Subsystem Signalling Flag, reply with an empty container
          if (not p_IgnoreIM_CN_SubsystemSignalingFlag) {
            v_ProtocolContainerList[v_PcoCnt] := cs_ProtocolContainer_Common(v_ProtocolId);
            v_PcoCnt := v_PcoCnt + 1;
            continue;
          }
        }
        case ('0003'O) {       // DNS Server IPv6 address
          v_Contents := f_Convert_IPv6Addr2OctString(v_PDN_AddressInfo.DNS_ServerAddressIPv6);
        }
        case ('000D'O) {       // DNS Server IPv4 address
          v_Contents := f_Convert_IPv4Addr2OctString(v_PDN_AddressInfo.DNS_ServerAddressIPv4);
        }
      }

      if (lengthof(v_Contents) > 0) {
        v_ProtocolContainerList[v_PcoCnt] := cs_ProtocolContainer(v_ProtocolId, v_Contents);
        v_PcoCnt := v_PcoCnt + 1;
      }
    }
    return v_ProtocolContainerList; // @sic R5-200649, R5s200643 sic@
  }
  //----------------------------------------------------------------------------
  /*
   * @desc      Get Pdn Index to be used in Multi PDN/PDU ATS
   * @param     p_DNNType
   * @return    PDN_Index_Type
   * @status    APPROVED (IMS, NR5GC, NR5GC_IRAT, POS)
   */
   function f_GetMultiPdnIndex(PDU_PDN_DNN_Type p_DNNType) return PDN_Index_Type
  { // @sic R5-221467 sic@
    var PDN_Index_Type v_PDN := PDN_3;
    select (p_DNNType) {
      case (IMS_DNN){
        v_PDN := PDN_1;
      }
      case (Emergency_PDN){
        v_PDN := PDN_2;
      }
      case (URLLC_DNN){
        v_PDN := PDN_4;
      }
      case (MIOT_DNN){
        v_PDN := PDN_5;
      }
      case (V2X_DNN){
        v_PDN := PDN_6;
      }
    }
    return v_PDN;
  }

  /*
   * @desc      Get Pdn Type from the Index to be used in Multi PDN/PDU ATS
   * @param     p_PDN_Index
   * @return    PDU_PDN_DNN_Type
   * @status    APPROVED (NR5GC_IRAT)
   */
   function f_GetMultiPdnTypeFromIndex(PDN_Index_Type p_PDN_Index) return PDU_PDN_DNN_Type
  { // @sic R5-221467 sic@
    var PDU_PDN_DNN_Type v_DNNType := Internet_DNN;
    select (p_PDN_Index) {
      case (PDN_1){
        v_DNNType := IMS_DNN;
      }
      case (PDN_2){
        v_DNNType := Emergency_PDN;
      }
      case (PDN_4){
        v_DNNType := URLLC_DNN;
      }
      case (PDN_5){
        v_DNNType := MIOT_DNN;
      }
      case (PDN_6){
        v_DNNType := V2X_DNN;
      }
    }
    return v_DNNType;
  }

  /*
   * @desc      Get PDN DNN Type to be used in Multi PDN/PDU ATS from APN
   * @param     p_APN
   * @return    PDU_PDN_DNN_Type
   * @status    APPROVED (IMS, NR5GC, NR5GC_IRAT, POS)
   */
   function f_GetPDN_DNNTypeFromAPN(octetstring p_APN) return PDU_PDN_DNN_Type
  { // @sic R5-221467 sic@
    var PDU_PDN_DNN_Type v_PDNType := None;

    if (match (p_APN, f_DomainName_Encode(pc_APN_ID_Internet))) { // @sic R5s210014 sic@
      v_PDNType := Internet_DNN;
    } else if (match (p_APN, f_DomainName_Encode(pc_APN_ID_IMS))) { // @sic R5s210014 sic@
      v_PDNType := IMS_DNN;
    } else if (match (p_APN, f_DomainName_Encode(pc_APN_ID_URLLC))) {
      v_PDNType := URLLC_DNN;
    } else if (match (p_APN, f_DomainName_Encode(pc_APN_ID_MIOT))) {
      v_PDNType := MIOT_DNN;
    } else if (match (p_APN, f_DomainName_Encode(pc_APN_ID_V2X))) {
      v_PDNType := V2X_DNN;
    } else if (f_IsDNNForEmergency(p_APN)) {  // this isn't specified in table 4.8.4-1, but is a valid PDU type
      v_PDNType := Emergency_PDN;
    }

    return v_PDNType;
  }

  /*
   * @desc      function to get/initialise p_DNN as per PICS
   * @param     p_PDUType
   * @return    charstring
   * @status    APPROVED (IMS, NR5GC, NR5GC_IRAT, POS)
   */
  function f_GetDNNStringFromPICS(PDU_PDN_DNN_Type p_PDUType) return charstring
  {
    var charstring v_DNN;

    select (p_PDUType) {
      case (Internet_DNN) {
        v_DNN := pc_APN_ID_Internet;
      }
      case (IMS_DNN) {
        v_DNN := pc_APN_ID_IMS;
      }
      case (URLLC_DNN) {
        v_DNN := pc_APN_ID_URLLC;
      }
      case (MIOT_DNN) {
        v_DNN := pc_APN_ID_MIOT;
      }
      case (V2X_DNN) {
        v_DNN := pc_APN_ID_V2X;
      }
      case (Ethernet_DNN) { // @sic R5-227472 sic@
        v_DNN := pc_APN_ID_Ethernet;
      }
      // @sic R5s220670 sic@
      case else { FatalError(__FILE__, __LINE__, "Unexpected PICS setting in f_GetDNNStringFromPICS"); }
    }
    return v_DNN;
  }

  /*
   * @desc      Get Default PDN DNN Type to be used in Multi PDN/PDU ATS from PICS
   * @return    PDU_PDN_DNN_Type
   * @status    APPROVED (IMS, NR5GC, NR5GC_IRAT, POS)
   */
   function f_GetDefaultAPN_DNNType_FromPICS() return PDU_PDN_DNN_Type
  { // @sic R5-221467 sic@
    var PDU_PDN_DNN_Type v_PDNType := None;

    select (pc_APN_Default_Configuration) {
      case (internet) {
        v_PDNType := Internet_DNN;
      }
      case (ims) {
        v_PDNType := IMS_DNN;
      }
      case (urllc) {
        v_PDNType := URLLC_DNN;
      }
      case (miot) {
        v_PDNType := MIOT_DNN;
      }
      case (v2x) {
        v_PDNType := V2X_DNN;
      }
      case (ethernet) { // @sic R5-227472 sic@
        v_PDNType := Ethernet_DNN;
      }
    }
    return v_PDNType;
  }

  /*
   * @desc      Checks DNN to see if it requests emergency PDN,  based on f_EUTRA_GetAPNForIMS
   * @param     p_DNNFromReq
   * @return    boolean
   * @status    APPROVED (IMS, NR5GC, NR5GC_IRAT, POS)
   */
  function f_IsDNNForEmergency(octetstring p_DNNFromReq) return boolean
  {
    return match(p_DNNFromReq, f_DomainName_EncodeLabels({"sos"}));
  }

  /*
   * @desc      return label for MCC or MNC to be used in APN
   * @param     p_MncMcc
   * @param     p_Digits
   * @return    charstring
   * @status    APPROVED (ENDC, IMS, IMS_IRAT, LTE, LTE_A_IRAT, LTE_A_PRO, LTE_A_R10_R11, LTE_A_R12, LTE_IRAT, NR5GC, NR5GC_IRAT, POS)
   */
  function f_APN_MccMncLabel(charstring p_MncMcc,
                             charstring p_Digits) return charstring
  { /* in case of the MCC there are 3 digits; for MNC it may be 2 or 3 digits */
    var integer v_DigitCnt := lengthof(p_Digits);
    var charstring v_Label := "";

    select (v_DigitCnt) {
      case (2) { v_Label := p_MncMcc & "0" & p_Digits; }
      case (3) { v_Label := p_MncMcc & p_Digits; }
      case else { FatalError(__FILE__, __LINE__, ""); }
    }
    return v_Label;
  }
  /*
   * @desc      Encode APN acc. to TS 23.003 cl. 9.1 and RFC 1035 cl. 4.1.2
   * @param     p_LabelList
   * @return    octetstring
   * @status    APPROVED (ENDC, IMS, IMS_IRAT, LTE, LTE_A_IRAT, LTE_A_PRO, LTE_A_R10_R11, LTE_A_R12, LTE_IRAT, NR5GC, NR5GC_IRAT, POS)
   */
  function f_DomainName_EncodeLabels(CharStringList_Type p_LabelList) return octetstring
  {
    var octetstring v_EncodedAPN := ''O;
    var octetstring v_LabelOctets;
    var integer v_Length;
    var integer i;

    for (i := 0; i < lengthof(p_LabelList); i := i + 1) {
      v_LabelOctets := char2oct(p_LabelList[i]);
      v_Length := lengthof(v_LabelOctets);
      v_EncodedAPN := v_EncodedAPN & int2oct(v_Length, 1) & v_LabelOctets;
    }
    return v_EncodedAPN;
  }

  /*
   * @desc      Encode APN acc. to TS 23.003 cl. 9.1 and RFC 1035 cl. 4.1.2
   * @param     p_DomainName
   * @return    octetstring
   * @status    APPROVED (ENDC, IMS, IMS_IRAT, LTE, LTE_A_IRAT, LTE_A_PRO, LTE_A_R10_R11, LTE_A_R12, LTE_IRAT, NR5GC, NR5GC_IRAT, POS)
   */
  function f_DomainName_Encode(charstring p_DomainName) return octetstring
  {
    var CharStringList_Type v_LabelList := f_StringSplit(p_DomainName, {"."});
    return f_DomainName_EncodeLabels(v_LabelList);
  }

  //============================================================================

  //----------------------------------------------------------------------------
  /*
   * @desc      Convert from IMSI, IMEI or IMEISV of type hexstring to octetstring (to be used in f_Imsi2MobileIdentity, f_Imei2MobileIdentity and f_Imeisv2MobileIdentity)
   * @param     p_MobileId
   * @return    octetstring
   * @status    APPROVED (IMS, IMS_IRAT, LTE, LTE_A_IRAT, LTE_A_R10_R11, LTE_IRAT, NBIOT, NR5GC, NR5GC_IRAT, POS, SSNITZ, UTRAN)
   */
  function f_ImsiImei2Octetstring(hexstring p_MobileId) return octetstring
  {
    var integer v_Length := lengthof(p_MobileId);
    var integer v_Odd := (v_Length rem 2);
    var octetstring v_Other := '00'O;
    var integer I;
    var integer K;

    if (v_Odd == 0) {                   // If length is even
      v_Length := v_Length + 1;
      p_MobileId := p_MobileId & 'F'H;          // add '1111' on to the end of the IMSI
    }

    // Reverse each pair of digits
    // First digit is not included as it is treated differently
    K:=0;
    for (I:=1; I < v_Length - 1; I:=I+2) {
      v_Other[K] := hex2oct(p_MobileId[I+1] & p_MobileId[I]);
      K := K + 1;
    }

    return v_Other;
  }

  //----------------------------------------------------------------------------
  /*
   * @desc      Convert from IMSI of type hexstring to NAS MobileIdentity
   * @param     p_IMSI
   * @return    template (value) MobileIdentity
   * @status    APPROVED (IMS, IMS_IRAT, LTE, LTE_A_IRAT, LTE_A_R10_R11, LTE_IRAT, NBIOT, NR5GC_IRAT, POS, SSNITZ, UTRAN)
   */
  function f_Imsi2MobileIdentity(hexstring p_IMSI) return template (value) MobileIdentity
  {
    var integer v_ImsiLength := lengthof(p_IMSI);
    var integer v_Odd := (v_ImsiLength rem 2);
    var octetstring v_Other  := f_ImsiImei2Octetstring (p_IMSI);  // @sic R5s100092 sic@
    var B4_Type v_FirstDigit := hex2bit(p_IMSI[0]);
    var B1_Type v_OddEvenInd := int2bit(v_Odd, 1);

    return cds_MobileIdentityImsi_lv(v_FirstDigit, v_OddEvenInd, v_Other);
  }

  //----------------------------------------------------------------------------
  /*
   * @desc      Convert from IMEI of type hexstring to NAS MobileIdentity
   *            When transmitted by the MS the CD/SD bit is set to 0.
   * @param     p_IMEI
   * @param     p_NAS_IdType        (default value: tsc_IdType_IMEI)
   * @return    template (present) MobileIdentity
   * @status    APPROVED (LTE, LTE_A_R10_R11, NBIOT)
   */
  function f_Imei2MobileIdentity(hexstring p_IMEI,
                                 NAS_IdType p_NAS_IdType := tsc_IdType_IMEI) return template (present) MobileIdentity // @sic R5s130758 sic@
  {
    var integer v_ImeiLength := lengthof(p_IMEI);
    var integer v_Odd := (v_ImeiLength rem 2);
    var octetstring v_Other  := f_ImsiImei2Octetstring (substr(p_IMEI, 0, (v_ImeiLength - 1)) & '0'H);  // @sic R5s160006 sic@
    var B4_Type v_FirstDigit := hex2bit(p_IMEI[0]);
    var B1_Type v_OddEvenInd := int2bit(v_Odd, 1);

    return cr_MobileIdentityImei(v_FirstDigit, v_OddEvenInd, v_Other, p_NAS_IdType); // @sic R5s130758 sic@
  }

  //----------------------------------------------------------------------------
  /*
   * @desc      Convert from IMEISV of type hexstring to NAS MobileIdentity
   * @param     p_IMEISV
   * @return    template (present) MobileIdentity
   * @status    APPROVED (LTE, NBIOT)
   */
  function f_Imeisv2MobileIdentity(hexstring p_IMEISV) return template (present) MobileIdentity
  { // @sic R5-131832 sic@
    var integer v_ImeiLength := lengthof(p_IMEISV);
    var integer v_Odd := (v_ImeiLength rem 2);
    var octetstring v_Other  := f_ImsiImei2Octetstring (p_IMEISV);
    var B4_Type v_FirstDigit := hex2bit(p_IMEISV[0]);
    var B1_Type v_OddEvenInd := int2bit(v_Odd, 1);

    return cr_MobileIdentityImeisv(v_FirstDigit, v_OddEvenInd, v_Other);
  }

  //----------------------------------------------------------------------------
  /*
   * @desc      Convert from Emergency Number of type hexstring to octetstring (to be used in Emergency Number List in e.g. Attach Accept)
   * @param     p_EmgNum
   * @return    octetstring
   * @status    APPROVED (ENDC, IMS, IMS_IRAT, LTE, LTE_A_IRAT, LTE_A_PRO, LTE_A_R10_R11, LTE_A_R12, LTE_IRAT, NR5GC, NR5GC_IRAT, POS)
   */
  function fl_EmgNum2Octetstring(hexstring p_EmgNum) return octetstring
  {
    var integer v_Length := lengthof(p_EmgNum);
    var integer v_Odd := (v_Length rem 2);
    var octetstring v_Result := '00'O;
    var integer I;
    var integer K;

    if (v_Odd == 1) {               // If length is odd
      v_Length := v_Length + 1;
      p_EmgNum := p_EmgNum & 'F'H;  // add '1111' on to the end of the Emergency Number
    }

    // Reverse each pair of digits, note that v_Length is even
    K:=0;
    for (I:=0; I < v_Length - 1; I:=I+2) {
      v_Result[K] := hex2oct(p_EmgNum[I+1] & p_EmgNum[I]);
      K := K + 1;
    }

    return v_Result;
  }

  /*
   * @desc      To generate an local emergency number list
   * @param     p_NoOfNums - To specify how many numbers to be included in the list (max = 10)
   * @param     p_NumList
   * @return    template (value) EmergNumList
   * @status    APPROVED (ENDC, IMS, IMS_IRAT, LTE, LTE_A_IRAT, LTE_A_PRO, LTE_A_R10_R11, LTE_A_R12, LTE_IRAT, NR5GC, NR5GC_IRAT, POS)
   */
  function f_Build_EmergNumList(integer p_NoOfNums,
                                EmergencyNumList p_NumList) return  template (value) EmergNumList
  {
    var integer i;
    var template (value) EmergNumList v_EmergNum;
    var octetstring v_LocalNum;
    var integer v_LocalLength := 0;
    var integer v_TotalLength := 0;
    var B7_Type v_ServCat;

    v_EmergNum.iei := '34'O;
    for (i := 0; i < p_NoOfNums; i:=i+1) {
      v_LocalNum := fl_EmgNum2Octetstring (p_NumList[i]);
      v_LocalLength := lengthof(v_LocalNum);
      if (i/2 > 0) {
        v_ServCat := ('00'B & ('00001'B << i));
      } else {
        v_ServCat := ('00'B & ('00001'B << i));
      }
      v_EmergNum.emergNum[i]:= cs_EmergencyLocalNumber(int2oct(v_LocalLength + 1, 1),
                                                       cs_EmergServCat(v_ServCat),
                                                       v_LocalNum);
      v_TotalLength := v_TotalLength + 2 + v_LocalLength; // 1 octet for v_LocalLength + 1 for SCValue + length of number
    }
    v_EmergNum.iel := int2oct (v_TotalLength, 1);
    return v_EmergNum;
  }

  /*
   * @desc      To generate an extended emergency number list
   * @param     p_NoOfNums
   * @param     p_NumList
   * @param     p_EENLV             (default value: '0'B)
   * @param     p_SubServices       (default value: ''O)
   * @return    template (value) ExtdEmergNumList
   * @status    APPROVED (NR5GC)
   */
  function f_Build_ExtdEmergNumList(integer p_NoOfNums,
                                    EmergencyNumList p_NumList,
                                    B1_Type p_EENLV := '0'B,
                                    octetstring p_SubServices := ''O) return  template (value) ExtdEmergNumList
  {
    var integer i;
    var template (value) ExtdEmergNumList v_EmergNum;
    var octetstring v_LocalNum;
    var integer v_LocalLength := 0;
    var integer v_TotalLength := 0;

    v_EmergNum.iei := '7A'O;
    for (i := 0; i < p_NoOfNums; i:=i+1) {
      v_LocalNum := fl_EmgNum2Octetstring (p_NumList[i]);
      v_EmergNum.emergNum[i]:= cs_ExtdEmergNum(v_LocalNum, p_SubServices);
      v_LocalLength := (lengthof(v_LocalNum)+ 2 + lengthof (p_SubServices));
      v_TotalLength := v_TotalLength + v_LocalLength;
    }
    v_EmergNum.spareBits := tsc_Spare7;
    v_EmergNum.extdEmergNumListValidity := p_EENLV;
    v_EmergNum.iel := int2oct (v_TotalLength + 1, 2); // 1 octet for EENLV
    return v_EmergNum;
  }

  /*
   * @desc      Generation of a new list of local emergency numbers different from existing ones
   * @param     p_NoOfNums         .. maximum 20
   * @param     p_ExistingNums
   * @return    EmergencyNumList   .. numbers of 3 decimal digits each
   * @status    APPROVED (ENDC, IMS, IMS_IRAT, LTE, LTE_A_IRAT, LTE_A_PRO, LTE_A_R10_R11, LTE_A_R12, LTE_IRAT, NR5GC_IRAT, POS)
   */
  function f_Get_EmergencyNumList(integer p_NoOfNums,
                                  EmergencyNumList p_ExistingNums) return EmergencyNumList
  { /* @sic R5s210369 sic@ */
    var EmergencyNumList v_NewNumbers;
    var integer v_Number := 115;  // start as implied by LTE test case 11.2.6  @sic R5s141315 sic@
    var boolean v_NumberExists;
    var hexstring v_NumberDigits;
    var integer i;

    if ( p_NoOfNums > 20 ) {   FatalError(__FILE__, __LINE__, "unsupported number of emg nums") }; // max 20 emg nums may be generated

    for (i:=0; i<p_NoOfNums; i:=i+1) { // @sic R5s130748 sic@
      do {
        v_Number := v_Number mod 1000;
        if (v_Number < 100) { v_Number := v_Number + 100}; // ensure it is 3 digits
        v_NumberDigits := str2hex(int2str(v_Number));
        v_NumberExists := fl_CheckEmergencyNum(p_ExistingNums, v_NumberDigits);
        if (v_NumberExists) {
          v_Number := v_Number + 1;              // find another number
        }
      } while (v_NumberExists);
      v_NewNumbers[i] := v_NumberDigits;
      v_Number := v_Number + 1;
    }
    return v_NewNumbers;
  } // f_Get_EmergencyNumList

  /*
   * @desc      Check if an emergency number is contained in an emergency number list or not
   * @param     p_ExistingNums
   * @param     p_NewNum
   * @return    boolean
   * @status    APPROVED (ENDC, IMS, IMS_IRAT, LTE, LTE_A_IRAT, LTE_A_PRO, LTE_A_R10_R11, LTE_A_R12, LTE_IRAT, NR5GC_IRAT, POS)
   */
  function fl_CheckEmergencyNum(EmergencyNumList p_ExistingNums, hexstring p_NewNum) return boolean
  {
    var integer i; // @sic R5s130748 sic@

    for (i := 0; i<lengthof(p_ExistingNums); i := i+1) { /* @sic R5s141315 sic@ @sic R5s210369 sic@ */
      if (p_ExistingNums[i] == p_NewNum){ return true; }
    }
    return false;
  } // fl_CheckEmergencyNum


  /*
   * @desc      check response on AT command from the UE
   * @param     p_AT_Response
   * @param     p_ExpectedString
   * @param     p_Text              (default value: "")
   * @param     p_Expression        (default value: "*[""]([^""]+)[""]*")
   * @param     p_Group             (default value: 0)
   * @status    APPROVED (LTE, NR5GC, SSNITZ)
   */
  function f_AT_ResponseCheck(charstring p_AT_Response,
                              charstring p_ExpectedString,
                              charstring p_Text := "",
                              charstring p_Expression := "*[""]([^""]+)[""]*",
                              integer p_Group := 0)
  { /* p_Expression := "*[""]([^""]+)[""]*";    TTCN-3 regular expression: anything + quote + no quote (at least one character) + quote + anything;
     *                                          => the text between the most inner quotes is returned */
    var charstring v_Expression := p_Expression;
    var charstring v_Result;
    var template charstring v_ExpectedString := pattern p_ExpectedString;
    var boolean v_MatchResult;
    var charstring v_CRLF := oct2char('0D'O) & oct2char('0A'O);

    v_Result := regexp(p_AT_Response, v_Expression, p_Group);

    v_MatchResult := match(v_Result, v_ExpectedString);

    if (v_MatchResult == true) {
      f_SetVerdict(pass,__FILE__, __LINE__, p_Text);
    } else  {
      f_ErrorLog(__FILE__, __LINE__, v_CRLF & "Value expected: " & p_ExpectedString & v_CRLF & "Value received: " & v_Result);  // if anything went wrong, show it
      f_SetVerdict(fail,__FILE__, __LINE__, p_Text);
    }
  }

  /*----------------------------------------------------------------------------
   * @desc      Extract UE security capabilities from IEs and store for use later
   * @param     p_UeNetworkCapability
   * @param     p_MsNetworkCapability
   * @return    UE_SecurityCapability
   * @status    APPROVED (ENDC, IMS, IMS_IRAT, LTE, LTE_A_IRAT, LTE_A_PRO, LTE_A_R10_R11, LTE_A_R12, LTE_IRAT, NBIOT, NR5GC, NR5GC_IRAT, POS)
   */
  function f_EPS_FillNAS_SecurityCapability_Common(UE_NetworkCap p_UeNetworkCapability,
                                                        template (omit) MS_NetworkCap p_MsNetworkCapability) return UE_SecurityCapability
  { /* @sic R5s110014, R5-190717 Renamed to be re-used by NR sic@ */
    var UE_SecurityCapability v_SecurityCapability;
    var MS_NetworkCap v_MS_NetworkCap;
    var B8_Type v_GEA := '00000000'B;
    var integer v_Length := 2;

    v_SecurityCapability.eeaCap := bit2oct(p_UeNetworkCapability.eeaCap); // @sic R5s180271 Baseline Moving 2018 Phase 1 sic@
    v_SecurityCapability.eiaCap := bit2oct(p_UeNetworkCapability.eiaCap); // @sic R5s180271 Baseline Moving 2018 Phase 1 sic@

    if (ispresent(p_UeNetworkCapability.ueaCap)) {
      v_SecurityCapability.ueaCap := bit2oct(p_UeNetworkCapability.ueaCap); // @sic R5s180271 Baseline Moving 2018 Phase 1 sic@
      v_Length := v_Length + 2; // if octet 5 is present, 6 will be too @sic R5s100485 sic@
      if (ispresent(p_UeNetworkCapability.uiaCap)) {
        v_SecurityCapability.uiaCap := bit2oct(p_UeNetworkCapability.uiaCap) and4b '7F'O; // @sic R5s100520, R5s180271 Baseline Moving 2018 Phase 1 sic@
      } else {
        v_SecurityCapability.uiaCap := '00'O; // @sic R5s100485 sic@
      }
    } else {
      v_SecurityCapability.ueaCap := omit;
      v_SecurityCapability.uiaCap := omit; // @sic R5s100184 sic@
    }

    if (isvalue (p_MsNetworkCapability)) {
      v_MS_NetworkCap := valueof(p_MsNetworkCapability);
      if (v_MS_NetworkCap.gea1 == '1'B) {
        v_GEA := v_GEA or4b '01000000'B; // error corrected in delivery
      }
      if (v_MS_NetworkCap.gea2 == '1'B) {
        v_GEA := v_GEA or4b '00100000'B; // error corrected in delivery
      }
      if (v_MS_NetworkCap.gea3 == '1'B) {
        v_GEA := v_GEA or4b '00010000'B; // error corrected in delivery
      }
      if (v_MS_NetworkCap.gea4 == '1'B) {
        v_GEA := v_GEA or4b '00001000'B; // error corrected in delivery
      }
      if (v_MS_NetworkCap.gea5 == '1'B) {
        v_GEA := v_GEA or4b '00000100'B; // error corrected in delivery
      }
      if (v_MS_NetworkCap.gea6 == '1'B) {
        v_GEA := v_GEA or4b '00000010'B; // error corrected in delivery
      }
      if (v_MS_NetworkCap.gea7 == '1'B) {
        v_GEA := v_GEA or4b '00000001'B; // error corrected in delivery
      }
      v_Length := v_Length + 1;
      v_SecurityCapability.geaCap := bit2oct (v_GEA); // Now store the completed GEA octet
      if (v_Length == 3) { // UE didn't report any UTRAN capabilities, so set them to 0 @sic R5s100485 sic@
        v_Length := 5;
        v_SecurityCapability.ueaCap := '00'O;
        v_SecurityCapability.uiaCap := '00'O;
      }
    } else {// MS Network Capability not present @sic R5s100184 sic@
      v_SecurityCapability.geaCap := omit;
      // if UE included UTRAN capabilities but they're all zero
      if (v_SecurityCapability.ueaCap == '00'O and v_SecurityCapability.uiaCap == '00'O) { // @sic R5s130107 sic@
        v_SecurityCapability.ueaCap := omit;
        v_SecurityCapability.uiaCap := omit;
        v_Length := 2;
      }
    }

    v_SecurityCapability.iel := int2oct (v_Length, 1);  // Now store the length

    return v_SecurityCapability;
  }

  /*
   * @desc      Calculate current year from System Time
   * @return    integer
   * @status    APPROVED (LTE, NR5GC, SSNITZ)
   */
  function f_NITZ_GetCurrentYear() return integer
  {
    var Struct_tm_Type v_LocalTime;
    var integer v_TimezoneSeconds;
    fx_GetSystemTime(v_LocalTime, v_TimezoneSeconds);
    return v_LocalTime.tm_year + 1900;
  }

  /*
   * @desc      Convert current year from integer to 1 octet
   * @param     p_Year
   * @return    O1_Type
   * @status    APPROVED (LTE, NR5GC, SSNITZ)
   */
  function f_NITZ_CurrentYear2Oct(integer p_Year) return O1_Type
  {
    return hex2oct(f_SwappedNibbles(p_Year));
  }

  /*
   * @desc      Extract the last 2 digits of the current year (as integer value)
   * @param     p_Year
   * @return    charstring
   * @status    APPROVED (LTE, NR5GC, SSNITZ)
   */
  function f_NITZ_CurrentYear2Str(integer p_Year) return charstring
  {
    return int2str(p_Year mod 100);
  }

}
