// vim: ts=4 sw=4 expandtab ft=c

// Copyright (C) 2003-2005, 2007, 2009, 2011 The University of Melbourne.
// Copyright (C) 2014, 2016-2018, 2021 The Mercury team.
// This file is distributed under the terms specified in COPYING.LIB.

// mercury_term_size.c
//
// This module defines a function for measuring the sizes of terms.

#include "mercury_imp.h"
#include "mercury_deconstruct_macros.h"
#include "mercury_runtime_util.h" // For MR_STRERROR_BUF_SIZE.
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <stdlib.h>
#ifdef MR_HAVE_UNISTD_H
  #include <unistd.h>
#endif
#include <string.h>

#ifdef MR_RECORD_TERM_SIZES

MR_ComplexityCounter    MR_complexity_word_counter = 0;
MR_ComplexityCounter    MR_complexity_cell_counter = 0;
MR_ComplexityCounter    MR_complexity_tick_counter = 0;

static  void    MR_write_complexity_proc(MR_ComplexityProc *proc);
static  void    MR_complexity_output_args_desc(FILE *fp,
                    MR_ComplexityProc *proc);

MR_Unsigned
MR_term_size(MR_TypeInfo type_info, MR_Word term)
{
    MR_TypeCtorInfo         type_ctor_info;
    const MR_DuPtagLayout   *ptag_layout;
    const MR_DuFunctorDesc  *functor_desc;
    int                     ptag;
    int                     sectag;
    int                     arity;
    int                     size;

try_again:
    type_ctor_info = MR_TYPEINFO_GET_TYPE_CTOR_INFO(type_info);

    if (! MR_type_ctor_has_valid_rep(type_ctor_info)) {
        MR_fatal_error("MR_term_size: term of unknown representation");
    }

    switch (MR_type_ctor_rep(type_ctor_info)) {
        case MR_TYPECTOR_REP_DU:
        case MR_TYPECTOR_REP_DU_USEREQ:
            ptag = MR_tag(term);
            MR_index_or_search_ptag_layout(ptag, ptag_layout);

            switch (ptag_layout->MR_sectag_locn) {
                case MR_SECTAG_NONE:
#ifdef MR_DEBUG_TERM_SIZES
                    // We can index MR_sectag_alternatives for MR_SECTAG_NONE.
                    if (ptag_layout->MR_sectag_alternatives[0]->
                        MR_du_functor_orig_arity <= 0)
                    {
                        MR_fatal_error("MR_term_size: zero arity ptag none");
                    }

                    if (MR_heapdebug && MR_lld_print_enabled) {
                        printf("MR_term_size: du sectag none %p -> %d\n",
                            (void *) term,
                            (int) MR_field(MR_mktag(ptag), term, -1));
                        printf("type %s.%s/%d, functor %s\n",
                            type_ctor_info->MR_type_ctor_module_name,
                            type_ctor_info->MR_type_ctor_name,
                            type_ctor_info->MR_type_ctor_arity,
                            ptag_layout->MR_sectag_alternatives[0]->
                                MR_du_functor_name);
                    }
#endif
                    return MR_field(MR_mktag(ptag), term, -1);

                case MR_SECTAG_NONE_DIRECT_ARG:
                     // The compiler should not generate direct arg tags
                     // in term size recording grades.
                     MR_fatal_error("MR_term_size: DIRECT_ARG");

                case MR_SECTAG_LOCAL_REST_OF_WORD:  // fall-through
                case MR_SECTAG_LOCAL_BITS:
#ifdef MR_DEBUG_TERM_SIZES
                    if (MR_heapdebug && MR_lld_print_enabled) {
                        printf("MR_term_size: du sectag local %p\n",
                            (void *) term);
                    }
#endif
                    return 0;

                case MR_SECTAG_REMOTE_FULL_WORD:    // fall-through
                case MR_SECTAG_REMOTE_BITS:
#ifdef MR_DEBUG_TERM_SIZES
                    sectag = MR_field(MR_mktag(ptag), term, 0);

                    MR_index_or_search_sectag_functor(ptag_layout,
                        sectag, functor_desc);
                    if (functor_desc->MR_du_functor_orig_arity <= 0) {
                        MR_fatal_error("MR_term_size: zero arity ptag remote");
                    }

                    if (MR_heapdebug && MR_lld_print_enabled) {
                        printf("MR_term_size: du sectag remote %p -> %d\n",
                            (void *) term,
                            (int) MR_field(MR_mktag(ptag), term, -1));
                        printf("type %s.%s/%d, functor %s\n",
                            type_ctor_info->MR_type_ctor_module_name,
                            type_ctor_info->MR_type_ctor_name,
                            type_ctor_info->MR_type_ctor_arity,
                            functor_desc->MR_du_functor_name);
                    }
#endif
                    return MR_field(MR_mktag(ptag), term, -1);

                case MR_SECTAG_VARIABLE:
                    MR_fatal_error("MR_term_size: VARIABLE");

                default:
                    fprintf(stderr, "sectag_locn: %d\n",
                        (int) ptag_layout->MR_sectag_locn);
                    MR_fatal_error("MR_term_size: sectag_locn");
            }

        case MR_TYPECTOR_REP_EQUIV:
#ifdef MR_DEBUG_TERM_SIZES
            if (MR_heapdebug && MR_lld_print_enabled) {
                printf("MR_term_size: equiv %p\n", (void *) term);
            }
#endif
            type_info = MR_create_type_info(
                MR_TYPEINFO_GET_FIXED_ARITY_ARG_VECTOR(type_info),
                MR_type_ctor_layout(type_ctor_info).MR_layout_equiv);
            goto try_again;

        case MR_TYPECTOR_REP_EQUIV_GROUND:
#ifdef MR_DEBUG_TERM_SIZES
            if (MR_heapdebug && MR_lld_print_enabled) {
                printf("MR_term_size: equiv ground %p\n", (void *) term);
            }
#endif
            type_info = MR_pseudo_type_info_is_ground(
                MR_type_ctor_layout(type_ctor_info).MR_layout_equiv);
            goto try_again;

        case MR_TYPECTOR_REP_NOTAG:
        case MR_TYPECTOR_REP_NOTAG_USEREQ:
#ifdef MR_DEBUG_TERM_SIZES
            if (MR_heapdebug && MR_lld_print_enabled) {
                printf("MR_term_size: notag (usereq) %p\n", (void *) term);
            }
#endif
            MR_save_transient_hp();
            type_info = MR_create_type_info(
                MR_TYPEINFO_GET_FIXED_ARITY_ARG_VECTOR(type_info),
                MR_type_ctor_layout(type_ctor_info).MR_layout_notag->
                    MR_notag_functor_arg_type);
            MR_restore_transient_hp();
            goto try_again;

        case MR_TYPECTOR_REP_NOTAG_GROUND:
        case MR_TYPECTOR_REP_NOTAG_GROUND_USEREQ:
#ifdef MR_DEBUG_TERM_SIZES
            if (MR_heapdebug && MR_lld_print_enabled) {
                printf("MR_term_size: notag ground (usereq) %p\n",
                    (void *) term);
            }
#endif
            type_info = MR_pseudo_type_info_is_ground(
                MR_type_ctor_layout(type_ctor_info).MR_layout_notag
                ->MR_notag_functor_arg_type);
            goto try_again;

        case MR_TYPECTOR_REP_TUPLE:
            arity = MR_TYPEINFO_GET_VAR_ARITY_ARITY(type_info);
            if (arity == 0) {
                // Term may be a NULL pointer, so don't follow it.
                size = 0;
            } else {
                size = MR_field(MR_mktag(0), term, -1);
            }

#ifdef MR_DEBUG_TERM_SIZES
            if (MR_heapdebug && MR_lld_print_enabled) {
                printf("MR_term_size: tuple %p -> %d\n",
                    (void *) term, size);
            }
#endif
            return size;

        case MR_TYPECTOR_REP_PRED:
        case MR_TYPECTOR_REP_FUNC:
#ifdef MR_DEBUG_TERM_SIZES
            if (MR_heapdebug && MR_lld_print_enabled) {
                printf("MR_term_size: pred/func %p\n", (void *) term);
            }
#endif
            // Currently we don't collect stats on closure sizes.
            return 0;

        case MR_TYPECTOR_REP_ARRAY:
            // Currently we don't collect stats on array sizes.
#ifdef MR_DEBUG_TERM_SIZES
            if (MR_heapdebug && MR_lld_print_enabled) {
                printf("MR_term_size: array %p\n", (void *) term);
            }
#endif
            return 0;

        case MR_TYPECTOR_REP_BITMAP:
#ifdef MR_DEBUG_TERM_SIZES
            if (MR_heapdebug && MR_lld_print_enabled) {
                printf("MR_term_size: bitmap %p\n", (void *) term);
            }
#endif
            return 0;

        case MR_TYPECTOR_REP_ENUM:
        case MR_TYPECTOR_REP_ENUM_USEREQ:
#ifdef MR_DEBUG_TERM_SIZES
            if (MR_heapdebug && MR_lld_print_enabled) {
                printf("MR_term_size: enum (usereq) %p\n", (void *) term);
            }
#endif
            return 0;

        case MR_TYPECTOR_REP_FOREIGN_ENUM:
        case MR_TYPECTOR_REP_FOREIGN_ENUM_USEREQ:
#ifdef MR_DEBUG_TERMSIZES
            if (MR_heapdebug && MR_lld_print_enabled) {
                printf("MR_term_size: foreign enum (usereq) %p\n",
                    (void *) term);
            }
#endif
            return 0;

        case MR_TYPECTOR_REP_INT:
#ifdef MR_DEBUG_TERM_SIZES
            if (MR_heapdebug && MR_lld_print_enabled) {
                printf(
                    "MR_term_size: int %p %" MR_INTEGER_LENGTH_MODIFIER "d\n",
                    (void *) term, (MR_Integer) term);
            }
#endif
            return 0;

        case MR_TYPECTOR_REP_UINT:
#ifdef MR_DEBUG_TERM_SIZES
            if (MR_heapdebug && MR_lld_print_enabled) {
                printf(
                    "MR_term_size: uint %p %" MR_INTEGER_LENGTH_MODIFIER "u\n",
                    (void *) term, (MR_Unsigned) term);
            }
#endif
            return 0;

        case MR_TYPECTOR_REP_INT8:
#ifdef MR_DEBUG_TERM_SIZES
            if (MR_heapdebug && MR_lld_print_enabled) {
                printf(
                    "MR_term_size: int8 %p %" MR_INTEGER_LENGTH_MODIFIER "d\n",
                    (void *) term, (MR_Integer) term);
            }
#endif
            return 0;

        case MR_TYPECTOR_REP_UINT8:
#ifdef MR_DEBUG_TERM_SIZES
            if (MR_heapdebug && MR_lld_print_enabled) {
                printf(
                    "MR_term_size: uint8 %p %" MR_INTEGER_LENGTH_MODIFIER "u\n",
                    (void *) term, (MR_Unsigned) term);
            }
#endif
            return 0;

        case MR_TYPECTOR_REP_INT16:
#ifdef MR_DEBUG_TERM_SIZES
            if (MR_heapdebug && MR_lld_print_enabled) {
                printf(
                    "MR_term_size: int16 %p %" MR_INTEGER_LENGTH_MODIFIER "d\n",
                    (void *) term, (MR_Integer) term);
            }
#endif
            return 0;

        case MR_TYPECTOR_REP_UINT16:
#ifdef MR_DEBUG_TERM_SIZES
            if (MR_heapdebug && MR_lld_print_enabled) {
                printf(
                    "MR_term_size: uint16 %p %" MR_INTEGER_LENGTH_MODIFIER "u\n",
                    (void *) term, (MR_Unsigned) term);
            }
#endif
            return 0;

        case MR_TYPECTOR_REP_INT32:
#ifdef MR_DEBUG_TERM_SIZES
            if (MR_heapdebug && MR_lld_print_enabled) {
                printf(
                    "MR_term_size: int32 %p %" MR_INTEGER_LENGTH_MODIFIER "d\n",
                    (void *) term, (MR_Integer) term);
            }
#endif
            return 0;

        case MR_TYPECTOR_REP_UINT32:
#ifdef MR_DEBUG_TERM_SIZES
            if (MR_heapdebug && MR_lld_print_enabled) {
                printf(
                    "MR_term_size: uint32 %p %" MR_INTEGER_LENGTH_MODIFIER "u\n",
                    (void *) term, (MR_Unsigned) term);
            }
#endif
            return 0;

        // XXX Maybe we should also print the value of the term in the int64,
        // uint64 and float cases?
        case MR_TYPECTOR_REP_INT64:
#ifdef MR_DEBUG_TERM_SIZES
            if (MR_heapdebug && MR_lld_print_enabled) {
                printf(
                    "MR_term_size: int64 %p\n", (void *) term);
            }
#endif
            return 0;

        case MR_TYPECTOR_REP_UINT64:
#ifdef MR_DEBUG_TERM_SIZES
            if (MR_heapdebug && MR_lld_print_enabled) {
                printf(
                    "MR_term_size: uint64 %p\n", (void *) term);
            }
#endif
            return 0;

        case MR_TYPECTOR_REP_CHAR:
#ifdef MR_DEBUG_TERM_SIZES
            if (MR_heapdebug && MR_lld_print_enabled) {
                printf("MR_term_size: char %p %c\n",
                    (void *) term, (char) term);
            }
#endif
            return 0;

        case MR_TYPECTOR_REP_FLOAT:
#ifdef MR_DEBUG_TERM_SIZES
            if (MR_heapdebug && MR_lld_print_enabled) {
                printf("MR_term_size: float %p\n", (void *) term);
            }
#endif
            return 0;

        case MR_TYPECTOR_REP_STRING:
#ifdef MR_DEBUG_TERM_SIZES
            if (MR_heapdebug && MR_lld_print_enabled) {
                printf("MR_term_size: string %p '%s'\n",
                    (void *) term, (char *) term);
            }
#endif
            return 0;

        case MR_TYPECTOR_REP_SUCCIP:
        case MR_TYPECTOR_REP_HP:
        case MR_TYPECTOR_REP_CURFR:
        case MR_TYPECTOR_REP_MAXFR:
        case MR_TYPECTOR_REP_REDOFR:
        case MR_TYPECTOR_REP_REDOIP:
        case MR_TYPECTOR_REP_TRAIL_PTR:
        case MR_TYPECTOR_REP_TICKET:
#ifdef MR_DEBUG_TERM_SIZES
            if (MR_heapdebug && MR_lld_print_enabled) {
                printf("MR_term_size: impl artifact type %p\n", (void *) term);
            }
#endif
            return 0;

        case MR_TYPECTOR_REP_TYPEINFO:
        case MR_TYPECTOR_REP_TYPECLASSINFO:
        case MR_TYPECTOR_REP_TYPECTORINFO:
        case MR_TYPECTOR_REP_BASETYPECLASSINFO:
        case MR_TYPECTOR_REP_TYPEDESC:
        case MR_TYPECTOR_REP_TYPECTORDESC:
        case MR_TYPECTOR_REP_PSEUDOTYPEDESC:
#ifdef MR_DEBUG_TERM_SIZES
            if (MR_heapdebug && MR_lld_print_enabled) {
                printf("MR_term_size: type_info etc %p\n", (void *) term);
            }
#endif
            return 0;

        case MR_TYPECTOR_REP_SUBGOAL:
#ifdef MR_DEBUG_TERM_SIZES
            if (MR_heapdebug && MR_lld_print_enabled) {
                printf("MR_term_size: subgoal %p\n", (void *) term);
            }
#endif
            return 0;

        case MR_TYPECTOR_REP_C_POINTER:
        case MR_TYPECTOR_REP_STABLE_C_POINTER:
        case MR_TYPECTOR_REP_FOREIGN:
        case MR_TYPECTOR_REP_STABLE_FOREIGN:
#ifdef MR_DEBUG_TERM_SIZES
            if (MR_heapdebug && MR_lld_print_enabled) {
                printf("MR_term_size: c_pointer/foreign %p\n", (void *) term);
            }
#endif
            return 0;

        case MR_TYPECTOR_REP_REFERENCE:
#ifdef MR_DEBUG_TERM_SIZES
            if (MR_heapdebug && MR_lld_print_enabled) {
                printf("MR_term_size: reference %p\n", (void *) term);
            }
#endif
            return 1;

        case MR_TYPECTOR_REP_DUMMY:
#ifdef MR_DEBUG_TERM_SIZES
            if (MR_heapdebug && MR_lld_print_enabled) {
                printf("MR_term_size: dummy %p\n",
                    (void *) term);
            }
#endif
            return 0;

        case MR_TYPECTOR_REP_VOID:
            MR_fatal_error("MR_term_size: VOID");

        case MR_TYPECTOR_REP_UNKNOWN:
            MR_fatal_error("MR_term_size: UNKNOWN");

        default:
            fprintf(stderr, "default rep: %d\n",
                    (int) MR_type_ctor_rep(type_ctor_info));
            MR_fatal_error("MR_term_size: default");
    }

    MR_fatal_error("MR_term_size: unexpected fallthrough");
}

void
MR_write_complexity_procs(void)
{
    int                 proc_num;
    MR_ComplexityProc   *proc;

    for (proc_num = 0; proc_num < MR_num_complexity_procs; proc_num++) {
        proc = &MR_complexity_procs[proc_num];
        if (proc->MR_clp_num_profiled_args >= 0) {
            MR_write_complexity_proc(proc);
        }
    }
}

#define MR_COMPLEXITY_ARGS_DIR   "ComplexityArgs"
#define MR_COMPLEXITY_DATA_DIR   "ComplexityData"

static  MR_bool MR_have_created_complexity_dirs = MR_FALSE;
static  MR_bool MR_have_printed_complexity_dirs_error = MR_FALSE;

static void
MR_write_complexity_proc(MR_ComplexityProc *proc)
{
    char                    *full_proc_name;
    int                     full_proc_name_len;
    FILE                    *fp;
    char                    *args_filename;
    char                    *data_filename;
    const char              *data_filemode;
    struct stat             statbuf;
    char                    *slash;
    MR_ComplexityMetrics    *metrics;
    int                     num_profiled_args;
    int                     *sizes;
    int                     num_slots;
    MR_ComplexityPastSlots  *past_slots;
    char                    *cmd_buf;
    char                    errbuf[MR_STRERROR_BUF_SIZE];

    full_proc_name_len = strlen(proc->MR_clp_full_proc_name);
    full_proc_name = MR_malloc(100 + full_proc_name_len);
    strcpy(full_proc_name, proc->MR_clp_full_proc_name);

    // We can't have slash characters in the filenames we construct from
    // full_proc_name.

    while ((slash = strchr(full_proc_name, '/')) != NULL) {
        *slash = ':';
    }

    cmd_buf = MR_malloc(100 + 2 * full_proc_name_len);
        // Will be big enough.

    if (! MR_have_created_complexity_dirs) {
        sprintf(cmd_buf, "mkdir -p %s %s",
            MR_COMPLEXITY_ARGS_DIR, MR_COMPLEXITY_DATA_DIR);
        if (system(cmd_buf) != 0) {
            if (! MR_have_printed_complexity_dirs_error) {
                fprintf(stderr, "%s: cannot create %s and %s: %s\n",
                    MR_progname, MR_COMPLEXITY_ARGS_DIR,
                    MR_COMPLEXITY_DATA_DIR,
                    MR_strerror(errno, errbuf, sizeof(errbuf)));
                // there is no point in aborting
                MR_have_printed_complexity_dirs_error = MR_TRUE;
                return;
            }
        }
        MR_have_created_complexity_dirs = MR_TRUE;
    }

    args_filename = MR_malloc(100 + full_proc_name_len);
        // Will be big enough.
    sprintf(args_filename, "%s/%s", MR_COMPLEXITY_ARGS_DIR, full_proc_name);

    if (stat(args_filename, &statbuf) != 0) {
        // args_filename does not exist.
        fp = fopen(args_filename, "w");
        if (fp == NULL) {
            fprintf(stderr, "%s: cannot open %s: %s\n",
                MR_progname, args_filename,
                MR_strerror(errno, errbuf, sizeof(errbuf)));
            // There is no point in aborting.
            return;
        }

        MR_complexity_output_args_desc(fp, proc);
        fclose(fp);
        data_filemode = "w";
    } else {
        // args_filename does exist.
        char    *tmp_filename;

        tmp_filename = MR_malloc(100 + full_proc_name_len);
        sprintf(tmp_filename, "%s/%s.tmp",
            MR_COMPLEXITY_ARGS_DIR, full_proc_name);
        fp = fopen(tmp_filename, "w");
        if (fp == NULL) {
            fprintf(stderr, "%s: cannot open %s: %s\n",
                MR_progname, tmp_filename,
                MR_strerror(errno, errbuf, sizeof(errbuf)));
            // There is no point in aborting.
            return;
        }

        MR_complexity_output_args_desc(fp, proc);
        fclose(fp);

        sprintf(cmd_buf, "cmp -s %s %s", args_filename, tmp_filename);
        if (system(cmd_buf) == 0) {
            // The files are identical.
            (void) unlink(tmp_filename);
            data_filemode = "a";
        } else {
            // The files are different.
            rename(tmp_filename, args_filename);
            data_filemode = "w";
        }
    }

    data_filename = MR_malloc(100 + full_proc_name_len);
    sprintf(data_filename, "%s/%s", MR_COMPLEXITY_DATA_DIR, full_proc_name);

    fp = fopen(data_filename, data_filemode);
    if (fp == NULL) {
        fprintf(stderr, "%s: cannot open %s: %s\n",
            MR_progname, data_filename,
            MR_strerror(errno, errbuf, sizeof(errbuf)));
        // There is no point in aborting.
        return;
    }

    num_profiled_args = proc->MR_clp_num_profiled_args;

    metrics = proc->MR_clp_metrics;
    sizes = proc->MR_clp_sizes;
    num_slots = proc->MR_clp_next_slot_num;
    past_slots = proc->MR_clp_past_slots;

    do {
        int slot, arg;

        for (slot = num_slots - 1; slot >= 0; slot--) {
            fprintf(fp, "%d %d %d",
                metrics[slot].MR_clpm_num_words,
                metrics[slot].MR_clpm_num_cells,
                metrics[slot].MR_clpm_num_ticks);

            for (arg = 0; arg < num_profiled_args; arg++) {
                fprintf(fp, " %d", sizes[slot * num_profiled_args + arg]);
            }

            fprintf(fp, "\n");
        }

        if (past_slots == NULL) {
            break;
        }

        metrics = past_slots->MR_clpps_metrics;
        sizes = past_slots->MR_clpps_sizes;
        num_slots = MR_COMPLEXITY_SLOTS_PER_CHUNK;
        past_slots = past_slots->MR_clpps_previous;
    } while (MR_TRUE);

    (void) fclose(fp);
}

static void
MR_complexity_output_args_desc(FILE *fp, MR_ComplexityProc *proc)
{
    int                     arg;
    int                     num_args;
    MR_ComplexityArgInfo    *arg_infos;

    arg_infos = proc->MR_clp_arg_infos;
    num_args = proc->MR_clp_num_args;

    for (arg = 0; arg < num_args; arg++) {
        if (arg_infos[arg].MR_clpai_maybe_name != NULL) {
            fprintf(fp, "%s ", arg_infos[arg].MR_clpai_maybe_name);
        } else {
            fprintf(fp, "_ ");
        }

        switch (arg_infos[arg].MR_clpai_kind) {
            case MR_COMPLEXITY_INPUT_VAR_SIZE:
                fprintf(fp, "profiled_input\n");
                break;
            case MR_COMPLEXITY_INPUT_FIX_SIZE:
                fprintf(fp, "unprofiled_input\n");
                break;
            case MR_COMPLEXITY_OUTPUT:
                fprintf(fp, "output\n");
                break;
            default:
                fprintf(fp, "unknown\n");
                break;
        }
    }
}

void
MR_init_complexity_proc(int proc_num, const char *fullname,
    int num_profiled_args, int num_args, MR_ComplexityArgInfo *arg_infos)
{
    MR_ComplexityProc   *proc;

    if (MR_complexity_procs == NULL) {
        fprintf(stderr, "%s: executable wasn't fully prepared "
            "for complexity experiment\n", MR_progname);
        exit(1);
    }

    proc = &MR_complexity_procs[proc_num];
    if (! MR_streq(fullname, proc->MR_clp_full_proc_name)) {
        fprintf(stderr, "%s: proc_num %d is %s: expected %s\n",
            MR_progname, proc_num, proc->MR_clp_full_proc_name, fullname);
        exit(1);
    }

    if (proc->MR_clp_num_profiled_args >= 0) {
        fprintf(stderr, "%s: proc_num %d: duplicate initialization\n",
            MR_progname, proc_num);
        exit(1);
    }

    if (num_profiled_args < 0) {
        fprintf(stderr, "%s: proc_num %d: bad num_profiled_args\n",
            MR_progname, proc_num);
        exit(1);
    }

    proc->MR_clp_num_profiled_args = num_profiled_args;
    proc->MR_clp_num_args = num_args;
    proc->MR_clp_arg_infos = arg_infos;

    proc->MR_clp_metrics = MR_NEW_ARRAY(MR_ComplexityMetrics,
        MR_COMPLEXITY_SLOTS_PER_CHUNK);
    proc->MR_clp_sizes = MR_NEW_ARRAY(int,
        MR_COMPLEXITY_SLOTS_PER_CHUNK * num_profiled_args);
}

void
MR_check_complexity_init(void)
{
    int                 proc_num;
    MR_bool             printed_heading;
    MR_ComplexityProc   *proc;

    printed_heading = MR_FALSE;
    for (proc_num = 0; proc_num < MR_num_complexity_procs; proc_num++) {
        proc = &MR_complexity_procs[proc_num];

        if (proc->MR_clp_num_profiled_args < 0) {
            if (! printed_heading) {
                fprintf(stderr, "%s: the following procedures are "
                    "not available for complexity experiment:\n",
                    MR_progname);
                printed_heading = MR_TRUE;
            }

            fprintf(stderr, "%s\n", proc->MR_clp_full_proc_name);
        }
    }

    if (printed_heading) {
        exit(1);
    }
}

MR_ComplexityIsActive
MR_complexity_is_active_func(int num_procs, int proc_num, const char *name,
    int num_profiled_inputs)
{
    MR_ComplexityProc   *proc;

    if (num_procs != MR_num_complexity_procs || MR_complexity_procs == NULL) {
        fprintf(stderr, "%s: executable wasn't fully prepared "
            "for complexity experiment\n", MR_progname);
        exit(1);
    }

    if (proc_num >= num_procs) {
        fprintf(stderr, "%s: proc_num %d >= num_procs %d\n",
            MR_progname, proc_num, num_procs);
        exit(1);
    }

    proc = &MR_complexity_procs[proc_num];
    if (! MR_streq(name, proc->MR_clp_full_proc_name)) {
        fprintf(stderr, "%s: proc_num %d is %s: expected %s\n",
            MR_progname, proc_num, proc->MR_clp_full_proc_name, name);
        exit(1);
    }

    if (proc->MR_clp_num_profiled_args != num_profiled_inputs) {
        fprintf(stderr, "%s: proc_num %d: bad num_profiled_inputs\n",
            MR_progname, proc_num);
        exit(1);
    }

    return proc->MR_clp_is_active;
}

int
MR_complexity_call_func(int procnum)
{
    MR_ComplexityProc       *proc;
    MR_ComplexityMetrics    *metrics;
    int                     slot;

    proc = &MR_complexity_procs[procnum];
    slot = proc->MR_clp_next_slot_num;
    if (slot < MR_COMPLEXITY_SLOTS_PER_CHUNK) {
        proc->MR_clp_next_slot_num++;
    } else {
        MR_ComplexityPastSlots  *past_slots;

        past_slots = MR_NEW(MR_ComplexityPastSlots);
        past_slots->MR_clpps_metrics = proc->MR_clp_metrics;
        past_slots->MR_clpps_sizes = proc->MR_clp_sizes;
        past_slots->MR_clpps_previous = proc->MR_clp_past_slots;
        proc->MR_clp_past_slots = past_slots;

        proc->MR_clp_metrics = MR_NEW_ARRAY(MR_ComplexityMetrics,
            MR_COMPLEXITY_SLOTS_PER_CHUNK);
        proc->MR_clp_sizes = MR_NEW_ARRAY(int,
            MR_COMPLEXITY_SLOTS_PER_CHUNK * proc->MR_clp_num_profiled_args);
        proc->MR_clp_next_slot_num = 1;
        slot = 0;
    }
    metrics = &proc->MR_clp_metrics[slot];
    metrics->MR_clpm_num_words -= MR_complexity_word_counter;
    metrics->MR_clpm_num_cells -= MR_complexity_cell_counter;
    metrics->MR_clpm_num_ticks -= MR_complexity_tick_counter;
    proc->MR_clp_is_active = MR_COMPLEXITY_IS_ACTIVE;

    return slot;
}

void
MR_complexity_fill_size_slot(MR_ComplexityProc *proc, int slot,
    int num_profiled_args, int argnum, int size)
{
    MR_ComplexityCounter    *sizes;

    sizes = proc->MR_clp_sizes;
    sizes[(slot * proc->MR_clp_num_profiled_args) + argnum] = size;
}

void
MR_complexity_leave_func(int procnum, int slot)
{
    MR_ComplexityProc       *proc;
    MR_ComplexityMetrics    *metrics;

    proc = &MR_complexity_procs[procnum];
    metrics = &proc->MR_clp_metrics[slot];
    metrics->MR_clpm_num_words += MR_complexity_word_counter;
    metrics->MR_clpm_num_cells += MR_complexity_cell_counter;
    metrics->MR_clpm_num_ticks += MR_complexity_tick_counter;
    proc->MR_clp_is_active = MR_COMPLEXITY_IS_INACTIVE;
}

void
MR_complexity_redo_func(int procnum, int slot)
{
    MR_ComplexityProc       *proc;
    MR_ComplexityMetrics    *metrics;

    proc = &MR_complexity_procs[procnum];
    metrics = &proc->MR_clp_metrics[slot];
    metrics->MR_clpm_num_words -= MR_complexity_word_counter;
    metrics->MR_clpm_num_cells -= MR_complexity_cell_counter;
    metrics->MR_clpm_num_ticks -= MR_complexity_tick_counter;
    proc->MR_clp_is_active = MR_COMPLEXITY_IS_ACTIVE;
}

#endif  // MR_RECORD_TERM_SIZES
