/******************************************************************************
 * 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
 *
 ******************************************************************************/
module BitstringTests {

import from Types all;
import from Tools all;

// unaligned
external function enc_bit1(in Bit1 x) return bitstring with { extension "prototype(convert) encode(PER)" }
external function enc_bit2(in Bit2 x) return bitstring with { extension "prototype(convert) encode(PER)" }
external function enc_bit3(in Bit3 x) return bitstring with { extension "prototype(convert) encode(PER)" }
external function enc_bit4(in Bit4 x) return bitstring with { extension "prototype(convert) encode(PER)" }
external function enc_bit5(in Bit5 x) return bitstring with { extension "prototype(convert) encode(PER) errorbehavior(ALL:ERROR)" }
external function enc_bit6(in Bit6 x) return bitstring with { extension "prototype(convert) encode(PER)" }
external function enc_bit7(in Bit7 x) return bitstring with { extension "prototype(convert) encode(PER)" }
external function enc_bit8(in Bit8 x) return bitstring with { extension "prototype(convert) encode(PER)" }
external function enc_bit9(in Bit9 x) return bitstring with { extension "prototype(convert) encode(PER)" }
external function enc_bit10(in Bit10 x) return bitstring with { extension "prototype(convert) encode(PER)" }

external function dec_bit1(in bitstring x) return Bit1 with { extension "prototype(convert) decode(PER)" }
external function dec_bit2(in bitstring x) return Bit2 with { extension "prototype(convert) decode(PER)" }
external function dec_bit3(in bitstring x) return Bit3 with { extension "prototype(convert) decode(PER)" }
external function dec_bit4(in bitstring x) return Bit4 with { extension "prototype(convert) decode(PER)" }
external function dec_bit5(in bitstring x) return Bit5 with { extension "prototype(convert) decode(PER)" }
external function dec_bit6(in bitstring x) return Bit6 with { extension "prototype(convert) decode(PER) errorbehavior(ALL:ERROR)" }
external function dec_bit7(in bitstring x) return Bit7 with { extension "prototype(convert) decode(PER)" }
external function dec_bit8(in bitstring x) return Bit8 with { extension "prototype(convert) decode(PER)" }
external function dec_bit9(in bitstring x) return Bit9 with { extension "prototype(convert) decode(PER)" }
external function dec_bit10(in bitstring x) return Bit10 with { extension "prototype(convert) decode(PER)" }

// aligned
external function enc_bit1_a(in Bit1 x) return bitstring with { extension "prototype(convert) encode(PER:ALIGNED)" }
external function enc_bit2_a(in Bit2 x) return bitstring with { extension "prototype(convert) encode(PER:ALIGNED)" }
external function enc_bit3_a(in Bit3 x) return bitstring with { extension "prototype(convert) encode(PER:ALIGNED)" }
external function enc_bit4_a(in Bit4 x) return bitstring with { extension "prototype(convert) encode(PER:ALIGNED)" }
external function enc_bit5_a(in Bit5 x) return bitstring with { extension "prototype(convert) encode(PER:ALIGNED)" }
external function enc_bit6_a(in Bit6 x) return bitstring with { extension "prototype(convert) encode(PER:ALIGNED)" }
external function enc_bit7_a(in Bit7 x) return bitstring with { extension "prototype(convert) encode(PER:ALIGNED)" }
external function enc_bit8_a(in Bit8 x) return bitstring with { extension "prototype(convert) encode(PER:ALIGNED)" }
external function enc_bit9_a(in Bit9 x) return bitstring with { extension "prototype(convert) encode(PER:ALIGNED)" }
external function enc_bit10_a(in Bit10 x) return bitstring with { extension "prototype(convert) encode(PER:ALIGNED)" }

external function dec_bit1_a(in bitstring x) return Bit1 with { extension "prototype(convert) decode(PER:ALIGNED)" }
external function dec_bit2_a(in bitstring x) return Bit2 with { extension "prototype(convert) decode(PER:ALIGNED)" }
external function dec_bit3_a(in bitstring x) return Bit3 with { extension "prototype(convert) decode(PER:ALIGNED)" }
external function dec_bit4_a(in bitstring x) return Bit4 with { extension "prototype(convert) decode(PER:ALIGNED)" }
external function dec_bit5_a(in bitstring x) return Bit5 with { extension "prototype(convert) decode(PER:ALIGNED)" }
external function dec_bit6_a(in bitstring x) return Bit6 with { extension "prototype(convert) decode(PER:ALIGNED)" }
external function dec_bit7_a(in bitstring x) return Bit7 with { extension "prototype(convert) decode(PER:ALIGNED)" }
external function dec_bit8_a(in bitstring x) return Bit8 with { extension "prototype(convert) decode(PER:ALIGNED)" }
external function dec_bit9_a(in bitstring x) return Bit9 with { extension "prototype(convert) decode(PER:ALIGNED)" }
external function dec_bit10_a(in bitstring x) return Bit10 with { extension "prototype(convert) decode(PER:ALIGNED)" }

testcase tc_per_bitstring_encode_unaligned() runs on CT {
  // zero length
  var Bit1 x1 := ''B;
  var bitstring enc_exp := ''B;
  var bitstring enc := enc_bit1(x1);
  if (enc != enc_exp) {
    setverdict(fail, "Encoding bitstring #1 failed. Got: ", enc, ", expected: ", enc_exp);
  }
  
  // 1 length
  var Bit2 x2 := '1'B;
  enc_exp := '10000000'B;
  enc := enc_bit2(x2);
  if (enc != enc_exp) {
    setverdict(fail, "Encoding bitstring #2 failed. Got: ", enc, ", expected: ", enc_exp);
  }
  
  // 18 length, extendable
  var Bit3 x3 := '101010101010101011'B;
  enc_exp := '010101010101010101100000'B;
  enc := enc_bit3(x3);
  if (enc != enc_exp) {
    setverdict(fail, "Encoding bitstring #3 failed. Got: ", enc, ", expected: ", enc_exp);
  }
  
  // 4 length, extendable
  var Bit4 x4 := '1011'B;
  enc_exp := '01011000'B;
  enc := enc_bit4(x4);
  if (enc != enc_exp) {
    setverdict(fail, "Encoding bitstring #4 failed. Got: ", enc, ", expected: ", enc_exp);
  }
  var Bit4 x5 := '101101'B;
  enc_exp := '1000001101011010'B;
  enc := enc_bit4(x5);
  if (enc != enc_exp) {
    setverdict(fail, "Encoding bitstring #5 failed. Got: ", enc, ", expected: ", enc_exp);
  }
  
  // constrained length
  var Bit5 x6 := '11011'B;
  enc_exp := '10110110'B;
  enc := enc_bit5(x6);
  if (enc != enc_exp) {
    setverdict(fail, "Encoding bitstring #6 failed. Got: ", enc, ", expected: ", enc_exp);
  }
  
  // semi-constrained length (works the same way as if it were unconstrained)
  var Bit6 x7 := '101100010111'B;
  enc_exp := '000011001011000101110000'B;
  enc := enc_bit6(x7);
  if (enc != enc_exp) {
    setverdict(fail, "Encoding bitstring #7 failed. Got: ", enc, ", expected: ", enc_exp);
  }
  
  // unconstrained length
  var Bit7 x8 := '101100010111'B;
  enc_exp := '000011001011000101110000'B;
  enc := enc_bit7(x8);
  if (enc != enc_exp) {
    setverdict(fail, "Encoding bitstring #8 failed. Got: ", enc, ", expected: ", enc_exp);
  }
  
  // huge length (65800 = 64K + 264 = 8225 * 8)
  var Bit8 x9 := oct2bit(f_DFE(8225, false));
  enc_exp := '11000100'B & substr(x9, 0, 4 * c_16K) &
    '1000000100001000'B & substr(x9, 4 * c_16K, 264);
  enc := enc_bit8(x9);
  if (enc != enc_exp) {
    setverdict(fail, "Encoding bitstring #9 failed.");
  }
  
  // named bits, unconstrained
  enc_exp := '0000011110000010'B;
  enc := enc_bit9(bit_val1);
  if (enc != enc_exp) {
    setverdict(fail, "Encoding bitstring #10 failed. Got: ", enc, ", expected: ", enc_exp);
  }
  var Bit9 x10 := '00000000000000000000000'B; // all trailing zeros are removed, and only an empty bitstring is encoded
  enc_exp := '00000000'B;
  enc := enc_bit9(x10);
  if (enc != enc_exp) {
    setverdict(fail, "Encoding bitstring #11 failed. Got: ", enc, ", expected: ", enc_exp);
  }
  
  // named bits, constrained
  enc_exp := '0001001000000000'B;
  enc := enc_bit10(bit_val2);
  if (enc != enc_exp) {
    setverdict(fail, "Encoding bitstring #12 failed. Got: ", enc, ", expected: ", enc_exp);
  }
  var Bit10 x11 := '1100100'B; // one trailing '0' is removed before encoding
  enc_exp := '0001100100000000'B;
  enc := enc_bit10(x11);
  if (enc != enc_exp) {
    setverdict(fail, "Encoding bitstring #13 failed. Got: ", enc, ", expected: ", enc_exp);
  }
  var Bit10 x12 := '1100100100'B; // both trailing '0's are removed before encoding
  enc_exp := '0101100100100000'B;
  enc := enc_bit10(x12);
  if (enc != enc_exp) {
    setverdict(fail, "Encoding bitstring #14 failed. Got: ", enc, ", expected: ", enc_exp);
  }
  setverdict(pass);
}

testcase tc_per_bitstring_encode_aligned() runs on CT {
  // zero length
  var Bit1 x1 := ''B;
  var bitstring enc_exp := ''B;
  var bitstring enc := enc_bit1_a(x1);
  if (enc != enc_exp) {
    setverdict(fail, "Encoding bitstring #1 failed. Got: ", enc, ", expected: ", enc_exp);
  }
  
  // 1 length
  var Bit2 x2 := '1'B;
  enc_exp := '10000000'B;
  enc := enc_bit2_a(x2);
  if (enc != enc_exp) {
    setverdict(fail, "Encoding bitstring #2 failed. Got: ", enc, ", expected: ", enc_exp);
  }
  
  // 18 length, extendable
  var Bit3 x3 := '101010101010101011'B;
  enc_exp := '00000000101010101010101011000000'B;
  enc := enc_bit3_a(x3);
  if (enc != enc_exp) {
    setverdict(fail, "Encoding bitstring #3 failed. Got: ", enc, ", expected: ", enc_exp);
  }
  
  // 4 length, extendable
  var Bit4 x4 := '1011'B;
  enc_exp := '01011000'B;
  enc := enc_bit4_a(x4);
  if (enc != enc_exp) {
    setverdict(fail, "Encoding bitstring #4 failed. Got: ", enc, ", expected: ", enc_exp);
  }
  var Bit4 x5 := '101101'B;
  enc_exp := '100000000000011010110100'B;
  enc := enc_bit4_a(x5);
  if (enc != enc_exp) {
    setverdict(fail, "Encoding bitstring #5 failed. Got: ", enc, ", expected: ", enc_exp);
  }
  
  // constrained length
  var Bit5 x6 := '11011'B;
  enc_exp := '1000000011011000'B;
  enc := enc_bit5_a(x6);
  if (enc != enc_exp) {
    setverdict(fail, "Encoding bitstring #6 failed. Got: ", enc, ", expected: ", enc_exp);
  }
  
  // semi-constrained length (works the same way as if it were unconstrained)
  var Bit6 x7 := '101100010111'B;
  enc_exp := '000011001011000101110000'B;
  enc := enc_bit6_a(x7);
  if (enc != enc_exp) {
    setverdict(fail, "Encoding bitstring #7 failed. Got: ", enc, ", expected: ", enc_exp);
  }
  
  // unconstrained length
  var Bit7 x8 := '101100010111'B;
  enc_exp := '000011001011000101110000'B;
  enc := enc_bit7_a(x8);
  if (enc != enc_exp) {
    setverdict(fail, "Encoding bitstring #8 failed. Got: ", enc, ", expected: ", enc_exp);
  }
  
  // huge length (65800 = 64K + 264 = 8225 * 8)
  var Bit8 x9 := oct2bit(f_DFE(8225, false));
  enc_exp := '11000100'B & substr(x9, 0, 4 * c_16K) &
    '1000000100001000'B & substr(x9, 4 * c_16K, 264);
  enc := enc_bit8_a(x9);
  if (enc != enc_exp) {
    setverdict(fail, "Encoding bitstring #9 failed.");
  }
  
  // named bits, unconstrained
  enc_exp := '0000011110000010'B;
  enc := enc_bit9_a(bit_val1);
  if (enc != enc_exp) {
    setverdict(fail, "Encoding bitstring #10 failed. Got: ", enc, ", expected: ", enc_exp);
  }
  var Bit9 x10 := '00000000000000000000000'B; // all trailing zeros are removed, and only an empty bitstring is encoded
  enc_exp := '00000000'B;
  enc := enc_bit9_a(x10);
  if (enc != enc_exp) {
    setverdict(fail, "Encoding bitstring #11 failed. Got: ", enc, ", expected: ", enc_exp);
  }
  
  // named bits, constrained
  enc_exp := '0000000010010000'B;
  enc := enc_bit10_a(bit_val2);
  if (enc != enc_exp) {
    setverdict(fail, "Encoding bitstring #12 failed. Got: ", enc, ", expected: ", enc_exp);
  }
  var Bit10 x11 := '1100100'B; // one trailing '0' is removed before encoding
  enc_exp := '0000000011001000'B;
  enc := enc_bit10_a(x11);
  if (enc != enc_exp) {
    setverdict(fail, "Encoding bitstring #13 failed. Got: ", enc, ", expected: ", enc_exp);
  }
  var Bit10 x12 := '1100100100'B; // both trailing '0's are removed before encoding
  enc_exp := '0100000011001001'B;
  enc := enc_bit10_a(x12);
  if (enc != enc_exp) {
    setverdict(fail, "Encoding bitstring #14 failed. Got: ", enc, ", expected: ", enc_exp);
  }
  setverdict(pass);
}

testcase tc_per_bitstring_decode_unaligned() runs on CT {
  // zero length
  var bitstring enc := ''B;
  var Bit1 x1 := dec_bit1(enc);
  var Bit1 x1_exp := ''B;
  if (x1 != x1_exp) {
    setverdict(fail, "Decoding bitstring #1 failed. Got: ", x1, ", expected: ", x1_exp);
  }
  
  // 1 length
  enc := '10000000'B;
  var Bit2 x2 := dec_bit2(enc);
  var Bit2 x2_exp := '1'B;
  if (x2 != x2_exp) {
    setverdict(fail, "Decoding bitstring #2 failed. Got: ", x2, ", expected: ", x2_exp);
  }
  
  // 18 length, extendable
  enc := '010101010101010101100000'B;
  var Bit3 x3 := dec_bit3(enc);
  var Bit3 x3_exp := '101010101010101011'B;
  if (x3 != x3_exp) {
    setverdict(fail, "Decoding bitstring #3 failed. Got: ", x3, ", expected: ", x3_exp);
  }
  
  // 4 length, extendable
  enc := '01011000'B;
  var Bit4 x4 := dec_bit4(enc);
  var Bit4 x4_exp := '1011'B;
  if (x4 != x4_exp) {
    setverdict(fail, "Decoding bitstring #4 failed. Got: ", x4, ", expected: ", x4_exp);
  }
  enc := '1000001101011010'B;
  var Bit4 x5 := dec_bit4(enc);
  var Bit4 x5_exp := '101101'B;
  if (x5 != x5_exp) {
    setverdict(fail, "Decoding bitstring #5 failed. Got: ", x5, ", expected: ", x5_exp);
  }
  
  // constrained length
  enc := '10110110'B;
  var Bit5 x6 := dec_bit5(enc);
  var Bit5 x6_exp := '11011'B;
  if (x6 != x6_exp) {
    setverdict(fail, "Decoding bitstring #6 failed. Got: ", x6, ", expected: ", x6_exp);
  }
  
  // semi-constrained length (works the same way as if it were unconstrained)
  enc := '000011001011000101110000'B;
  var Bit6 x7 := dec_bit6(enc);
  var Bit6 x7_exp := '101100010111'B;
  if (x7 != x7_exp) {
    setverdict(fail, "Decoding bitstring #7 failed. Got: ", x7, ", expected: ", x7_exp);
  }
  
  // unconstrained length
  enc := '000011001011000101110000'B;
  var Bit7 x8 := dec_bit7(enc);
  var Bit7 x8_exp := '101100010111'B;
  if (x8 != x8_exp) {
    setverdict(fail, "Decoding bitstring #8 failed. Got: ", x8, ", expected: ", x8_exp);
  }
  
  // huge length (65800 = 64K + 264 = 8225 * 8)
  var Bit8 x9_exp := oct2bit(f_DFE(8225, false));
  enc := '11000100'B & substr(x9_exp, 0, 4 * c_16K) &
    '1000000100001000'B & substr(x9_exp, 4 * c_16K, 264);
  var Bit8 x9 := dec_bit8(enc);
  if (x9 != x9_exp) {
    setverdict(fail, "Decoding bitstring #9 failed.");
  }
  
  // named bits, unconstrained
  enc := '0000011110000010'B;
  var Bit9 x10 := dec_bit9(enc);
  if (x10 != bit_val1) {
    setverdict(fail, "Decoding bitstring #10 failed. Got: ", x10, ", expected: ", bit_val1);
  }
  
  // named bits, constrained
  enc := '0001001000000000'B;
  var Bit10 x11 := dec_bit10(enc);
  if (x11 != bit_val2) {
    setverdict(fail, "Decoding bitstring #11 failed. Got: ", x11, ", expected: ", bit_val2);
  }
  setverdict(pass);
}

testcase tc_per_bitstring_decode_aligned() runs on CT {
  // zero length
  var bitstring enc := ''B;
  var Bit1 x1 := dec_bit1_a(enc);
  var Bit1 x1_exp := ''B;
  if (x1 != x1_exp) {
    setverdict(fail, "Decoding bitstring #1 failed. Got: ", x1, ", expected: ", x1_exp);
  }
  
  // 1 length
  enc := '10000000'B;
  var Bit2 x2 := dec_bit2_a(enc);
  var Bit2 x2_exp := '1'B;
  if (x2 != x2_exp) {
    setverdict(fail, "Decoding bitstring #2 failed. Got: ", x2, ", expected: ", x2_exp);
  }
  
  // 18 length, extendable
  enc := '00000000101010101010101011000000'B;
  var Bit3 x3 := dec_bit3_a(enc);
  var Bit3 x3_exp := '101010101010101011'B;
  if (x3 != x3_exp) {
    setverdict(fail, "Decoding bitstring #3 failed. Got: ", x3, ", expected: ", x3_exp);
  }
  
  // 4 length, extendable
  enc := '01011000'B;
  var Bit4 x4 := dec_bit4_a(enc);
  var Bit4 x4_exp := '1011'B;
  if (x4 != x4_exp) {
    setverdict(fail, "Decoding bitstring #4 failed. Got: ", x4, ", expected: ", x4_exp);
  }
  enc := '100000000000011010110100'B;
  var Bit4 x5 := dec_bit4_a(enc);
  var Bit4 x5_exp := '101101'B;
  if (x5 != x5_exp) {
    setverdict(fail, "Decoding bitstring #5 failed. Got: ", x5, ", expected: ", x5_exp);
  }
  
  // constrained length
  enc := '1000000011011000'B;
  var Bit5 x6 := dec_bit5_a(enc);
  var Bit5 x6_exp := '11011'B;
  if (x6 != x6_exp) {
    setverdict(fail, "Decoding bitstring #6 failed. Got: ", x6, ", expected: ", x6_exp);
  }
  
  // semi-constrained length (works the same way as if it were unconstrained)
  enc := '000011001011000101110000'B;
  var Bit6 x7 := dec_bit6_a(enc);
  var Bit6 x7_exp := '101100010111'B;
  if (x7 != x7_exp) {
    setverdict(fail, "Decoding bitstring #7 failed. Got: ", x7, ", expected: ", x7_exp);
  }
  
  // unconstrained length
  enc := '000011001011000101110000'B;
  var Bit7 x8 := dec_bit7_a(enc);
  var Bit7 x8_exp := '101100010111'B;
  if (x8 != x8_exp) {
    setverdict(fail, "Decoding bitstring #8 failed. Got: ", x8, ", expected: ", x8_exp);
  }
  
  // huge length (65800 = 64K + 264 = 8225 * 8)
  var Bit8 x9_exp := oct2bit(f_DFE(8225, false));
  enc := '11000100'B & substr(x9_exp, 0, 4 * c_16K) &
    '1000000100001000'B & substr(x9_exp, 4 * c_16K, 264);
  var Bit8 x9 := dec_bit8_a(enc);
  if (x9 != x9_exp) {
    setverdict(fail, "Decoding bitstring #9 failed.");
  }
  
  // named bits, unconstrained
  enc := '0000011110000010'B;
  var Bit9 x10 := dec_bit9_a(enc);
  if (x10 != bit_val1) {
    setverdict(fail, "Decoding bitstring #10 failed. Got: ", x10, ", expected: ", bit_val1);
  }
  
  // named bits, constrained
  enc := '0000000010010000'B;
  var Bit10 x11 := dec_bit10_a(enc);
  if (x11 != bit_val2) {
    setverdict(fail, "Decoding bitstring #11 failed. Got: ", x11, ", expected: ", bit_val2);
  }
  setverdict(pass);
}

testcase tc_per_bitstring_negtest() runs on CT {
  var bitstring x;
  var bitstring buf;
  @try {
    buf := enc_bit3(x);
    setverdict(fail, "Bitstring #1 encoded successfully.");
  }
  @catch (msg) {
    if (not match(msg, pattern "*Encoding an unbound BIT STRING value.")) {
      setverdict(fail, "Invalid error message received (#1): ", msg);
    }
  }
  x := '10'B;
  @try {
    buf := enc_bit5(x);
    setverdict(fail, "Bitstring #2 encoded successfully.");
  }
  @catch (msg) {
    if (not match(msg, pattern "*Encoding an invalid BIT STRING value \(does not match PER-visible constraints\).")) {
      setverdict(fail, "Invalid error message received (#2): ", msg);
    }
  }
  buf := ''B;
  @try {
    x := dec_bit3(buf);
    setverdict(fail, "Bitstring #3 decoded successfully.");
  }
  @catch (msg) {
    if (not match(msg, pattern "*Buffer overflow while PER-decoding. Requested bits: 1, remaining bits in buffer: 0")) {
      setverdict(fail, "Invalid error message received (#3): ", msg);
    }
  }
  buf := '0000000100000000'B;
  @try {
    x := dec_bit6(buf);
    setverdict(fail, "Bitstring #4 decoded successfully.");
  }
  @catch (msg) {
    if (not match(msg, pattern "*Decoded BIT STRING value does not match PER-visible constraints.")) {
      setverdict(fail, "Invalid error message received (#4): ", msg);
    }
  }
  setverdict(pass);
}
  
control {
  execute(tc_per_bitstring_encode_unaligned());
  execute(tc_per_bitstring_encode_aligned());
  execute(tc_per_bitstring_decode_unaligned());
  execute(tc_per_bitstring_decode_aligned());
  execute(tc_per_bitstring_negtest());
}

}
