text: import ctypes
  import os
  import random
  import re
  
  import jinja2
  
  from pygears.sim import sim_log
  from pygears.sim.extens.rand_base import RandBase
  from pygears.typing.visitor import TypingVisitorBase
  from pygears.util.fileio import save_file
  
  
  class SCVCompileError(Exception):
      pass
  
  
  def file_changed(fn, new_contents):
      with open(fn) as f:
          data = f.read()
  
      return data != new_contents
  
  
  def rebuild_needed(outdir, name, new_contents):
      lib_fn = os.path.join(outdir, name)
      cpp_fn = os.path.join(outdir, f'{name}.cpp')
      if not os.path.exists(lib_fn):
          return True
  
      return file_changed(cpp_fn, new_contents)
  
  
  class SCVTypeSeqVisitor(TypingVisitorBase):
      def __init__(self, scvlib=None):
          self.scvlib = scvlib
          self.context = ''
  
          if self.scvlib:
              self.scvlib.init()
              self.scvlib.next()
  
      def visit(self, dtype, field=''):
          if field:
              '_'.join([self.context, field])
          val = super().visit(dtype, field)
  
          self.context.rsplit('_', 1)[0]
          return val
  
      def visit_queue(self, dtype, field):
          qlen = random.randrange(1, 3)
          return [self.visit(dtype[:-1]) for i in range(qlen)]
  
      def visit_tuple(self, dtype, field):
          return tuple(self.visit(d, f) for d, f in zip(dtype, dtype.fields))
  
      def visit_uint(self, dtype, field):
          scv_var_func = getattr(self.scvlib, f'get_{field}', None)
          if scv_var_func:
              return scv_var_func()
          else:
              return random.randrange(0, 2**(int(dtype)) - 1)
  
      def visit_int(self, dtype, field):
          scv_var_func = getattr(self.scvlib, f'get_{field}', None)
          if scv_var_func:
              return scv_var_func()
          else:
              return random.randrange(-2**(int(dtype) - 1),
                                      2**(int(dtype) - 1) - 1)
  
  
  class SCVTypeVisitor(TypingVisitorBase):
      def __init__(self):
          self.cvars = {}
          self.depth = -1
  
      def visit(self, type_, field):
          self.depth += 1
          type_declaration = super().visit(type_, field)
          self.depth -= 1
          return type_declaration
  
      def visit_int(self, type_, field):
          self.cvars[field] = (f'sc_int<{int(type_)}>', int(type_))
  
      def visit_uint(self, type_, field):
          if (int(type_) != 0):
              self.cvars[field] = (f'sc_uint<{int(type_)}>', int(type_))
  
      def visit_unit(self, type_, field):
          return None
  
      def visit_union(self, type_, field):
          pass
  
      def visit_queue(self, type_, field):
          pass
  
      def visit_tuple(self, type_, field):
          for t, f in zip(type_.args, type_.fields):
  
              if field:
                  self.visit(t, f'field_{f}')
              else:
                  self.visit(t, f)
  
      def visit_array(self, type_, field):
          pass
  
  
  class SCVConstraints:
      def __init__(self,
                   name='dlft',
                   dtype=None,
                   cvars={},
                   cons=[],
                   cls='scv_constraint_base'):
          self.name = name
          self.dtype = dtype
          self.cvars = cvars.copy()
          self.cons = cons.copy()
          self.cls = cls
  
      def add_var(self, name, dtype):
          v = SCVTypeVisitor()
          v.visit(dtype, name)
          self.cvars.update(v.cvars)
  
  
  class SCVRand(RandBase):
      def create_type_cons(self, desc={}):
          v = SCVTypeVisitor()
          v.visit(desc['dtype'], desc['name'])
          tcons = SCVConstraints(
              name=desc['name'],
              dtype=desc['dtype'],
              cvars=v.cvars,
              cons=desc['cons'],
              cls=desc['cls'])
          for name, dtype in desc['params'].items():
              tcons.add_var(name, dtype)
  
          return tcons
  
      def before_setup(self, sim):
          self.create_scv()
  
      def create_scv(self):
          for constraint in self.constraints:
              jenv = jinja2.Environment(trim_blocks=True, lstrip_blocks=True)
              jenv.globals.update(int=int)
              jenv.loader = jinja2.FileSystemLoader([os.path.dirname(__file__)])
  
              def add_parens_for_vars(cons, cvars):
                  def add_parens(matchobj):
                      if matchobj.group(0) in cvars:
                          return f'{matchobj.group(0)}()'
                      else:
                          return matchobj.group(0)
  
                  return re.sub('([^\d\W]\w*)', add_parens, cons)
  
              scv_cons = [
                  add_parens_for_vars(c, constraint.cvars)
                  for c in constraint.cons
              ]
              context = {
                  'vars': constraint.cvars,
                  'constraints': scv_cons,
                  'base_cls': constraint.cls
              }
              c = jenv.get_template('scv_wrap.j2').render(context)
              save_file(f'{constraint.name}.cpp', self.outdir, c)
  
      def get_rand(self, name):
          ret = os.system(
              f"cd {self.outdir}; g++ -fpic -shared -I. -I$SYSTEMC_INCLUDE -I$SCV_INCLUDE -Wall -Wformat -O2 $SCV_LIBDIR/libscv.so -L$SYSTEMC_LIBRID $SYSTEMC_LIBDIR/libsystemc.so -lpthread -pthread -Wl,-rpath -Wl,$SCV_LIBDIR -Wl,-rpath -Wl,$SYSTEMC_LIBDIR -o {name} {name}.cpp"
          )
  
          if ret != 0:
              sim_log().warning(
                  'Possible reason for exception: Queues not supported in SCV')
              raise SCVCompileError(
                  f'Constrained random library compilation failed for module {name}.'
              )
  
          scvlib = ctypes.CDLL(os.path.join(self.outdir, name))
          t = self.get_dtype_by_name(name)
          return SCVTypeSeqVisitor(scvlib).visit(t, name)
  