/******************************************************************************
 * Copyright (c) 2000-2021 Ericsson Telecom AB
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v2.0
 * which accompanies this distribution, and is available at
 * https://www.eclipse.org/org/documents/epl-2.0/EPL-2.0.html
 *
 * Contributors:
 *   Balasko, Jeno
 *   Baranyi, Botond
 *   Szabo, Bence Janos
 *
 ******************************************************************************/

// This module contains tests for the variant attributes defined in Annex E of the
// TTCN-3 language standard.
// The encoding of the types with the new variants are compared to the encoding of
// types declared using traditional RAW variant attributes (whenever possible).

// New attributes and their equivalents:
// "N bit" == "FIELDLENGTH(N), COMP(signbit), BYTEORDER(last)"
// "unsigned N bit" == "FIELDLENGTH(N), COMP(nosign), BYTEORDER(last)"
// "IEEE754 float" == "FORMAT(IEEE754 float)"
// "IEEE754 double" == "FORMAT(IEEE754 double)"

// Other new attributes and their meaning:
// "UTF-8", "UTF-16" - universal charstring encoding types, they are equivalent to calling
// oct2unichar or unichar2oct
module Annex_E_variants {

// E.2.1.0 Signed and unsigned single byte integers
type integer Byte (-128 .. 127) with { variant "8 bits" }; // bits is also supported and does the same as bit
type integer ByteEq (-128 .. 127) with { variant "FIELDLENGTH(8), COMP(signbit), BYTEORDER(last)" };

type integer UnsignedByte (0 .. 255) with { variant "unsigned 8 bits" }; // bits is also supported and does the same as bit
type integer UnsignedByteEq (0 .. 255) with { variant "FIELDLENGTH(8), COMP(nosign), BYTEORDER(last)" };

// E.2.1.1 Signed and unsigned short integers
type integer Short (-32768 .. 32767) with { variant "16 bit" };
type integer ShortEq (-32768 .. 32767) with { variant "FIELDLENGTH(16), COMP(signbit), BYTEORDER(last)" };

type integer UnsignedShort (0 .. 65535) with { variant "unsigned 16 bit" };
type integer UnsignedShortEq (0 .. 65535) with { variant "FIELDLENGTH(16), COMP(nosign), BYTEORDER(last)" };

// E.2.1.2 Signed and unsigned long integers
type integer Long (-2147483648 .. 2147483647) with { variant "32 bit" };
type integer LongEq (-2147483648 .. 2147483647) with { variant "FIELDLENGTH(32), COMP(signbit), BYTEORDER(last)" };

type integer UnsignedLong (0 .. 4294967295) with { variant "unsigned 32 bit" };
type integer UnsignedLongEq (0 .. 4294967295) with { variant "FIELDLENGTH(32), COMP(nosign), BYTEORDER(last)" };

// E.2.1.3 Signed and unsigned longlong integers
type integer LongLong (-9223372036854775808 .. 9223372036854775807) with { variant "64 bit" };
type integer LongLongEq (-9223372036854775808 .. 9223372036854775807) with { variant "FIELDLENGTH(64), COMP(signbit), BYTEORDER(last)" };

type integer UnsignedLongLong (0 .. 18446744073709551615) with { variant "unsigned 64 bit" };
type integer UnsignedLongLongEq (0 .. 18446744073709551615) with { variant "FIELDLENGTH(64), COMP(nosign), BYTEORDER(last)" };

// Custom integer types
type integer Int6 with { variant "6 bit" };
type integer Int6Eq with { variant "FIELDLENGTH(6), COMP(signbit), BYTEORDER(last)" };

type integer UInt4 with { variant "unsigned 4 bit" };
type integer UInt4Eq with { variant "FIELDLENGTH(4), COMP(nosign), BYTEORDER(last)" };

type integer BigInt with { variant "300 bit " };
type integer BigIntEq with { variant "FIELDLENGTH(300), COMP(signbit), BYTEORDER(last)" };

// Using the "N bit" attribute on enumerated types
type enumerated Enum5 { val1(1), val7(7), val2(2), val11(11) } with { variant "unsigned 5 bit" };
type enumerated Enum5Eq { val1(1), val7(7), val2(2), val11(11) } with { variant "FIELDLENGTH(5), COMP(nosign), BYTEORDER(last)" };

type enumerated Enum12 { val30(30), valm10(-10), val9(9) } with { variant "12 bit" };
type enumerated Enum12Eq { val30(30), valm10(-10), val9(9) } with { variant "FIELDLENGTH(12), COMP(signbit), BYTEORDER(last)" };

// Using the "N bit" attribute on boolean types
type boolean Bool3 with { variant "3 bit" };
type boolean Bool3Eq with { variant "FIELDLENGTH(3), BYTEORDER(last)" };

type boolean Bool13 with { variant "13 bit" };
type boolean Bool13Eq with { variant "FIELDLENGTH(13), BYTEORDER(last)" };

// Using the "N bit" attribute on string types
type bitstring BStr14 with { variant "14 bit" };
type bitstring BStr14Eq with { variant "FIELDLENGTH(14), BYTEORDER(last)" };

type hexstring HStr20 with { variant "unsigned 20 bit" }; // 20 bits = 5 hex nibbles, 'unsigned' is ignored
type hexstring HStr20Eq with { variant "FIELDLENGTH(5), BYTEORDER(last)" };

type octetstring OStr32 with { variant "32 bit" }; // 32 bits = 4 octets
type octetstring OStr32Eq with { variant "FIELDLENGTH(4), BYTEORDER(last)" };

type charstring CStr64 with { variant "64 bit" }; // 64 bits = 8 characters
type charstring CStr64Eq with { variant "FIELDLENGTH(8), BYTEORDER(last)" };

type universal charstring UStr80 with { variant "80 bit" }; // 80 bits = 10 UTF-8 characters
type universal charstring UStr80Eq with { variant "FIELDLENGTH(10), BYTEORDER(last)" };

// E.2.1.4 IEEE 754 (TM) floats
type float IEEE754Float with { variant "IEEE754 float" };
type float IEEE754FloatEq with { variant "FORMAT(IEEE754 float)" };

type float IEEE754Double with { variant "IEEE754 double" };
type float IEEE754DoubleEq with { variant "FORMAT(IEEE754 double)" };

// E.2.2.0 UTF-8 characters string
type universal charstring UTF8String with { variant "UTF-8" };

// E.2.2.1 and E.2.2.2 UTF-16 characters string
type universal charstring UTF16String with { variant "UTF-16" };

// Component type
type component CT {}

// Test cases
testcase tc_byte() runs on CT
{
  var Byte x := -12;
  var ByteEq x_eq := x;
  var bitstring enc := encvalue(x);
  var bitstring enc_exp := encvalue(x_eq);
  if (enc != enc_exp) {
    setverdict(fail, "Expected: ", enc_exp, ", got: ", enc);
  }
  else {
    var Byte dec;
    if (decvalue(enc, dec) != 0) {
      setverdict(fail, "Could not decode ", enc);
    }
    else if (dec != x) {
      setverdict(fail, "Expected: ", x, ", got: ", dec);
    }
  }
  setverdict(pass);
}

testcase tc_unsigned_byte() runs on CT
{
  var UnsignedByte x := 91;
  var UnsignedByteEq x_eq := x;
  var bitstring enc := encvalue(x);
  var bitstring enc_exp := encvalue(x_eq);
  if (enc != enc_exp) {
    setverdict(fail, "Expected: ", enc_exp, ", got: ", enc);
  }
  else {
    var UnsignedByte dec;
    if (decvalue(enc, dec) != 0) {
      setverdict(fail, "Could not decode ", enc);
    }
    else if (dec != x) {
      setverdict(fail, "Expected: ", x, ", got: ", dec);
    }
  }
  setverdict(pass);
}

testcase tc_short() runs on CT
{
  var Short x := 399;
  var ShortEq x_eq := x;
  var bitstring enc := encvalue(x);
  var bitstring enc_exp := encvalue(x_eq);
  if (enc != enc_exp) {
    setverdict(fail, "Expected: ", enc_exp, ", got: ", enc);
  }
  else {
    var Short dec;
    if (decvalue(enc, dec) != 0) {
      setverdict(fail, "Could not decode ", enc);
    }
    else if (dec != x) {
      setverdict(fail, "Expected: ", x, ", got: ", dec);
    }
  }
  setverdict(pass);
}

testcase tc_unsigned_short() runs on CT
{
  var UnsignedShort x := 399;
  var UnsignedShortEq x_eq := x;
  var bitstring enc := encvalue(x);
  var bitstring enc_exp := encvalue(x_eq);
  if (enc != enc_exp) {
    setverdict(fail, "Expected: ", enc_exp, ", got: ", enc);
  }
  else {
    var UnsignedShort dec;
    if (decvalue(enc, dec) != 0) {
      setverdict(fail, "Could not decode ", enc);
    }
    else if (dec != x) {
      setverdict(fail, "Expected: ", x, ", got: ", dec);
    }
  }
  setverdict(pass);
}

testcase tc_long() runs on CT
{
  var Long x := 1457664;
  var LongEq x_eq := x;
  var bitstring enc := encvalue(x);
  var bitstring enc_exp := encvalue(x_eq);
  if (enc != enc_exp) {
    setverdict(fail, "Expected: ", enc_exp, ", got: ", enc);
  }
  else {
    var Long dec;
    if (decvalue(enc, dec) != 0) {
      setverdict(fail, "Could not decode ", enc);
    }
    else if (dec != x) {
      setverdict(fail, "Expected: ", x, ", got: ", dec);
    }
  }
  setverdict(pass);
}

testcase tc_unsigned_long() runs on CT
{
  var UnsignedLong x := 1457664;
  var UnsignedLongEq x_eq := x;
  var bitstring enc := encvalue(x);
  var bitstring enc_exp := encvalue(x_eq);
  if (enc != enc_exp) {
    setverdict(fail, "Expected: ", enc_exp, ", got: ", enc);
  }
  else {
    var UnsignedLong dec;
    if (decvalue(enc, dec) != 0) {
      setverdict(fail, "Could not decode ", enc);
    }
    else if (dec != x) {
      setverdict(fail, "Expected: ", x, ", got: ", dec);
    }
  }
  setverdict(pass);
}

testcase tc_long_long() runs on CT
{
  var LongLong x := -9223372036854775807;
  var LongLongEq x_eq := x;
  var bitstring enc := encvalue(x);
  var bitstring enc_exp := encvalue(x_eq);
  if (enc != enc_exp) {
    setverdict(fail, "Expected: ", enc_exp, ", got: ", enc);
  }
  else {
    var LongLong dec;
    if (decvalue(enc, dec) != 0) {
      setverdict(fail, "Could not decode ", enc);
    }
    else if (dec != x) {
      setverdict(fail, "Expected: ", x, ", got: ", dec);
    }
  }
  setverdict(pass);
}

testcase tc_unsigned_long_long() runs on CT
{
  var UnsignedLongLong x := 18446744073709551610;
  var UnsignedLongLongEq x_eq := x;
  var bitstring enc := encvalue(x);
  var bitstring enc_exp := encvalue(x_eq);
  if (enc != enc_exp) {
    setverdict(fail, "Expected: ", enc_exp, ", got: ", enc);
  }
  else {
    var UnsignedLongLong dec;
    if (decvalue(enc, dec) != 0) {
      setverdict(fail, "Could not decode ", enc);
    }
    else if (dec != x) {
      setverdict(fail, "Expected: ", x, ", got: ", dec);
    }
  }
  setverdict(pass);
}

testcase tc_int6() runs on CT
{
  var Int6 x := -19;
  var Int6Eq x_eq := x;
  var bitstring enc := encvalue(x);
  var bitstring enc_exp := encvalue(x_eq);
  if (enc != enc_exp) {
    setverdict(fail, "Expected: ", enc_exp, ", got: ", enc);
  }
  else {
    var Int6 dec;
    if (decvalue(enc, dec) != 0) {
      setverdict(fail, "Could not decode ", enc);
    }
    else if (dec != x) {
      setverdict(fail, "Expected: ", x, ", got: ", dec);
    }
  }
  setverdict(pass);
}

testcase tc_uint4() runs on CT
{
  var UInt4 x := 7;
  var UInt4Eq x_eq := x;
  var bitstring enc := encvalue(x);
  var bitstring enc_exp := encvalue(x_eq);
  if (enc != enc_exp) {
    setverdict(fail, "Expected: ", enc_exp, ", got: ", enc);
  }
  else {
    var UInt4 dec;
    if (decvalue(enc, dec) != 0) {
      setverdict(fail, "Could not decode ", enc);
    }
    else if (dec != x) {
      setverdict(fail, "Expected: ", x, ", got: ", dec);
    }
  }
  setverdict(pass);
}

testcase tc_big_int() runs on CT
{
  var BigInt x := -2837219487639565876438796348973264327463294623463287046324783264987325432;
  var BigIntEq x_eq := x;
  var bitstring enc := encvalue(x);
  var bitstring enc_exp := encvalue(x_eq);
  if (enc != enc_exp) {
    setverdict(fail, "Expected: ", enc_exp, ", got: ", enc);
  }
  else {
    var BigInt dec;
    if (decvalue(enc, dec) != 0) {
      setverdict(fail, "Could not decode ", enc);
    }
    else if (dec != x) {
      setverdict(fail, "Expected: ", x, ", got: ", dec);
    }
  }
  setverdict(pass);
}

testcase tc_enum5() runs on CT
{
  var Enum5 x := val11;
  var Enum5Eq x_eq := val11;
  var bitstring enc := encvalue(x);
  var bitstring enc_exp := encvalue(x_eq);
  if (enc != enc_exp) {
    setverdict(fail, "Expected: ", enc_exp, ", got: ", enc);
  }
  else {
    var Enum5 dec;
    if (decvalue(enc, dec) != 0) {
      setverdict(fail, "Could not decode ", enc);
    }
    else if (dec != x) {
      setverdict(fail, "Expected: ", x, ", got: ", dec);
    }
  }
  setverdict(pass);
}

testcase tc_enum12() runs on CT
{
  var Enum12 x := valm10;
  var Enum12Eq x_eq := valm10;
  var bitstring enc := encvalue(x);
  var bitstring enc_exp := encvalue(x_eq);
  if (enc != enc_exp) {
    setverdict(fail, "Expected: ", enc_exp, ", got: ", enc);
  }
  else {
    var Enum12 dec;
    if (decvalue(enc, dec) != 0) {
      setverdict(fail, "Could not decode ", enc);
    }
    else if (dec != x) {
      setverdict(fail, "Expected: ", x, ", got: ", dec);
    }
  }
  setverdict(pass);
}

testcase tc_bool3() runs on CT
{
  var Bool3 x := true;
  var Bool3Eq x_eq := x;
  var bitstring enc := encvalue(x);
  var bitstring enc_exp := encvalue(x_eq);
  if (enc != enc_exp) {
    setverdict(fail, "Expected: ", enc_exp, ", got: ", enc);
  }
  else {
    var Bool3 dec;
    if (decvalue(enc, dec) != 0) {
      setverdict(fail, "Could not decode ", enc);
    }
    else if (dec != x) {
      setverdict(fail, "Expected: ", x, ", got: ", dec);
    }
  }
  setverdict(pass);
}

testcase tc_bool13() runs on CT
{
  var Bool13 x := true;
  var Bool13Eq x_eq := x;
  var bitstring enc := encvalue(x);
  var bitstring enc_exp := encvalue(x_eq);
  if (enc != enc_exp) {
    setverdict(fail, "Expected: ", enc_exp, ", got: ", enc);
  }
  else {
    var Bool13 dec;
    if (decvalue(enc, dec) != 0) {
      setverdict(fail, "Could not decode ", enc);
    }
    else if (dec != x) {
      setverdict(fail, "Expected: ", x, ", got: ", dec);
    }
  }
  setverdict(pass);
}

testcase tc_bstr14() runs on CT
{
  var BStr14 x := '10101010101010'B;
  var BStr14Eq x_eq := x;
  var bitstring enc := encvalue(x);
  var bitstring enc_exp := encvalue(x_eq);
  if (enc != enc_exp) {
    setverdict(fail, "Expected: ", enc_exp, ", got: ", enc);
  }
  else {
    var BStr14 dec;
    if (decvalue(enc, dec) != 0) {
      setverdict(fail, "Could not decode ", enc);
    }
    else if (dec != x) {
      setverdict(fail, "Expected: ", x, ", got: ", dec);
    }
  }
  setverdict(pass);
}

testcase tc_hstr20() runs on CT
{
  var HStr20 x := 'CCCCC'H;
  var HStr20Eq x_eq := x;
  var bitstring enc := encvalue(x);
  var bitstring enc_exp := encvalue(x_eq);
  if (enc != enc_exp) {
    setverdict(fail, "Expected: ", enc_exp, ", got: ", enc);
  }
  else {
    var HStr20 dec;
    if (decvalue(enc, dec) != 0) {
      setverdict(fail, "Could not decode ", enc);
    }
    else if (dec != x) {
      setverdict(fail, "Expected: ", x, ", got: ", dec);
    }
  }
  setverdict(pass);
}

testcase tc_ostr32() runs on CT
{
  var OStr32 x := 'ABABABAB'O;
  var OStr32Eq x_eq := x;
  var bitstring enc := encvalue(x);
  var bitstring enc_exp := encvalue(x_eq);
  if (enc != enc_exp) {
    setverdict(fail, "Expected: ", enc_exp, ", got: ", enc);
  }
  else {
    var OStr32 dec;
    if (decvalue(enc, dec) != 0) {
      setverdict(fail, "Could not decode ", enc);
    }
    else if (dec != x) {
      setverdict(fail, "Expected: ", x, ", got: ", dec);
    }
  }
  setverdict(pass);
}

testcase tc_cstr64() runs on CT
{
  var CStr64 x := "Hello";
  var CStr64Eq x_eq := x;
  var bitstring enc := encvalue(x);
  var bitstring enc_exp := encvalue(x_eq);
  if (enc != enc_exp) {
    setverdict(fail, "Expected: ", enc_exp, ", got: ", enc);
  }
  else {
    var CStr64 dec;
    var CStr64Eq dec_exp;
    if (decvalue(enc, dec) != 0 or decvalue(enc_exp, dec_exp) != 0) {
      setverdict(fail, "Could not decode ", enc);
    }
    else if (dec != dec_exp) {
      setverdict(fail, "Expected: ", dec_exp, ", got: ", dec);
    }
  }
  setverdict(pass);
}

testcase tc_ustr80() runs on CT
{
  var UStr80 x := "sepr" & char(0, 0, 1, 113);
  var UStr80Eq x_eq := x;
  var bitstring enc := encvalue(x);
  var bitstring enc_exp := encvalue(x_eq);
  if (enc != enc_exp) {
    setverdict(fail, "Expected: ", enc_exp, ", got: ", enc);
  }
  else {
    var UStr80 dec;
    var UStr80Eq dec_exp;
    if (decvalue(enc, dec) != 0 or decvalue(enc_exp, dec_exp) != 0) {
      setverdict(fail, "Could not decode ", enc);
    }
    else if (dec != dec_exp) {
      setverdict(fail, "Expected: ", dec_exp, ", got: ", dec);
    }
  }
  setverdict(pass);
}

testcase tc_ieee754_float() runs on CT
{
  var IEEE754Float x := -391.582;
  var IEEE754FloatEq x_eq := x;
  var bitstring enc := encvalue(x);
  var bitstring enc_exp := encvalue(x_eq);
  if (enc != enc_exp) {
    setverdict(fail, "Expected: ", enc_exp, ", got: ", enc);
  }
  else {
    var IEEE754Float dec;
    var float eps := 0.0001;
    if (decvalue(enc, dec) != 0) {
      setverdict(fail, "Could not decode ", enc);
    }
    else if (dec - x > eps or dec - x < -eps) {
      setverdict(fail, "Expected: ", x, ", got: ", dec);
    }
  }
  setverdict(pass);
}

testcase tc_ieee754_double() runs on CT
{
  var IEEE754Double x := 1165.141997;
  var IEEE754DoubleEq x_eq := x;
  var bitstring enc := encvalue(x);
  var bitstring enc_exp := encvalue(x_eq);
  if (enc != enc_exp) {
    setverdict(fail, "Expected: ", enc_exp, ", got: ", enc);
  }
  else {
    var IEEE754Double dec;
    var float eps := 0.000001;
    if (decvalue(enc, dec) != 0) {
      setverdict(fail, "Could not decode ", enc);
    }
    else if (dec - x > eps or dec - x < -eps) {
      setverdict(fail, "Expected: ", x, ", got: ", dec);
    }
  }
  setverdict(pass);
}

testcase tc_utf8() runs on CT
{
  var UTF8String x := "árvíztűrő tükörfúrógép";
  var bitstring enc := encvalue(x);
  var bitstring enc_exp := oct2bit(unichar2oct(x, "UTF-8"));
  if (enc != enc_exp) {
    setverdict(fail, "Expected: ", enc_exp, ", got: ", enc);
  }
  else {
    var UTF8String dec;
    if (decvalue(enc, dec) != 0) {
      setverdict(fail, "Could not decode ", enc);
    }
    else if (dec != x) {
      setverdict(fail, "Expected: ", x, ", got: ", dec);
    }
  }
  setverdict(pass);
}

testcase tc_utf16() runs on CT
{
  var UTF16String x := "árvíztűrő tükörfúrógép";
  var bitstring enc := encvalue(x);
  var bitstring enc_exp := oct2bit(unichar2oct(x, "UTF-16"));
  if (enc != enc_exp) {
    setverdict(fail, "Expected: ", enc_exp, ", got: ", enc);
  }
  else {
    var UTF16String dec;
    if (decvalue(enc, dec) != 0) {
      setverdict(fail, "Could not decode ", enc);
    }
    else if (dec != x) {
      setverdict(fail, "Expected: ", x, ", got: ", dec);
    }
  }
  setverdict(pass);
}

// Control part
control {
  execute(tc_byte());
  execute(tc_unsigned_byte());
  execute(tc_short());
  execute(tc_unsigned_short());
  execute(tc_long());
  execute(tc_unsigned_long());
  execute(tc_long_long());
  execute(tc_unsigned_long_long());
  execute(tc_int6());
  execute(tc_uint4());
  execute(tc_big_int());
  execute(tc_enum5());
  execute(tc_enum12());
  execute(tc_bool3());
  execute(tc_bool13());
  execute(tc_bstr14());
  execute(tc_hstr20());
  execute(tc_ostr32());
  execute(tc_cstr64());
  execute(tc_ustr80());
  execute(tc_ieee754_float());
  execute(tc_ieee754_double());
  execute(tc_utf8());
  execute(tc_utf16());
}

}
with {
  encode "RAW";
}
