/******************************************************************************
 * 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:
 *   Balasko, Jeno
 *   Kovacs, Ferenc
 *
 ******************************************************************************/
module Compat
{
import from Ext all
import from ASN_Ext all
import from Std all

type component Empty {
  timer T
  port MyPort1 P, Q
  port MyPort2 X
}

//** MODULEPARS **//
modulepar myrec1 mp1 := { 1, 1 }
modulepar myrec2 mp2 := { 0, 0 }


//** PORTS **//
// Compatibility means equivalence for port types.  Just as before.  (Alias
// type like "iammyrec1" are rejected, it's not a real type.)
type port MyPort1 message {
  inout myrec1
  inout myrec2
  inout integer
  inout intlist4
} with { extension "internal" }

type port MyPort2 procedure {
  inout mysig
} with { extension "internal" }


//** RECORDS **//
type record myrec1 {
  integer f1 optional,
  integer f2
} with { variant "" encode "RAW" }
// Alias type.  Just a "typedef" in the code.
type myrec1 iammyrec1
// Same as "myrec1", but a different class.  It's a real new type and
// compatible, but not the same.  0-level structure.
type record myrec2 {
  integer f1 optional,
  integer f2
} with { variant "" encode "RAW" }
// "myrec3" and "myrec4" are a little bit more complicated, but compatible
// types.  1-level structure.
type record myrec3 {
  integer f1,
  myrec1 f2,
  myrec2 f3,
  integer f4 optional
}
type record myrec4 {
  integer f1,
  myrec2 f2,
  myrec1 f3,
  integer f4 optional
}

type record myrec5 {
  myrec1 f1,
  myrec2 f2
}
type record myrec6 {
  myrec2 f1,
  myrec1 f2
}
type record myrec7 {
  myrec5 f1,
  myrec6 f2
}

type record myrec8 {
  myarr1 f1,
  myarr1 f2
}


//** RECORDOFS **//
type record length(2..2) of integer intlist1
type record length(4..4) of integer intlist2
type record of myrec5 intlist3
type record of integer intlist4 with { variant "" encode "RAW" }
type record of myrec1 mr1list
type record of myrec2 mr2list


//** ARRAYS **//
type integer myarr1[2]
type integer myarr2[1..2]
type integer myarr3[1][1]
type integer myarr4[1][2]
type myrec1 myarr5[2]
type myrec2 myarr6[2]


//** UNIONS **//
type union myuni1 {
  myrec1 f1,
  myrec2 f2
}
type union myuni2 {
  intlist2 f1,
  intlist1 f2
}

//** SIGNATURES **//
signature mysig() return intlist4 exception (intlist4, myrec5);


//** TEMPLATES **//
template myrec1 t_rec1 := { 1, 2 }
template myrec2 t_rec2 := { 2, 3 }
template myrec3 t_rec3 := { 99, { 88, 77 }, { ?, ? /* * */ }, ? }
template myrec4 t_rec4 := { 77, { 66, 55 }, { *, ? }, * }
template myrec2 t_rec5 := t_rec1
template intlist4 t_il4 := t_rec2
template myrec3 tp_rec3(in myrec1 p1, in myrec2 p2) := {
  f1 := 1,
  f2 := p1,
  f3 := p2,
  f4 := 2
}
template myrec3 tp_rec4 := tp_rec3(valueof(t_rec2), valueof(t_rec1))
template myrec5 tp_rec5(in template myrec1 p1, in template myrec2 p2) := {
  f1 := p1,
  f2 := p2
}
template myrec5 tp_rec6 := tp_rec5(t_rec2, t_rec1)
template myrec5 tp_rec7 := tp_rec5(t_rec1, t_rec2)
template myrec5 tp_rec8 := tp_rec5(t_rec2, t_il4)
template myrec5 tp_rec9 := tp_rec5(t_il4, t_il4)

//** FATS **//
function f1(in myrec1 p1) return integer { return p1.f1 }
function t_f1(in template myrec1 p1) return integer { return valueof(p1.f1) }
function t_f2(in template myrec1 p1 := t_rec2) { log("From t_f2(): ", p1) }
function f2() return myrec1 { return { 2, 3 } }
function f3(in template myrec1 p1) return template myrec2 {
  return p1
}
function f4(in template intlist4 p1) { }
function f5(inout myrec1 p1, out intlist4 p2) {
  p1.f1 := p1.f1 + 1;
  p2 := { p1.f2, p1.f1 };
}
function f6(inout template intlist4 p1, out template myrec2 p2) {
  p1 := { p1[1], p1[0] };
  p2 := p1;
}

// A very nice use of type compatibility!  Return a different type on each
// branch.  Both f2() and ReturnMeAll() returns "{ 2, 3 }".
function ReturnMeAll() return myrec1 {
  var integer a := 1, b := 2
  var intlist4 ret_val1 := { 0, 1 }
  var myrec1 ret_val2 := { 1, 2 }
  var myrec2 ret_val3 := { 2, 3 }
  if (a > b) { return ret_val1 }
  else if (a == b) { return ret_val2 }
  else { return ret_val3 }
}
// Return expressions.
function ReturnMeAll2() return intlist1 {
  var integer a := 1, b := 2
  var intlist4 ret_val1 := { 11, 22 }
  var intlist1 ret_val2 := { 33, 44 }
  if (a > b) { return substr(ret_val2, 0, 1) }
  else if (a == b) { return substr(ret_val1 & ret_val1, 0, 2) & ret_val1 }
  else if (a == 111) { return valueof(t_rec1) }
  else if (a == 222) { return replace(ret_val1, 0, 1, ret_val1) }
  else if (a == 333) { return ret_val1 & ret_val1 }
  else if (a == 444) { return ret_val1 @> 2 }
  else if (a == 555) { return ret_val2 <@ 2 }
  else if (a == 666) { return (ret_val1 <@ 2) & (ret_val1 @> 2) }
  else if (a == 777) { return substr(t_il4, 0, 1) }
  else if (a == 888) { return substr(intlist4 : t_il4, 0, 1) }
  else if (a == 999) { return substr(intlist4 : t_rec1, 0, 1) }
  else { return substr(substr(ret_val2, 0, 1) & ret_val2, 0, 2) }
}
// Return templates.
function ReturnMeAll3(in integer a, in integer b) return template myrec1 {
  if (a > b) { return t_il4 }
  else if (a == b) { return t_rec1 }
  else { return t_rec2 }
}

// The default parameter can be a literal value or template.  Return values of
// a function cannot be used here only static values (e.g. "valueof(t_myrec1)"
// won't work here either).
function DefPar(in myrec1 p1, in myrec2 p2 := mp1) { p1 := p2 }
// Defined reference counts.
testcase tc_DefPar(in myrec1 p1, in myrec2 p2 := { 1, 2 }) runs on Empty {
  log("From tc_DefPar(): ", p1, " and ", p2)
  if (p1 == { 88, 99 } and { 66, 77 } == p2) { setverdict(pass) } else { setverdict(fail) }
  activate(myAltStep(p2))
}
altstep myAltStep(in myrec1 p1) runs on Empty {
  [p1.f1 > 0] T.timeout { }
}

testcase tc_MsgPortOps() runs on Empty system Empty {
  var myrec1 rec1 :=  { 1, 2 }
  var myrec2 rec2 :=  { 1, 2 }
  var intlist4 il4 := { 1, 2 }

  template intlist4 tl_il4 := { 1, 2 }
  template myrec2 tl_r2 :=    { 1, 2 }
  connect(self:P, self:Q)
  timer MyTimer := 0.5
  MyTimer.start
  P.send(il4)
  alt {
    [] Q.receive(il4) -> value rec1 { if (rec1 == { 1, 2 }) { setverdict(pass) } else { setverdict(fail) } }
    [] MyTimer.timeout { setverdict(inconc) }
  }
  MyTimer.start
  P.send(il4)
  alt {
    [] Q.receive(il4) -> value il4 { if (rec1 == il4 and not il4 != rec1) { setverdict(pass) } else { setverdict(fail) } }
    [] MyTimer.timeout { setverdict(inconc) }
  }
  MyTimer.start
  var intlist4 il4_1 := { [0] := 1, [1] := 1 }  // Only "{ 1 }" would cause run-time error.
  P.send(il4_1)
  alt {
    [] Q.receive(intlist4 : { 1, 1 }) -> value rec1 { if (rec1.f1 == 1) { setverdict(pass) } else { setverdict(fail) } }
    [] MyTimer.timeout { setverdict(inconc) }
  }
  MyTimer.start
  var intlist4 il4_2 := valueof(t_rec1)
  P.send(il4_2)
  alt {
    [] Q.receive(intlist4 : t_rec1) -> value rec1 { if (match(rec1, t_rec1)) { setverdict(pass) } else { setverdict(fail) } }
    [] MyTimer.timeout { setverdict(inconc) }
  }
  MyTimer.start
  var intlist4 il4_3 := { 1, 3 }
  var myrec1 rec1_tmp
  P.send(il4_3)
  alt {
    [] Q.receive(intlist4 : { 1, 3 }) -> value rec1_tmp { if (rec1_tmp == il4_3) { setverdict(pass) } else { setverdict(fail) } }
    [] MyTimer.timeout { setverdict(inconc) }
  }
  MyTimer.start
  P.send(il4)
  alt {
   [] Q.receive(tl_il4) -> value rec1 { if (match(rec1, tl_il4)) { setverdict(pass) } else { setverdict(fail) } }
   [] MyTimer.timeout { setverdict(inconc) }
  }
  MyTimer.start
  P.send(il4)
  alt {
    [] Q.receive(tl_il4) -> value rec2 { if ({ 1, 2 } == rec2) { setverdict(pass) } else { setverdict(fail) } }
    [] MyTimer.timeout { setverdict(inconc) }
  }
  MyTimer.start
  P.send(il4)
  alt {
    [] Q.receive(tl_il4) -> value il4 { if (il4 == { 1, 2 }) { setverdict(pass) } else { setverdict(fail) } }
    [] MyTimer.timeout { setverdict(inconc) }
  }
  if (rec1 == { 1, 2 } and valueof(tl_il4) == rec1) { setverdict(pass) } else { setverdict(fail) }
  if (match(rec2, tl_il4)) { setverdict(pass) } else { setverdict(fail) }
  disconnect(self:P, self:Q)
}

testcase tc_MsgPortOps_idx() runs on Empty system Empty {
  var myrec1 rec1 :=  { 1, 2 }
  var intlist4 il4 := { 1, 2 }

  connect(self:P, self:Q)
  timer MyTimer := 0.5
  MyTimer.start
  P.send(il4[0])
  alt {
    [] Q.receive(il4[0]) -> value rec1.f1 {
    	if (rec1.f1 == 1) { setverdict(pass) } else { setverdict(fail) }
    	MyTimer.stop;
    }
    [] MyTimer.timeout { setverdict(inconc) }
  }
  
  MyTimer.start
  P.send(il4[1])
  alt {
    [] Q.receive(il4[1]) -> value rec1.f2 {
    	if (rec1.f2 == 2) { setverdict(pass) } else { setverdict(fail) }
    	MyTimer.stop;
    }
    [] MyTimer.timeout { setverdict(inconc) }
  }
}

testcase tc_ProcPortOps() runs on Empty {
  connect(self:X, self:X);
  var intlist4 v_il4;
  var myrec2 v_rec2;
  var myrec6 v_rec6;
  var template myrec6 vt_rec6 := tp_rec5(t_rec1, t_rec2);
  timer tmr;
  X.reply(mysig: { } value valueof(t_il4));
  tmr.start(0.5);
  alt {
    [] X.getreply(mysig: { } value t_il4) -> value v_rec2 { if (match(v_rec2, t_rec2)) { setverdict(pass); } else { setverdict(fail, 1); } }
    [] tmr.timeout { setverdict(fail, -1); }
  }
  X.reply(mysig: { } value valueof(t_il4));
  tmr.start(0.5);
  alt {
    [] X.getreply(mysig: { } value intlist4: t_rec2) -> value v_il4 { if (match(v_il4, t_il4)) { setverdict(pass); } else { setverdict(fail, 2); } }
    [] tmr.timeout { setverdict(fail, -2); }
  }
  X.reply(mysig: { } value valueof(t_il4));
  tmr.start(0.5);
  alt {
    [] X.getreply(mysig: { } value intlist4: t_rec2) -> value v_rec2 { if (match(v_rec2, t_rec2)) { setverdict(pass); } else { setverdict(fail, 3); } }
    [] tmr.timeout { setverdict(fail, -3); }
  }
  X.raise(mysig, valueof(t_il4));
  tmr.start(0.5);
  alt {
    [] X.catch(mysig, t_il4) -> value v_rec2 { if (match(v_rec2, t_rec2)) { setverdict(pass); } else { setverdict(fail, 4); } }
    [] tmr.timeout { setverdict(fail, -4); }
  }
  X.raise(mysig, valueof(t_il4));
  tmr.start(0.5);
  alt {
    [] X.catch(mysig, intlist4: t_rec2) -> value v_il4 { if (match(v_il4, t_il4)) { setverdict(pass); } else { setverdict(fail, 5); } }
    [] tmr.timeout { setverdict(fail, -5); }
  }
  X.raise(mysig, valueof(tp_rec5(t_rec1, t_rec2)));
  tmr.start(0.5);
  alt {
    [] X.catch(mysig, myrec5: vt_rec6) -> value (v_rec6, v_il4 := f1, v_rec2 := f2) {
      if (match(v_rec6, vt_rec6)) { setverdict(pass); } else { setverdict(fail, 6); }
      if (match(v_il4, t_rec1)) { setverdict(pass); } else { setverdict(fail, 7); }
      if (match(v_rec2, t_rec2)) { setverdict(pass); } else { setverdict(fail, 8); }
    }
    [] tmr.timeout { setverdict(fail, -678); }
  }
  disconnect(self:X, self:X);
}

testcase tc_Params() runs on Empty {
  var myrec1 rec1 := { 1, 2 }
  var myrec2 rec2 := { 1, 2 }
  var intlist4 il4 := { 1, 2 }
  var mr1list mr1l := { { 1, 2 } }
  var mr2list mr2l := { { 1, 2 } }
  var myuni1 uni1 := { f2 := { 1, 2 } }

  // Using a function call expression in compatible operations.
  rec1 := f2()
  rec2 := f2()
  rec1 := ReturnMeAll()
  rec2 := ReturnMeAll()
  il4 := ReturnMeAll()
  if (sizeof(rec1) != sizeof(ReturnMeAll())) { setverdict(fail) } else { setverdict(pass) }
  if (rec1 == rec2 and rec2 == il4 and not (il4 != rec1 and rec1 != rec2)) { setverdict(pass) } else { setverdict(fail) }
  if (ReturnMeAll() == rec1 and il4 == ReturnMeAll() and not ReturnMeAll() != ReturnMeAll()) { setverdict(pass) } else { setverdict(fail) }
  if (rec1 == f2() and f2() == rec1) { setverdict(pass) } else { setverdict(fail) }
  if (rec1 != f2() and f2() != rec1) { setverdict(fail) } else { setverdict(pass) }
  if (rec2 == f2() and f2() == rec2) { setverdict(pass) } else { setverdict(fail) }
  if (rec2 != f2() and f2() != rec2) { setverdict(fail) } else { setverdict(pass) }
  if (mr1l == mr2l and mr2l == mr1l) { setverdict(pass) } else { setverdict(fail) }
  if (mr1l != mr2l and mr2l != mr1l) { setverdict(fail) } else { setverdict(pass) }
  mr1l[0] := f2()
  mr1l[1] := f2()
  mr2l[0] := f2()
  mr2l[1] := f2()
  if (mr1l == mr2l) { setverdict(pass) } else { setverdict(fail) }
  if (mr1l != mr2l) { setverdict(fail) } else { setverdict(pass) }
  mr1l[0] := mr2l[0]
  mr1l[1] := mr2l[1]
  if (f1(il4) != 2)  { setverdict(fail) } else { setverdict(pass) }  // Run-time error if il4 is too big.
  if (2 == f1(il4))  { setverdict(pass) } else { setverdict(fail) }
  if (f1(rec1) != 2) { setverdict(fail) } else { setverdict(pass) }
  if (2 == f1(rec1)) { setverdict(pass) } else { setverdict(fail) }
  DefPar({1, 1})
  DefPar(rec1)
  var integer ri := t_f1(t_rec5)
  t_f2(rec1)
  t_f2(rec2)
  t_f2(t_il4)
  t_f2(myrec2 : { 0, 0 })
  t_f2(intlist4 : { 11, 22 })
  var template myrec2 v2
  v2 := f3(t_il4)
  v2 := f3(intlist4 : t_il4)
  v2 := f3(il4)
  v2 := f3(intlist4 : il4)
  v2 := f3(intlist4 : t_rec1)
  v2 := f3(myrec1 : il4)
  v2 := f3(intlist4 : { 1, 2 })
  f4(myrec1 : { 1, 2 })
}

// testing type compatibility for 'out' and 'inout' parameters
testcase tc_Params2() runs on Empty {
  // testing value parameters
  var myrec1 v_rec1 := { 1, 4 };
  var myrec2 v_rec2;
  var intlist1 v_il1 := { 1, 4 };
  var intlist4 v_il4;
  
  // only the 2nd parameter needs conversion
  f5(v_rec1, v_rec2);
  if (v_rec1 == { 2, 4 }) { setverdict(pass); } else { setverdict(fail, 1); }
  if (v_rec2 == { 4, 2 }) { setverdict(pass); } else { setverdict(fail, v_rec2); }
  
  // only the 1st parameter needs conversion
  f5(v_il1, v_il4);
  if (v_il1 == { 2, 4 }) { setverdict(pass); } else { setverdict(fail, 3); }
  if (v_il4 == { 4, 2 }) { setverdict(pass); } else { setverdict(fail, 4); }
  
  // both parameters need conversion
  v_rec2 := { 1, 4 };
  f5(v_rec2, v_il1);
  if (v_rec2 == { 2, 4 }) { setverdict(pass); } else { setverdict(fail, 5); }
  if (v_il1 == { 4, 2 }) { setverdict(pass); } else { setverdict(fail, 6); }
  
  // testing template parameters
  var template myrec1 vt_rec1;
  var template (value) myrec2 vt_rec2;
  var template intlist1 vt_il1 := { 1, 3 };
  var template intlist4 vt_il4 := { 1, 3 };
  
  // only the 2nd parameter needs conversion
  f6(vt_il4, vt_rec1);
  if (isvalue(vt_il4) and valueof(vt_il4) == { 3, 1 }) { setverdict(pass); } else { setverdict(fail, 7); }
  if (isvalue(vt_rec1) and valueof(vt_rec1) == { 3, 1 }) { setverdict(pass); } else { setverdict(fail, 8); }
  
  // only the 1st parameter needs conversion
  f6(vt_il1, vt_rec2);
  if (isvalue(vt_il1) and valueof(vt_il1) == { 3, 1 }) { setverdict(pass); } else { setverdict(fail, 9); }
  if (isvalue(vt_il4) and valueof(vt_il4) == { 3, 1 }) { setverdict(pass); } else { setverdict(fail, 10); }
  
  // both parameters need conversion
  vt_rec2 := { 1, 3 };
  f6(vt_rec2, vt_il4);
  if (isvalue(vt_rec2) and valueof(vt_rec2) == { 3, 1 }) { setverdict(pass); } else { setverdict(fail, 11); }
  if (isvalue(vt_il4) and valueof(vt_il4) == { 3, 1 }) { setverdict(pass); } else { setverdict(fail, 12); }
}

testcase tc_EqNeAss() runs on Empty {
  var myrec1 rec1 := { 1, 2 }
  var myrec1Ext rece1 := { 1, 2 }
  var Myrec1ASN reca1 := { 1, 2 }
  var myrec2 rec2 := { 1, 2 }
  var myrec3 rec3 := { 1, { 1, 2 }, { 1, 2 }, 2 }
  var myrec4 rec4 := { 1, { 1, 2 }, { 1, 2 }, 2 }
  var intlist1 il1 := { 1, 2 }
  var intlist3 il3 := { { { 1, 2 }, { 1, 2 } }, { { 1, 2 }, { 1, 2 } } }
  var myuni1 uni1 := { f2 := { 1, 2 } }
  var anytype any1 := { myrec1 := { 1, 2 } }
  var myarr2 arr2 := { 1, 2 }
  var myrec7 rec7
  var intlist4 il4 := { 1, 2 }
  var myarr1 arr1 := { 1, 2 }
  var myarr3 arr3 := { { 1 } }
  var myarr4 arr4 := { { 1, 2 } }
  const myrec1 c_rec1 := { 5, 5 }
  const myrec2 c_rec2 := { 5, 5 }

  if (c_rec1 == c_rec2 and c_rec2 == c_rec1) { setverdict(pass) } else { setverdict(fail) }
  if (c_rec1 != c_rec2 and c_rec2 != c_rec1) { setverdict(fail) } else { setverdict(pass) }
  // We shouldn't generate conversion function for these types.  They're just
  // typedefs in the generated code.  "get_type_refd_last()" will return the
  // same Type for both.  The alias type won't be used at all.
  var iammyrec1 mrec1 := { 1, 2 }
  mrec1 := rec1  // No additional code.
  rec1 := mrec1  // No...
  if (rec1 == mrec1 and not mrec1 != rec1) { setverdict(pass) } else { setverdict(fail) }
  // Assignments and ==, != operations on different structured types from
  // several other modules.
  rec1 := reca1
  rec1 := rece1
  rec1 := arr1
  il1 := rec1
  rec7 := il3
  il3 := rec7
  il4 := arr2
  if (il4 == il1 and il1 == il4) { setverdict(pass) } else { setverdict(fail) }
  if (rec7 == il3) { setverdict(pass) } else { setverdict(fail) }
  if (rec1 == il1) { setverdict(pass) } else { setverdict(fail) }
  if (il1 == rec1) { setverdict(pass) } else { setverdict(fail) }
  // Simple record-record assignments and ==, != operations with some field
  // references.
  rec1 := rec2
  rec3 := rec4
  rec3.f1 := rec4.f1
  rec3.f1 := 1
  rec3.f2 := rec4.f2
  rec3.f2 := { 1, 2 }
  rec3.f3 := rec4.f3
  rec3.f3 := { 1, 2 }
  rec3.f4 := rec4.f4
  rec3.f4 := 2
  rec4.f1 := rec3.f1
  rec4.f1 := 1
  rec4.f2 := rec3.f2
  rec4.f2 := { 1, 2 }
  rec4.f3 := rec3.f3
  rec4.f3 := { 1, 2 }
  rec4.f4 := rec3.f4
  rec4.f4 := 2
  if (rec1 == rec2) { setverdict(pass) } else { setverdict(fail) }
  if (rec1 != rec2) { setverdict(fail) } else { setverdict(pass) }
  if (rec2 == rec1) { setverdict(pass) } else { setverdict(fail) }
  if (rec2 != rec1) { setverdict(fail) } else { setverdict(pass) }
  if (rec1 == rec2 and rec2 == rec1) { setverdict(pass) } else { setverdict(fail) }
  if (rec1 != rec2 and rec2 != rec1) { setverdict(fail) } else { setverdict(pass) }
  if (not rec1 != rec2 and not rec2 != rec1) { setverdict(pass) } else { setverdict(fail) }
  if (not rec1 == rec2 and not rec2 == rec1) { setverdict(fail) } else { setverdict(pass) }
  if (rec3 == rec4) { setverdict(pass) } else { setverdict(fail) }
  if (rec3 != rec4) { setverdict(fail) } else { setverdict(pass) }
  if (rec4 == rec3) { setverdict(pass) } else { setverdict(fail) }
  if (rec4 != rec3) { setverdict(fail) } else { setverdict(pass) }
  if (rec3 == rec4 and rec4 == rec3) { setverdict(pass) } else { setverdict(fail) }
  if (rec3 != rec4 and rec4 != rec3) { setverdict(fail) } else { setverdict(pass) }
  if (not rec3 != rec4 and not rec4 != rec3) { setverdict(pass) } else { setverdict(fail) }
  if (not rec3 == rec4 and not rec4 == rec3) { setverdict(fail) } else { setverdict(pass) }
}

testcase tc_Exprs() runs on Empty {
  var intlist1 il1 := { 1, 2 }
  var intlist4 il4 := { 0 }

  il1 := il4
  il4 := il1
  if (il1 == { 0 } and il4 == il1) { setverdict(pass) } else { setverdict(fail) }
  il4 := substr(il1, 0, 1)
  il4 := replace(il1, 0, 1, il1)
  il4 := { 1, 2, 3, 4 }
  il1 := il4 @> 2
  if (il1 == { 3, 4, 1, 2 } and { 1, 2, 3, 4 } == il4) { setverdict(pass) } else { setverdict(fail) }
  il4 := il1 <@ 4
  if (il4 == { 3, 4, 1, 2 } and il4 == il1) { setverdict(pass) } else { setverdict(fail) }
  il1 := (il1 @> 2) <@ 2
  if (il1 == il4) { setverdict(pass) } else { setverdict(fail) }
  il1 := (il4 @> 2 ) <@ 1
  if (il1 == { 2, 3, 4, 1 } and il4 == il1 <@ 1) { setverdict(pass) } else { setverdict(fail) }
  il1 := { 99, 101 }
  il4 := il1 & il1
  if (il4 == { 99, 101, 99, 101 } and il4 == il1 & il1 and il1 & il1 == il4) { setverdict(pass) } else { setverdict(fail) }
  il4 := { 99, 101, 101, 99, 99, 101 }
  if ((il1 & (il1 @> 1)) & il1 == il4) { setverdict(pass) } else { setverdict(fail) }
  // This should be a simple error, but TITAN gives FATAL_ERROR!
  // "if (({ 1, 2, 3 } & (il1 @> 1)) & il1 == il4) { setverdict(pass) } else { setverdict(fail) }"
  il1 := substr(il4, 1, 2)
  il1 := ReturnMeAll2()
  if (il1 == { 33, 33 } and ReturnMeAll2() == il1) { setverdict(pass) } else { setverdict(fail) }
  var myrec1 v_rec1 := valueof(t_rec2)
  v_rec1 := valueof(t_rec2)
  if (match(v_rec1, t_rec2)) { setverdict(pass) } else { setverdict(fail) }
}

testcase tc_Templates() runs on Empty {
  var template myrec1 tv_rec1 := ReturnMeAll3(1, 2)
  var template myrec2 tv_rec2 := ReturnMeAll3(1, 2)
  template myrec1 tl_rec1 := ReturnMeAll3(1, 2)
  template myrec1 tl_rec2 := tl_rec1

  tv_rec1 := tv_rec2
  tv_rec1 := ReturnMeAll3(1, 2)
  var myrec1 v_rec1 := valueof(tv_rec1)
  var myrec2 v_rec2 := v_rec1
  var myrec3 v_rec3 := valueof(tp_rec3(v_rec1, v_rec2))
  var myrec3 v_rec4 := valueof(tp_rec3(v_rec2, v_rec1))
  template myrec3 tl_rec3 := tp_rec3(v_rec2, v_rec1)
  var template myrec3 tv_rec3 := tp_rec3(v_rec2, v_rec1)
  if (v_rec3 == v_rec4 and v_rec3 == { 1, { 2, 3 }, { 2, 3 }, 2 }) { setverdict(pass) } else { setverdict(fail) }
  if (match({ 1, { 2, 3 }, { 2, 3 }, 2 }, tv_rec3)) { setverdict(pass) } else { setverdict(fail) }
  if (match({ 1, { 2, 3 }, { 2, 3 }, 2 }, tl_rec3)) { setverdict(pass) } else { setverdict(fail) }
  if (match({ 2, 3 }, tv_rec1)) { setverdict(pass) } else { setverdict(fail) }
  if (match(v_rec2, tv_rec1))   { setverdict(pass) } else { setverdict(fail) }
  if (valueof(tv_rec2) == v_rec1 and v_rec1 == valueof(tv_rec2)) { setverdict(pass) } else { setverdict(fail) }
  if (valueof(tl_rec2) == v_rec1 and v_rec1 == valueof(tl_rec2)) { setverdict(pass) } else { setverdict(fail) }
  if (match({ f1 := 1, f2 := { 2, 3 }, f3 := { 1, 2 }, f4 := 2 }, tp_rec4)) { setverdict(pass) } else { setverdict(fail) }
  var myrec1 vm1 := { 2, 3 }  // t_rec2
  var myrec2 vm2 := { 1, 2 }  // t_rec1
  if (match({ vm1, vm2 }, tp_rec6)) { setverdict(pass) } else { setverdict(fail) }
  if (match({ vm2, vm1 }, tp_rec7)) { setverdict(pass) } else { setverdict(fail) }
  if (match({ vm1, vm1 }, tp_rec8)) { setverdict(pass) } else { setverdict(fail) }
  if (match({ vm1, vm1 }, tp_rec9)) { setverdict(pass) } else { setverdict(fail) }
}


// Bug 541100 - Compiler crash during code generation for type conversion
type record Rec1 {
  integer x
}

type record Rec2 {
  integer y
}

type record Main541100 {
  Rec2 field
}

template (value) Main541100 t_541100(template(value) Rec2 p) := { p };

const Rec1 c_541100 := { 1 };

testcase tc_541100() runs on Empty {
  template (value) Main541100 t_res := t_541100(c_541100); // parameter needs type conversion
  if (not isvalue(t_res) or valueof(t_res) != { { 1 } }) {
    setverdict(fail, t_res);
  }
  else {
    setverdict(pass);
  }
}

// Enumerated type compatibility
type enumerated EWeekDays { 
  Mon, Tue, Wed, Thu, Fri, Sat, Sun 
}

type enumerated EWorkDays { 
  Mon, Tue, Wed, Thu, Fri 
}

type enumerated ERevWeekDays { 
  Sun, Sat, Fri, Thu, Wed, Tue, Mon
}

type enumerated EComplexValues { 
  e_num (1), 
  e_expr (2+2), 
  e_bin_conv (bit2int('0111'B)), 
  e_oct_conv (oct2int('34'O)), 
  e_hex_conv (hex2int('AC'H)) 
}

type enumerated ESimpleValues { 
  e_num (1), 
  e_expr (4), 
  e_bin_conv (7), 
  e_oct_conv (52), 
  e_hex_conv (172) 
}

type enumerated Parity1 {
  Odd(1, 3, 5, 7),
  Even(2, 4, 6, 8)
}

type enumerated Parity2 {
  Odd(1, 3, 5, 7),
  Even(2, 4, 6, 8, 10, 12)
}

testcase tc_enums() runs on Empty {
  var EWeekDays v_weekDay := Mon;
  var EWorkDays v_workDay := v_weekDay;
  if (v_workDay != Mon) {
    setverdict(fail, "#1: ", v_workDay);
  }
  
  v_workDay := Wed;
  v_weekDay := v_workDay;
  if (v_weekDay != Wed) {
    setverdict(fail, "#2: ", v_weekDay);
  }
  
  var template charstring vt_error := pattern "*are not compatible at run-time";
  @try {
    v_weekDay := Sat;
    v_workDay := v_weekDay;
    setverdict(fail, "#3: expected error");
  }
  @catch (msg) {
    if (not match(msg, vt_error)) {
      setverdict(fail, "#3: received incorrect message: ", msg);
    }
  }
  
  v_weekDay := Thu;
  var ERevWeekDays v_revWeekDay := v_weekDay;
  if (v_revWeekDay != Thu) {
    setverdict(fail, "#4: ", v_revWeekDay);
  }
  
  @try {
    v_weekDay := Tue;
    v_revWeekDay := v_weekDay;
    setverdict(fail, "#5: expected error");
  }
  @catch (msg) {
    if (not match(msg, vt_error)) {
      setverdict(fail, "#5: received incorrect message: ", msg);
    }
  }
  
  var ESimpleValues v_simpleVal := e_bin_conv;
  var EComplexValues v_complexVal := v_simpleVal;
  if (v_complexVal != e_bin_conv) {
    setverdict(fail, "#6: ", v_complexVal);
  }
  
  var template EComplexValues vt_complexVal := e_expr;
  var template ESimpleValues vt_simpleVal := vt_complexVal;
  if (not isvalue(vt_simpleVal) or valueof(vt_simpleVal) != e_expr) {
    setverdict(fail, "#7: ", vt_simpleVal);
  }
  
  var Parity1 v_parity1 := Even(8);
  var Parity2 v_parity2 := v_parity1;
  if (v_parity2 != Even(8)) {
    setverdict(fail, "#8: ", v_parity2);
  }
  
  v_parity2 := Odd(5);
  v_parity1 := v_parity2;
  if (v_parity1 != Odd(5)) {
    setverdict(fail, "#9: ", v_parity1);
  }
  
  @try {
    v_parity2 := Even(12);
    v_parity1 := v_parity2;
    setverdict(fail, "#10: expected error");
  }
  @catch (msg) {
    if (not match(msg, vt_error)) {
      setverdict(fail, "#10: received incorrect message: ", msg);
    }
  }
  setverdict(pass);
}


control {
  var myrec1 rec1 := { 66, 77 }
  var myrec2 rec2 := { 88, 99 }
  execute(tc_MsgPortOps())
  execute(tc_MsgPortOps_idx());
  execute(tc_ProcPortOps())
  execute(tc_EqNeAss())
  execute(tc_Params())
  execute(tc_Params2())
  execute(tc_DefPar(rec2, rec1))
  execute(tc_Exprs())
  execute(tc_Templates())
  execute(Ext.tc_ExtMatch())
  execute(Ext.tc_ExtSimple())
  execute(Ext.tc_Recursive())
  execute(Std.tc_Enums())
  execute(Std.tc_Records())
  execute(Std.tc_RecordOfsArrays())
  execute(Std.tc_SetsSetOfs())
  execute(Std.tc_Unions())
  execute(Std.tc_Anytypes())
  execute(Std.tc_Substruct())
  execute(tc_541100());
  execute(tc_enums());
}

} with { extension "anytype myrec1, myrec2" }
