#!/usr/bin/python

import subprocess
import os
import sys
import shutil

from gen_hdd import *
from gen_hard_config import *
from gen_arch_info import *

#TODO (?): recopier les fichiers d'entrees dans le script en fonction de l'appli selectionnee
# Par exemple, tk14.O pour LU, img.raw pour ep_filter, etc.


# User parameters
bscpu = 0
#nb_procs = [ 4 ]
nb_procs = [ 1, 4, 8, 16, 32, 64, 128, 256 ]
rerun_stats = True
use_omp = True
protocol = 'rwt'
cpu_per_cluster = 4
# mode must be one of 'test' and 'simu'
mode = 'simu'

#apps = [ 'cholesky', 'fft', 'fft_ga', 'filter', 'filt_ga', 'histogram', 'kmeans', 'lu', 'mandel', 'mat_mult', 'pca', 'radix_ga' ]
#apps = [ 'histogram', 'mandel', 'filter', 'radix_ga', 'fft_ga', 'kmeans' ]
apps = [ 'blackscholes', 'linear_regression', 'string_match', 'swaptions', 'fluidanimate' ]


# Variables which could be changed but ought not to because they are reflected in the create_graphs.py script
if mode == 'test':
    data_dir = 'data_test'
else:
    data_dir = 'data'
log_init_name = protocol + '_stdo_'
log_term_name = protocol + '_term_'

# Global Variables

all_apps = [ 'blackscholes', 'boot_only', 'cholesky', 'fft', 'fft_ga', 'filter', 'filt_ga', 'fluidanimate', 'histogram', 'histo-opt', 'kmeans', 'kmeans-opt', 'linear_regression', 'lu', 'mandel', 'mat_mult', 'mat_mult-opt', 'pca', 'pca-opt', 'radix', 'radix_ga', 'showimg', 'string_match', 'swaptions', ]
# to come: 'barnes', 'fmm', 'ocean', 'raytrace', 'radiosity', 'waters', 'watern'

all_protocols = [ 'dhccp', 'rwt', 'hmesi', 'wtidl', 'snoop' ]

top_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), "..")
config_name = os.path.join(os.path.dirname(os.path.realpath(__file__)), "config.py")

scripts_path         = os.path.join(top_path, 'scripts')
almos_path           = os.path.join(top_path, 'almos')
soclib_conf_name     = os.path.join(top_path, "soclib.conf")
topcell_name         = os.path.join(top_path, "top.cpp")
partition_root_path  = os.path.join(top_path, "hdd_root")
arch_info_name       = os.path.join(almos_path, "arch-info-gen.info")
arch_info_bib_name   = os.path.join(almos_path, 'arch-info.bib')
hdd_img_file_name    = os.path.join(almos_path, "hdd-img.bin")
shrc_file_name       = os.path.join(almos_path, "shrc")
hard_config_name     = os.path.join(almos_path, "hard_config.h")
bootloader_link_name = os.path.join(almos_path, "bootloader-tsar-mipsel.bin")


# Checks
if mode != 'test' and mode != 'simu':
    help_str = '''
*** Error: variable mode must be one either 'test' (testing purpose, small data sets) or 'simu' (performance evaluation, large data sets)
'''
    print help_str
    sys.exit()

if protocol not in all_protocols:
    help_str = '''
*** Error: variable protocol has an unsupported value
'''
    print help_str
    sys.exit()

for the_app in apps:
    if the_app not in all_apps:
        print "*** Error: application %s is not defined" % (the_app)
        sys.exit()

if not os.path.isfile(config_name):
    help_str = '''
You should create a file named config.py in this directory with the following definitions:
 - apps_dir:          path to almos-tsar-mipsel/apps directory
 - almos_src_dir:     path to almos source directory (for kernel and bootloader binaries)
 - preloader_src_dir: path to the preloader main directory (where to run make)
 - hdd_img_name:      path to the hdd image to use (will be copied but not modified)
 - tsar_dir:          path to tsar repository
Optional definitions (necessary if you want to use alternative protocols):
 - rwt_dir:           path to the RWT repository
 - hmesi_dir:         path to HMESI directory
 - wtidl_dir:         path to the ideal write-through protocol directory
*** Stopping execution
'''
    print help_str
    sys.exit()

# Loading config
exec(file(config_name))

# Check that variables and paths exist
for var in [ 'apps_dir', 'almos_src_dir', 'hdd_img_name', 'tsar_dir' ]:
    if eval(var) == "":
        print "*** Error: variable %s not defined in config file" % (var)
        sys.exit()
    if not os.path.exists(eval(var)):
        print "*** Error: variable %s does not define a valid path" % (var)
        sys.exit()

if protocol == "rwt":
    if rwt_dir == "":
        print "*** Error: variable rwt_dir not defined in config file"
        sys.exit()
    if not os.path.exists(rwt_dir):
        print "*** Error: variable rwt_dir does not define a valid path"
        sys.exit()

if protocol == "hmesi":
    if hmesi_dir == "":
        print "*** Error: variable hmesi_dir not defined in config file"
        sys.exit()
    if not os.path.exists(hmesi_dir):
        print "*** Error: variable hmesi_dir does not define a valid path"
        sys.exit()

if protocol == "wtidl":
    if wtidl_dir == "":
        print "*** Error: variable wtidl_dir not defined in config file"
        sys.exit()
    if not os.path.exists(wtidl_dir):
        print "*** Error: variable wtidl_dir does not define a valid path"
        sys.exit()




#splash_app_dir = {}
#splash_app_dir['barnes'] = 'apps/barnes'
#splash_app_dir['fmm'] = 'apps/fmm'
#splash_app_dir['ocean'] = 'apps/ocean/contiguous_partitions'
#splash_app_dir['raytrace'] = 'apps/raytrace'
#splash_app_dir['radiosity'] = 'apps/radiosity'
#splash_app_dir['volrend'] = 'apps/volrend'
#splash_app_dir['watern'] = 'apps/water-nsquared'
#splash_app_dir['waters'] = 'apps/water-spatial'


def print_and_call(cmd):
    print subprocess.list2cmdline(cmd)
    retval = subprocess.call(cmd)
    return retval

def print_and_popen(cmd, outfile):
    print subprocess.list2cmdline(cmd),
    print " >", outfile
    output = subprocess.Popen(cmd, stdout = subprocess.PIPE).communicate()[0]
    return output


def get_x_y(nb_procs, cpu_per_cluster):
    x = 1
    y = 1
    to_x = True
    while (x * y * cpu_per_cluster < nb_procs):
        if to_x:
            x = x * 2
        else:
            y = y * 2
        to_x = not to_x
    return x, y


def get_nb_bits(size):
    i = 0
    while 2**i < size:
        i += 1
    return i



def gen_soclib_conf():
    if os.path.isfile(soclib_conf_name):
        print "Updating file %s" % (soclib_conf_name)
        # First, remove lines containing "addDescPath"
        f = open(soclib_conf_name, "r")
        lines = f.readlines()
        f.close()

        f = open(soclib_conf_name, "w")

        for line in lines:
            if not ("addDescPath" in line):
                f.write(line)
        f.close()
    else:
        print "Creating file %s" % (soclib_conf_name)
        f = open(soclib_conf_name, "w")
        f.close()

    # Defining common and specific modules 
    common_modules = [
            'lib/generic_llsc_global_table',
            'modules/dspin_router_tsar', 
            'modules/sdmmc',
            'modules/vci_block_device_tsar',
            'modules/vci_ethernet_tsar',
            'modules/vci_io_bridge',
            'modules/vci_iox_network',
            'modules/vci_spi',
            'platforms/tsar_generic_xbar/tsar_xbar_cluster'
    ]

    specific_modules = [
            'communication',
            'lib/generic_cache_tsar',
            'modules/vci_cc_vcache_wrapper',
            'modules/vci_mem_cache',
    ]

    f = open(soclib_conf_name, "a")
    # Adding common modules
    for common_module in common_modules:
        f.write("config.addDescPath(\"%s/%s\")\n" % (tsar_dir, common_module))
    #f.write("\n")

    if protocol == "dhccp":
        arch_dir = tsar_dir
    elif protocol == "rwt":
        arch_dir = rwt_dir
    elif protocol == "hmesi":
        arch_dir = hmesi_dir
    elif protocol == "wtidl":
        arch_dir = wtidl_dir
    else:
        assert(False)

    for specific_module in specific_modules:
        f.write("config.addDescPath(\"%s/%s\")\n" % (arch_dir, specific_module))

    #f.write("\n")
    f.close()



def gen_arch_info_bib(x, y, arch_info, arch_info_bib):
    old_path = os.getcwd()

    print "cd", scripts_path
    os.chdir(scripts_path)
    gen_arch_info(x, y, x_width, y_width, bscpu, arch_info)
    os.chdir(almos_path)
   
    cmd = ['./info2bib', '-i', arch_info, '-o', arch_info_bib]
    print_and_call(cmd)

    print "cd", old_path
    os.chdir(old_path)
   

def gen_sym_links():
    target = os.path.join(almos_src_dir, 'tools/soclib-bootloader/bootloader-tsar-mipsel.bin')
    if not os.path.isfile(bootloader_link_name):
        print "ln -s", target, bootloader_link_name
        os.symlink(target, bootloader_link_name)

    #target = os.path.join(almos_src_dir, 'kernel/obj.tsar/almix-tsar-mipsel.bin')
    #link_name = 'kernel-soclib.bin'
    #if not os.path.isfile(link_name):
    #    print "ln -s", target, link_name
    #    os.symlink(target, link_name)


def compile_almos():
    old_path = os.getcwd()

    print "cd", almos_src_dir
    os.chdir(almos_src_dir)
    cmd = ['make']
    print_and_call(cmd)
    bootloader_dir = os.path.join(almos_src_dir, 'tools/soclib-bootloader')
    print "cd", bootloader_dir
    os.chdir(bootloader_dir)
    print_and_call(cmd)

    print "cd", old_path
    os.chdir(old_path)
 

def compile_preloader():
    old_path = os.getcwd()
    hard_conf_path_set = "HARD_CONFIG_PATH=" + almos_path
    bscpu_set = "BS_PROC=%d" % bscpu

    print "cd", preloader_src_dir
    os.chdir(preloader_src_dir)
    cmd = ['make', hard_conf_path_set, bscpu_set, 'USE_DT=0']
    print_and_call(cmd)

    print "cd", old_path
    os.chdir(old_path)




def compile_app(app_name):

    #if app_name in splash2:
    #   app_dir_name = os.path.join(splash2_dir, splash_app_dir[app_name])
    #elif app_name in splash2_ga:
    #   app_dir_name = os.path.join(splash2_ga_dir, splash_ga_app_dir[app_name])
    #else:
    #   app_dir_name = os.path.join(apps_dir, app_name)

     
    app_dir_name = os.path.join(apps_dir, app_name)

    old_path = os.getcwd()
    print "cd", app_dir_name
    os.chdir(app_dir_name)

    # Compilation process is different in splash and other apps
    #if app_name in splash2:
    #   print "make clean"
    #   subprocess.call([ 'make', 'clean' ])

    #   print "rm Makefile"
    #   subprocess.call([ 'rm', 'Makefile' ])

    #   print "ln -s Makefile.soclib Makefile"
    #   subprocess.call([ 'ln', '-s', 'Makefile.soclib', 'Makefile' ])

    #   print "make"
    #   subprocess.call([ 'make' ])

    #else:
    #   print "make clean"
    #   subprocess.call([ 'make', 'clean' ])
 
    #   print "make TARGET=tsar"
    #   subprocess.call([ 'make', 'TARGET=tsar' ])

    cmd = ['make', 'clean']
    print_and_call(cmd)
 
    cmd = ['make', 'TARGET=tsar']
    retval = print_and_call(cmd)
    if retval != 0:
        sys.exit()

    print "cd", old_path
    os.chdir(old_path)
# end of compile_app




def gen_shrc(app_name, nprocs):
    # Creation/Modification du shrc de almos
    if mode == 'test':
        if (app_name == "blackscholes"):
            shrc = "exec -p 0 /bin/blacksch -n%(nproc)d /etc/black_16.txt\n" % dict(nproc = nprocs)
        elif (app_name == "boot_only"):
            shrc = "exec -p 0 /bin/boot_onl\n"
        elif (app_name == "filter"):
            shrc = "exec -p 0 /bin/filter -l 128 -c 128 -n %(nproc)d -i /etc/img128.raw\n" % dict(nproc = nprocs)
        elif (app_name == "filt_ga"):
            shrc = "exec -p 0 /bin/filt_ga -l 128 -c 128 -n %(nproc)d -i /etc/img128.raw\n" % dict(nproc = nprocs)
        elif (app_name == "fft"):
            shrc = "exec -p 0 /bin/fft -n%(nproc)d -m4\n" % dict(nproc = nprocs)
        elif (app_name == "fft_ga"):
            shrc = "exec -p 0 /bin/fft_ga -n%(nproc)d -m4\n" % dict(nproc = nprocs) # m = vector size
        elif (app_name == "fluidanimate"):
            shrc = "exec -p 0 /bin/fluidani -n%(nproc)d -i /etc/flui_15K.flu\n" % dict(nproc = nprocs)
        elif (app_name == "histogram"):
            shrc = "exec -p 0 /bin/histogra -n%(nproc)d /etc/histo_s.bmp\n" % dict(nproc = nprocs)
        elif (app_name == "histo-opt"):
            shrc = "exec -p 0 /bin/histo-op -n%(nproc)d /etc/histo_s.bmp\n" % dict(nproc = nprocs)
        elif (app_name == "kmeans"):
            shrc = "exec -p 0 /bin/kmeans -n %(nproc)d -p %(npoints)d\n" % dict(nproc = nprocs, npoints = 1000)
        elif (app_name == "kmeans-opt"):
            shrc = "exec -p 0 /bin/kmeans-o -n %(nproc)d -p %(npoints)d\n" % dict(nproc = nprocs, npoints = 1000)
        elif (app_name == "linear_regression"):
            shrc = "exec -p 0 /bin/linear_r -n%(nproc)d /etc/key_1MB.txt\n" % dict(nproc = nprocs)
        elif (app_name == "lu"):
            shrc = "exec -p 0 /bin/lu -n%(nproc)d -m16\n" % dict(nproc = nprocs)
        elif (app_name == "mandel"):
            shrc = "exec -p 0 /bin/mandel -n%(nproc)d -x 30 -y 20\n" % dict(nproc = nprocs)
        elif (app_name == "mat_mult"):
            shrc = "exec -p 0 /bin/mat_mult -n%(nproc)d -s 10\n" % dict(nproc = nprocs) # s = matrix size
        elif (app_name == "mat_mult-opt"):
            shrc = "exec -p 0 /bin/mat_mult -n%(nproc)d -s 10\n" % dict(nproc = nprocs)
        elif (app_name == "pca"):
            shrc = "exec -p 0 /bin/pca -n%(nproc)d -r 16 -c 8\n" % dict(nproc = nprocs) # r = num rows, c = num cols
        elif (app_name == "pca-opt"):
            shrc = "exec -p 0 /bin/pca-opt -n%(nproc)d -r 16 -c 8\n" % dict(nproc = nprocs)
        elif (app_name == "radix"):
            shrc = "exec -p 0 /bin/radix -n%(nproc)d -k1024\n" % dict(nproc = nprocs)
        elif (app_name == "radix_ga"):
            shrc = "exec -p 0 /bin/radix_ga -n%(nproc)d -k1024\n" % dict(nproc = nprocs) # n = num_keys
        elif (app_name == "string_match"):
            shrc = "exec -p 0 /bin/string_m -n%(nproc)d /etc/key_1MB.txt\n" % dict(nproc = nprocs)
        elif (app_name == "swaptions"):
            shrc = "exec -p 0 /bin/swaption -n%(nproc)d -s 16 -t 1000\n" % dict(nproc = nprocs) # s = num swaptions (default 16), t = num simus (default 10000)
        else:
            print "*** Error: The application %s is not available in test mode", app_name
            sys.exit()
    else:
        if (app_name == "barnes"):
            shrc = "exec -p 0 /bin/barnes -n%(nproc)d -b%(nbody)d\n" % dict(nproc = nprocs, nbody = 1024)
        elif (app_name == "blackscholes"):
            shrc = "exec -p 0 /bin/blacksch -n %(nproc)d /etc/black_4K.txt\n" % dict(nproc = nprocs)
        elif (app_name == "boot_only"):
            shrc = "exec -p 0 /bin/boot_onl\n"
        elif (app_name == "cholesky"):
            shrc = "exec -p 0 /bin/cholesky -n%(nproc)d /etc/tk14.O\n" % dict(nproc = nprocs)
        elif (app_name == "fft"):
            shrc = "exec -p 0 /bin/fft -n%(nproc)d -m18\n" % dict(nproc = nprocs)
        elif (app_name == "fft_ga"):
            shrc = "exec -p 0 /bin/fft_ga -n%(nproc)d -m18\n" % dict(nproc = nprocs)
        elif (app_name == "fluidanimate"):
            shrc = "exec -p 0 /bin/fluidani -n%(nproc)d -i /etc/flui_35K.flu\n" % dict(nproc = nprocs)
        elif (app_name == "filter"):
            shrc = "exec -p 0 /bin/filter -l 1024 -c 1024 -n %(nproc)d -i /etc/img1024.raw\n" % dict(nproc = nprocs)
        elif (app_name == "filt_ga"):
            shrc = "exec -p 0 /bin/filt_ga -l 1024 -c 1024 -n %(nproc)d -i /etc/img1024.raw\n" % dict(nproc = nprocs)
        elif (app_name == "histogram"):
            shrc = "exec -p 0 /bin/histogra -n%(nproc)d /etc/histo.bmp\n" % dict(nproc = nprocs)
        elif (app_name == "histo-opt"):
            shrc = "exec -p 0 /bin/histo-op -n%(nproc)d /etc/histo.bmp\n" % dict(nproc = nprocs)
        elif (app_name == "kmeans"):
            shrc = "exec -p 0 /bin/kmeans -n %(nproc)d -p %(npoints)d\n" % dict(nproc = nprocs, npoints = 10000) # default npoints = 100000
        elif (app_name == "kmeans-opt"):
            shrc = "exec -p 0 /bin/kmeans-o -n %(nproc)d -p %(npoints)d\n" % dict(nproc = nprocs, npoints = 10000) # default npoints = 100000
        elif (app_name == "linear_regression"):
            shrc = "exec -p 0 /bin/linear_r -n%(nproc)d /etc/key_50MB.txt\n" % dict(nproc = nprocs)
        elif (app_name == "lu"):
            shrc = "exec -p 0 /bin/lu -n%(nproc)d -m512\n" % dict(nproc = nprocs)
        elif (app_name == "mandel"):
            shrc = "exec -p 0 /bin/mandel -n%(nproc)d\n" % dict(nproc = nprocs)
        elif (app_name == "mat_mult"):
            shrc = "exec -p 0 /bin/mat_mult -n%(nproc)d\n" % dict(nproc = nprocs)
        elif (app_name == "mat_mult-opt"):
            shrc = "exec -p 0 /bin/mat_mult -n%(nproc)d\n" % dict(nproc = nprocs)
        elif (app_name == "pca"):
            shrc = "exec -p 0 /bin/pca -n%(nproc)d\n" % dict(nproc = nprocs)
        elif (app_name == "pca-opt"):
            shrc = "exec -p 0 /bin/pca-opt -n%(nproc)d\n" % dict(nproc = nprocs)
        elif (app_name == "fmm"):
            shrc = "exec -p 0 /bin/fmm -n%(nproc)d -p%(nparticles)d\n" % dict(nproc = nprocs, nparticles = 1024)
        elif (app_name == "ocean"):
            shrc = "exec -p 0 /bin/ocean -n%(nproc)d -m%(gridsize)d\n" % dict(nproc = nprocs, gridsize = 66)
        elif (app_name == "radiosity"):
            shrc = "exec -p 0 /bin/radiosit -n %(nproc)d -batch\n" % dict(nproc = nprocs)
        elif (app_name == "radix"):
            shrc = "exec -p 0 /bin/radix -n%(nproc)d -k2097152\n" % dict(nproc = nprocs)
        elif (app_name == "radix_ga"):
            shrc = "exec -p 0 /bin/radix_ga -n%(nproc)d -k2097152\n" % dict(nproc = nprocs) # k = num_keys
        elif (app_name == "string_match"):
            shrc = "exec -p 0 /bin/string_m -n%(nproc)d /etc/key_50MB.txt\n" % dict(nproc = nprocs)
        elif (app_name == "raytrace"):
            shrc = "exec -p 0 /bin/raytrace -n%(nproc)d /etc/tea.env\n" % dict(nproc = nprocs)
        elif (app_name == "showimg"):
            shrc = "exec -p 0 /bin/showimg -i /etc/lena.sgi\n"
        elif (app_name == "swaptions"):
            shrc = "exec -p 0 /bin/swaption -n%(nproc)d -s 256 -t 1000\n" % dict(nproc = nprocs) # s = num swaptions (default 16), t = num simus (default 10000)
        elif (app_name == "watern"):
            shrc = "exec -p 0 /bin/watern -n%(nproc)d -m512\n" % dict(nproc = nprocs)
        elif (app_name == "waters"):
            shrc = "exec -p 0 /bin/waters -n%(nproc)d -m512\n" % dict(nproc = nprocs)
        else:
            assert(False)
    
    file = open(shrc_file_name, 'w')
    file.write(shrc)
    file.close()

    # Copie du shrc dans la future image disque
    cmd = ['cp', shrc_file_name, os.path.join(partition_root_path, "etc", "shrc")]
    print_and_call(cmd)
# end of gen_shrc



cmd = ['mkdir', '-p', os.path.join(scripts_path, data_dir)]
print_and_call(cmd)

gen_sym_links()
gen_soclib_conf()
compile_preloader()
compile_almos()
# Compile application once at the beginning not to intererfere with
# other simulations
for app in apps:
    compile_app(app)
    full_app_name = os.path.join(apps_dir, app, app)
    cmd = ['cp', full_app_name, os.path.join(partition_root_path, "bin", app)]
    print_and_call(cmd)

 
print "cd", top_path
os.chdir(top_path)

for i in nb_procs:
    x, y = get_x_y(i, cpu_per_cluster)
    x_width = get_nb_bits(x)
    y_width = get_nb_bits(y)
    nthreads = min(4, x * y)
    hard_config(x, y, x_width, y_width, cpu_per_cluster, hard_config_name, protocol)
    gen_arch_info_bib(x, y, arch_info_name, arch_info_bib_name)

    cmd = ['touch', topcell_name]
    print_and_call(cmd)

    cmd = ['make']
    retval = print_and_call(cmd)
    if retval != 0:
        sys.exit()
 
    for app in apps:
        # Generate shrc
        gen_shrc(app, i)

        # Remove current disk image
        print "rm", hdd_img_file_name
        os.remove(hdd_img_file_name)

        # Regenerate disk image
        hdd_img(partition_root_path, hdd_img_file_name, "fat32", bootloader_link_name)

        # Launch simulation
        if use_omp:
            cmd = ['./simul.x', '-THREADS', str(nthreads)]
        else:
            cmd = ['./simul.x']
        filename = os.path.join(scripts_path, data_dir, app + '_' + log_init_name + str(i))
        output = print_and_popen(cmd, filename)
 
        # Write simulation results to data directory
        file = open(filename, 'w')
        file.write(output)
        file.close()
 
        filename = os.path.join(scripts_path, data_dir, app + '_' + log_term_name + str(i))
        
        cmd = ['mv', os.path.join(top_path, 'term1'), filename]
        print_and_call(cmd)
 
        if rerun_stats:
            start2_found = False
            end_found = False
            for line in open(filename):
                if "[THREAD_COMPUTE_START]" in line:
                    start2 = line.split()[-1]
                    start2_found = True
                if "[THREAD_COMPUTE_END]" in line:
                    end = line.split()[-1]
                    end_found = True
            assert(start2_found and end_found)
          
            # Regenerate shrc and hdd to ensure having the same hdd image
            gen_shrc(app, i)
 
            # Relauching simulation with reset and dump of counters
            if use_omp:
                cmd = ['./simul.x', '-THREADS', str(nthreads), '--reset-counters', start2, '--dump-counters', end]
            else:
                cmd = ['./simul.x', '--reset-counters', start2, '--dump-counters', end]
            filename = os.path.join(scripts_path, data_dir, app + '_' + log_init_name + str(i))
            output = print_and_popen(cmd, filename)
           
            # Checking that the two terms obtained are identical
            import filecmp
            term_orig_file = os.path.join(scripts_path, data_dir, app + '_' + log_term_name + str(i))
            term_new_file = os.path.join(top_path, 'term1')
            assert(filecmp.cmp(term_orig_file, term_new_file))
 
            # Write simulation results (counters) to data directory
            file = open(filename, 'w')
            file.write(output)
            file.close()
 

## End of simulations



