from typing import List

import math
import numpy as np
import tensorflow as tf
from absl import flags
from six.moves import xrange

import data_utils
import model
import tpu_estimator

# TPU parameters
flags.DEFINE_string("master", default=None, help="master")
flags.DEFINE_string(
    "tpu",
    default=None,
    help="The Cloud TPU to use for training. This should be either the name "
    "used when creating the Cloud TPU, or a grpc://ip.address.of.tpu:8470 url.",
)
flags.DEFINE_string(
    "gcp_project",
    default=None,
    help="Project name for the Cloud TPU-enabled project. If not specified, "
    "we will attempt to automatically detect the GCE project from metadata.",
)
flags.DEFINE_string(
    "tpu_zone",
    default=None,
    help="GCE zone where the Cloud TPU is located in. If not specified, we "
    "will attempt to automatically detect the GCE project from metadata.",
)
flags.DEFINE_bool("use_tpu", default=True, help="Use TPUs rather than plain CPUs.")
flags.DEFINE_integer("num_hosts", default=1, help="number of TPU hosts")
flags.DEFINE_integer("num_core_per_host", default=8, help="number of cores per host")

# Experiment (data/checkpoint/directory) parameters
flags.DEFINE_string("data_dir", default="", help="Path to tf-records directory.")
flags.DEFINE_string(
    "record_info_dir",
    default="",
    help="Path to local directory containing filenames.txt.",
)
flags.DEFINE_string(
    "corpus_info_path", default="", help="Path to corpus-info.json file."
)
flags.DEFINE_string("model_dir", default=None, help="Estimator model_dir.")
flags.DEFINE_bool("do_eval", default=False, help="Whether to run eval on the dev set.")
flags.DEFINE_bool("track_mean", default=True, help="Trace mean loss during training.")
flags.DEFINE_string(
    "eval_ckpt_path",
    None,
    help="Checkpoint path for evaluation."
    "If set, model_dir will be ignored."
    "If unset, will use the latest ckpt in model_dir.",
)
flags.DEFINE_string(
    "warm_start_path",
    None,
    help="Checkpoint path for warm start."
    "If set, will clear Adam states."
    "Note that the new model_dir should be different"
    " from warm_start_path.",
)

# Optimization paramenters
flags.DEFINE_float("learning_rate", default=2.5e-4, help="Maximum learning rate.")
flags.DEFINE_float("clip", default=0.25, help="Gradient clipping value.")
# for cosine decay
flags.DEFINE_float("min_lr_ratio", default=0.01, help="Minimum ratio learning rate.")
flags.DEFINE_integer(
    "warmup_steps", default=0, help="Number of steps for linear lr warmup."
)

# Training parameters
flags.DEFINE_integer("train_batch_size", default=60, help="Size of train batch.")
flags.DEFINE_integer("eval_batch_size", default=60, help="Size of valid batch.")
flags.DEFINE_integer(
    "train_steps", default=100000, help="Total number of training steps."
)
flags.DEFINE_integer(
    "iterations", default=500, help="Number of iterations per repeat loop."
)
flags.DEFINE_integer(
    "save_steps", default=10000, help="number of steps for model checkpointing."
)

# Evaluation parameters
flags.DEFINE_integer(
    "max_eval_batch", default=-1, help="Set -1 to turn off. Only used in test mode."
)
flags.DEFINE_bool("do_eval_only", default=False, help="Run evaluation only.")
flags.DEFINE_integer(
    "start_eval_steps",
    default=10000,
    help="Which checkpoint to start with in `do_eval_only` mode.",
)
flags.DEFINE_integer(
    "eval_interval",
    default=100000,
    help="Only eval specific steps to save time eg 100k, 200k, 300k...",
)
flags.DEFINE_string("eval_split", "valid", help="Which data split to evaluate.")

# Model paramenters
flags.DEFINE_integer("tgt_len", default=70, help="Number of steps to predict")
flags.DEFINE_integer("mem_len", default=70, help="Number of steps to cache")
flags.DEFINE_bool("same_length", default=False, help="Same length attention")
flags.DEFINE_integer("clamp_len", default=-1, help="Clamp length")

flags.DEFINE_integer("n_layer", default=6, help="Number of layers.")
flags.DEFINE_integer("d_model", default=500, help="Dimension of the model.")
flags.DEFINE_integer("d_embed", default=500, help="Dimension of the embeddings.")
flags.DEFINE_integer("n_head", default=10, help="Number of attention heads.")
flags.DEFINE_integer("d_head", default=50, help="Dimension of each attention head.")
flags.DEFINE_integer(
    "d_inner",
    default=1000,
    help="Dimension of inner hidden size in positionwise feed-forward.",
)
flags.DEFINE_float("dropout", default=0.1, help="Dropout rate.")
flags.DEFINE_float("dropatt", default=0.1, help="Attention dropout rate.")
flags.DEFINE_bool("untie_r", default=False, help="untie r_w_bias and r_r_bias")

# Adaptive Softmax / Embedding
flags.DEFINE_bool("tie_weight", default=True, help="Tie embedding and softmax weight.")
flags.DEFINE_integer(
    "div_val", default=1, help="Divide the embedding size by this val for each bin"
)
flags.DEFINE_bool(
    "proj_share_all_but_first",
    default=False,
    help="True to share all but first projs, False not to share.",
)
flags.DEFINE_bool(
    "proj_same_dim", default=True, help="Project the bin with the same dimension."
)

# Parameter initialization
flags.DEFINE_enum(
    "init",
    default="normal",
    enum_values=["normal", "uniform"],
    help="Initialization method.",
)
flags.DEFINE_float(
    "init_std", default=0.02, help="Initialization std when init is normal."
)
flags.DEFINE_float(
    "proj_init_std", default=0.01, help="Initialization std for embedding projection."
)
flags.DEFINE_float(
    "init_range", default=0.1, help="Initialization std when init is uniform."
)

FLAGS = flags.FLAGS

def metric_fn(loss):
    """Evaluation metric Fn which runs on CPU."""
    perplexity = tf.exp(tf.reduce_mean(loss))
    bpc = tf.reduce_mean(loss) / tf.constant(math.log(2))
    return {"perplexity": tf.metrics.mean(perplexity), "bpc": tf.metrics.mean(bpc)}

def get_model_fn(
    n_token: int,
    cutoffs: List[int],
    train_bin_sizes: List[int],
    eval_bin_sizes: List[int],
):
    def model_fn(features: dict, labels, mode, params: dict):
        # labels is dummy
        is_training = mode == tf.estimator.ModeKeys.TRAIN

        batch_size = params["batch_size"]

        mems = params["cache"]
        inp = tf.transpose(features["inputs"], [1, 0])
        tgt = tf.transpose(features["labels"], [1, 0])

        bin_sizes = train_bin_sizes if is_training else eval_bin_sizes
        if bin_sizes:
            inp_perms = [tf.transpose(features["inp_mask"], [1, 0])]
            tgt_perms = [tf.transpose(features["tgt_mask"], [1, 0])]

            head_tgt = tf.transpose(features["head_labels"], [1, 0])

            for b in range(len(bin_sizes)):
                inp_perm = tf.transpose(features["inp_perm_{}".format(b)], [1, 0, 2])
                tgt_perm = tf.transpose(features["tgt_perm_{}".format(b)], [1, 0, 2])

                inp_perms.append(inp_perm)
                tgt_perms.append(tgt_perm)
        else:
            inp_perms, tgt_perms, head_tgt = None, None, None

        if FLAGS.init == "uniform":
            initializer = tf.initializers.random_uniform(
                minval=-FLAGS.init_range, maxval=FLAGS.init_range, seed=None
            )
        elif FLAGS.init == "normal":
            initializer = tf.initializers.random_normal(
                stddev=FLAGS.init_std, seed=None
            )
            proj_initializer = tf.initializers.random_normal(
                stddev=FLAGS.proj_init_std, seed=None
            )

        tie_projs = [False for _ in range(len(cutoffs) + 1)]
        if FLAGS.proj_share_all_but_first:
            for i in range(1, len(tie_projs)):
                tie_projs[i] = True

        tf.logging.info("Vocab size : {}".format(n_token))
        tf.logging.info("Batch size : {}".format(batch_size))

        loss, new_mems, output_last_layer = model.transformer(
            dec_inp=inp,
            target=tgt,
            mems=mems,
            n_token=n_token,
            n_layer=FLAGS.n_layer,
            d_model=FLAGS.d_model,
            d_embed=FLAGS.d_embed,
            n_head=FLAGS.n_head,
            d_head=FLAGS.d_head,
            d_inner=FLAGS.d_inner,
            dropout=FLAGS.dropout,
            dropatt=FLAGS.dropatt,
            initializer=initializer,
            is_training=is_training,
            mem_len=FLAGS.mem_len,
            cutoffs=cutoffs,
            div_val=FLAGS.div_val,
            tie_projs=tie_projs,
            input_perms=inp_perms,
            target_perms=tgt_perms,
            head_target=head_tgt,
            same_length=FLAGS.same_length,
            clamp_len=FLAGS.clamp_len,
            use_tpu=FLAGS.use_tpu,
            untie_r=FLAGS.untie_r,
            proj_same_dim=FLAGS.proj_same_dim,
        )
        tf.logging.info("output_last_layer size: {}".format(output_last_layer.shape))

        total_loss = tf.reduce_mean(loss)

        if mode == tf.estimator.ModeKeys.EVAL:
            if FLAGS.use_tpu:
                with tf.colocate_with(total_loss):
                    total_loss = (
                        tf.contrib.tpu.cross_replica_sum(total_loss)
                        / FLAGS.num_hosts
                        / FLAGS.num_core_per_host
                    )
            metric_loss = tf.tile(tf.reshape(total_loss, [1, 1]), [batch_size, 1])
            eval_spec = tf.contrib.tpu.TPUEstimatorSpec(
                mode=mode, loss=total_loss, eval_metrics=(metric_fn, [metric_loss])
            )

            eval_spec.cache = new_mems

            return eval_spec

        # Configuring the optimization step.
        global_step = tf.train.get_global_step()

        # increase the learning rate linearly
        if FLAGS.warmup_steps > 0:
            warmup_lr = (
                tf.to_float(global_step)
                / tf.to_float(FLAGS.warmup_steps)
                * FLAGS.learning_rate
            )
        else:
            warmup_lr = 0.0

        # number of parameters
        num_params = np.sum([np.prod(v.shape) for v in tf.trainable_variables()])
        tf.logging.info("#params: {}".format(num_params))

        # format_str = '{{:<{0}s}}\t{{}}'.format(
        #     max([len(v.name) for v in tf.trainable_variables()]))
        # for v in tf.trainable_variables():
        #   tf.logging.info(format_str.format(v.name, v.get_shape()))

        # decay the learning rate using the cosine schedule
        decay_lr = tf.train.cosine_decay(
            FLAGS.learning_rate,
            global_step=global_step - FLAGS.warmup_steps,
            decay_steps=FLAGS.train_steps - FLAGS.warmup_steps,
            alpha=FLAGS.min_lr_ratio,
        )

        learning_rate = tf.where(global_step < FLAGS.warmup_steps, warmup_lr, decay_lr)

        if FLAGS.use_tpu:
            optimizer = tf.contrib.tpu.CrossShardOptimizer(
                tf.train.AdamOptimizer(learning_rate=learning_rate)
            )
            # GradientDescentOptimizer
        else:
            optimizer = tf.train.AdamOptimizer(learning_rate=learning_rate)

        grads_and_vars = optimizer.compute_gradients(total_loss)
        gradients, variables = zip(*grads_and_vars)
        clipped, _ = tf.clip_by_global_norm(gradients, FLAGS.clip)
        train_op = optimizer.apply_gradients(
            zip(clipped, variables), global_step=tf.train.get_global_step()
        )

        # Constucting TPUEstimatorSpec with cache.
        train_spec = tf.contrib.tpu.TPUEstimatorSpec(
            mode=mode, loss=total_loss, train_op=train_op
        )

        if FLAGS.mem_len < FLAGS.tgt_len:
            new_mems = [new_mems[: FLAGS.mem_len] for mem_t in new_mems]
        train_spec.cache = new_mems

        return train_spec

    return model_fn

def get_cache_fn(mem_len: int):
    def cache_fn(batch_size: int) -> List:
        mems = []
        for l in xrange(FLAGS.n_layer):
            if mem_len > 0:
                mems.append(
                    tf.zeros([mem_len, batch_size, FLAGS.d_model], dtype=tf.float32)
                )
            else:
                mems.append(tf.zeros([mem_len], dtype=tf.float32))

        return mems

    return cache_fn

class EvalManager:
    def __init__(self, model_dir: str) -> None:
        self.path = "{}/eval_dict.txt".format(model_dir)
        self.record = {}
        if not tf.io.gfile.exists(self.path):
            self.save()
        self.record = self.load()

    def load(self) -> dict:
        with tf.Session() as sess:
            d_string: str = sess.run(tf.io.read_file(self.path))
            return eval(d_string)

    def save(self) -> None:
        with tf.Session() as sess:
            sess.run(tf.io.write_file(self.path, str(self.record)))
        tf.logging.info("saving eval record: {}".format(str(self.record)))

def main(unused_argv) -> None:
    del unused_argv  # Unused

    tf.logging.set_verbosity(tf.logging.INFO)

    # Get corpus info
    corpus_info = data_utils.get_corpus_info(FLAGS.corpus_info_path)
    n_token = corpus_info["vocab_size"]
    cutoffs = corpus_info["cutoffs"][1:-1]

    if FLAGS.save_steps == 0:
        FLAGS.save_steps = None

    if not FLAGS.do_eval_only:
        # Get train input function
        train_input_fn, train_record_info = data_utils.get_input_fn(
            record_info_dir=FLAGS.record_info_dir,
            split="train",
            per_host_bsz=FLAGS.train_batch_size // FLAGS.num_hosts,
            tgt_len=FLAGS.tgt_len,
            num_core_per_host=FLAGS.num_core_per_host,
            num_hosts=FLAGS.num_hosts,
            use_tpu=FLAGS.use_tpu,
        )
        train_bin_sizes = train_record_info["bin_sizes"]
        num_train_batch = train_record_info["num_batch"]

        # Get train cache function
        train_cache_fn = get_cache_fn(FLAGS.mem_len)
    else:
        train_bin_sizes = []
        num_train_batch = None
        train_cache_fn = None

    if FLAGS.do_eval or FLAGS.do_eval_only:
        assert FLAGS.num_hosts == 1
        # Get eval input function
        eval_input_fn, eval_record_info = data_utils.get_input_fn(
            record_info_dir=FLAGS.record_info_dir,
            split=FLAGS.eval_split,
            per_host_bsz=FLAGS.eval_batch_size // FLAGS.num_hosts,
            tgt_len=FLAGS.tgt_len,
            num_core_per_host=FLAGS.num_core_per_host,
            num_hosts=FLAGS.num_hosts,
            use_tpu=FLAGS.use_tpu,
        )
        eval_bin_sizes = eval_record_info["bin_sizes"]
        num_eval_batch = eval_record_info["num_batch"]

        if FLAGS.max_eval_batch > 0:
            num_eval_batch = min(FLAGS.max_eval_batch, num_eval_batch)

        # Get eval cache function
        eval_cache_fn = get_cache_fn(FLAGS.mem_len)
        model_fn = get_model_fn(n_token, cutoffs, train_bin_sizes, eval_bin_sizes)
    else:
        eval_cache_fn = None
        model_fn = get_model_fn(n_token, cutoffs, train_bin_sizes, [])

    ##### Create estimator
    # TPU Configuration
    tpu_cluster_resolver = tf.contrib.cluster_resolver.TPUClusterResolver(
        FLAGS.tpu, zone=FLAGS.tpu_zone, project=FLAGS.gcp_project
    )

    per_host_input = tf.contrib.tpu.InputPipelineConfig.PER_HOST_V2
    run_config = tf.contrib.tpu.RunConfig(
        cluster=tpu_cluster_resolver,
        model_dir=FLAGS.model_dir,
        session_config=tf.ConfigProto(
            allow_soft_placement=True, log_device_placement=True
        ),
        tpu_config=tf.contrib.tpu.TPUConfig(
            iterations_per_loop=FLAGS.iterations,
            num_shards=FLAGS.num_core_per_host * FLAGS.num_hosts,
            per_host_input_for_training=per_host_input,
        ),
        keep_checkpoint_max=100000,  # effectively save all checkpoints
        save_checkpoints_secs=None,
        save_checkpoints_steps=FLAGS.save_steps,
    )

    # warm start
    warm_start_from = None
    if FLAGS.warm_start_path is not None:
        warm_start_from = tf.estimator.WarmStartSettings(
            ckpt_to_initialize_from=FLAGS.warm_start_path
        )

    # TPU Estimator
    estimator = tpu_estimator.TPUEstimator(
        model_fn=model_fn,
        train_cache_fn=train_cache_fn,
        eval_cache_fn=eval_cache_fn,
        use_tpu=FLAGS.use_tpu,
        config=run_config,
        params={"data_dir": FLAGS.data_dir, "track_mean": FLAGS.track_mean},
        train_batch_size=FLAGS.train_batch_size,
        eval_batch_size=FLAGS.eval_batch_size,
        warm_start_from=warm_start_from,
    )

    if FLAGS.do_eval_only:
        if FLAGS.eval_ckpt_path is not None:
            ret = estimator.evaluate(
                input_fn=eval_input_fn,
                steps=num_eval_batch,
                checkpoint_path=FLAGS.eval_ckpt_path,
            )
            tf.logging.info("=" * 200)
            log_str = "Eval results | "
            for key, val in ret.items():
                log_str += "{} {} | ".format(key, val)
            tf.logging.info(log_str)
            tf.logging.info("=" * 200)
        else:
            eval_manager = EvalManager(FLAGS.model_dir)
            ckpt_state = tf.train.get_checkpoint_state(FLAGS.model_dir)
            eval_results = []
            for eval_checkpoint in ckpt_state.all_model_checkpoint_paths:
                if not tf.io.gfile.exists(eval_checkpoint + ".index"):
                    continue
                global_step = int(eval_checkpoint.split("-")[-1])
                if (
                    global_step < FLAGS.start_eval_steps
                    # or global_step > FLAGS.train_steps  # defaults stops eval after 100k steps
                    or global_step % FLAGS.eval_interval != 0
                    or global_step in eval_manager.record
                ):
                    continue  # skip
                ret = estimator.evaluate(
                    input_fn=eval_input_fn,
                    steps=num_eval_batch,
                    checkpoint_path=eval_checkpoint,
                )
                eval_results.append(ret)
                eval_manager.record[global_step] = ret
                eval_manager.save()

            eval_results.sort(key=lambda x: x["perplexity"])

            tf.logging.info("=" * 200)
            log_str = "Best results | "
            for key, val in eval_results[0].items():
                log_str += "{} {} | ".format(key, val)
            tf.logging.info(log_str)
            tf.logging.info("=" * 200)

    else:
        if not FLAGS.do_eval:
            estimator.train(input_fn=train_input_fn, steps=FLAGS.train_steps)
        else:
            for step in range(0, FLAGS.train_steps, num_train_batch):
                train_steps = min(FLAGS.train_steps - step, num_train_batch)
                estimator.train(input_fn=train_input_fn, steps=train_steps)
                estimator.evaluate(input_fn=eval_input_fn, steps=num_eval_batch)

if __name__ == "__main__":
    tf.app.run()


import os
import sys
import numpy as np
import soundfile
import librosa
import h5py
import math
import pandas as pd
from sklearn import metrics
import logging
import matplotlib.pyplot as plt

import config

def create_folder(fd):
    if not os.path.exists(fd):
        os.makedirs(fd)

def get_filename(path):
    path = os.path.realpath(path)
    name_ext = path.split('/')[-1]
    name = os.path.splitext(name_ext)[0]
    return name

def create_logging(log_dir, filemode):
    create_folder(log_dir)
    i1 = 0

    while os.path.isfile(os.path.join(log_dir, '{:04d}.log'.format(i1))):
        i1 += 1

    log_path = os.path.join(log_dir, '{:04d}.log'.format(i1))
    logging.basicConfig(
        level=logging.DEBUG,
        format='%(asctime)s %(filename)s[line:%(lineno)d] %(levelname)s %(message)s',
        datefmt='%a, %d %b %Y %H:%M:%S',
        filename=log_path,
        filemode=filemode)

    # Print to console
    console = logging.StreamHandler()
    console.setLevel(logging.INFO)
    formatter = logging.Formatter('%(name)-12s: %(levelname)-8s %(message)s')
    console.setFormatter(formatter)
    logging.getLogger('').addHandler(console)

    return logging

def read_audio(audio_path, target_fs=None):
    (audio, fs) = soundfile.read(audio_path)

    if audio.ndim > 1:
        audio = np.mean(audio, axis=1)

    if target_fs is not None and fs != target_fs:
        audio = librosa.resample(audio, orig_sr=fs, target_sr=target_fs)
        fs = target_fs

    return audio, fs

def calculate_scalar_of_tensor(x):
    if x.ndim == 2:
        axis = 0
    elif x.ndim == 3:
        axis = (0, 1)

    mean = np.mean(x, axis=axis)
    std = np.std(x, axis=axis)

    return mean, std

def load_scalar(scalar_path):
    with h5py.File(scalar_path, 'r') as hf:
        mean = hf['mean'][:]
        std = hf['std'][:]

    scalar = {'mean': mean, 'std': std}
    return scalar

def scale(x, mean, std):
    return (x - mean) / std

def inverse_scale(x, mean, std):
    return x * std + mean

def read_metadata(metadata_path, lb_to_idx):
    '''Read metadata from a csv file.

    Returns:
      meta_dict: {'audio_name': (audios_num,),
                  'target': (audios_num, classes_num)}
    '''
    df = pd.read_csv(metadata_path, sep=',')

    audios_num = len(df)
    classes_num = len(lb_to_idx)

    target = np.zeros((audios_num, classes_num), dtype=np.bool)

    for n, labels in enumerate(df['labels']):
        for label in labels.split(','):
            idx = lb_to_idx[label]
            target[n, idx] = 1

    meta_dict = {}
    meta_dict['audio_name'] = np.array(df['fname'].tolist())
    meta_dict['target'] = target

    return meta_dict

def segment_prediction_to_clip_prediction(output_dict, average):
    '''Aggregate segment-level predictions to clip-level prediction.

    Args:
      output_dict: {'audio_name': (segments_num,),
                    'output': (segments_num, classes_num),
                    (if exist) 'target': (segments_num,)}
      average: 'arithmetic' | 'geometric'

    Returns:
      result_dict: {'audio_name': (audios_num,),
                    'output': (audios_num, classes_num),
                    'target': (audios_num, classes_num)}
    '''

    assert average in ['arithmetic', 'geometric']

    audio_names = np.array(sorted(set(output_dict['audio_name'])))
    outputs = []
    segment_output_dict = {}

    has_target = 'target' in output_dict.keys()

    if has_target:
        targets = []

    '''segment_output_dict: {'a.wav': [(classes_num,), ...]
                             'b.wav': [(classes_num,), (classes_num,), ...]
                             ...}'''
    for n, audio_name in enumerate(output_dict['audio_name']):
        if audio_name not in segment_output_dict.keys():
            segment_output_dict[audio_name] = [output_dict['output'][n]]
            if has_target:
                targets.append(output_dict['target'][n])
        else:
            segment_output_dict[audio_name].append(output_dict['output'][n])

    # Aggregate results in segment_output_dict
    for audio_name in audio_names:
        segment_outputs = np.array(segment_output_dict[audio_name])
        if average == 'arithmetic':
            output = np.mean(segment_outputs, axis=0)
        elif average == 'geometric':
            output = np.exp(np.mean(np.log(segment_outputs), axis=0))
        outputs.append(output)

    outputs = np.array(outputs)

    result_dict = {
        'audio_name': audio_names,
        'output': outputs}

    if has_target:
        targets = np.array(targets)
        result_dict['target'] = targets

    return result_dict

def write_submission(result_dict, submission_path):

    labels = config.labels

    fw = open(submission_path, 'w')

    fw.write('fname')
    for label in labels:
        fw.write(',{}'.format(label))
    fw.write('\n')

    for n, audio_name in enumerate(result_dict['audio_name']):
        fw.write(audio_name)
        for prob in result_dict['output'][n]:
            fw.write(',{}'.format(prob))
        fw.write('\n')
    fw.close()
    print('Write submission to {}'.format(submission_path))


import isolearn.keras as iso

import numpy as np

import pandas as pd

import scipy.sparse as sp
import scipy.io as spio

import matplotlib.pyplot as plt

import seaborn as sns

def plot_logo_w_hexamer_scores(pwm, iso_pred, cut_pred, hexamer_scores, cse_start_pos=70, annotate_peaks=None, sequence_template=None, figsize=(12, 3), width_ratios=[1, 7], logo_height=1.0, usage_unit='log', plot_start=0, plot_end=164, save_figs=False, fig_name=None, fig_dpi=300) :

    n_samples = pwm.shape[0]

    #Slice according to seq trim index
    pwm = pwm[:, plot_start: plot_end, :]
    cut_pred = cut_pred[:, plot_start: plot_end]
    sequence_template = sequence_template[plot_start: plot_end]

    iso_pred = np.mean(iso_pred, axis=0)
    cut_pred = np.mean(cut_pred, axis=0)
    pwm = np.sum(pwm, axis=0)

    pwm += 0.0001
    for j in range(0, pwm.shape[0]) :
        pwm[j, :] /= np.sum(pwm[j, :])

    entropy = np.zeros(pwm.shape)
    entropy[pwm > 0] = pwm[pwm > 0] * -np.log2(pwm[pwm > 0])
    entropy = np.sum(entropy, axis=1)
    conservation = 2 - entropy

    fig = plt.figure(figsize=figsize)

    gs = gridspec.GridSpec(2, 2, width_ratios=[width_ratios[0], width_ratios[-1]], height_ratios=[1, 1])

    ax0 = plt.subplot(gs[0, 0])
    ax1 = plt.subplot(gs[0, 1])
    ax2 = plt.subplot(gs[1, 0])
    ax3 = plt.subplot(gs[1, 1])

    plt.sca(ax0)
    plt.axis('off')
    plt.sca(ax2)
    plt.axis('off')

    annot_text = 'Samples = ' + str(int(n_samples))
    if usage_unit == 'log' :
        annot_text += '\nUsage = ' + str(round(np.log(iso_pred[0] / (1. - iso_pred[0])), 4))
    else :
        annot_text += '\nUsage = ' + str(round(iso_pred[0], 4))

    ax2.text(0.99, 0.5, annot_text, horizontalalignment='right', verticalalignment='center', transform=ax2.transAxes, color='black', fontsize=12, weight="bold")

    l2, = ax1.plot(np.arange(plot_end - plot_start), cut_pred, linewidth=3, linestyle='-', label='Predicted', color='red', alpha=0.7)

    if annotate_peaks is not None :
        objective_pos = 0
        if annotate_peaks == 'max' :
            objective_pos = np.argmax(cut_pred)
        else :
            objective_pos = annotate_peaks - plot_start

        text_x, text_y, ha = -30, -5, 'right'
        if objective_pos < 30 :
            text_x, text_y, ha = 30, -5, 'left'

        annot_text = '(CSE+' + str(objective_pos + plot_start - (cse_start_pos + 6) + 0) + ') ' + str(int(round(cut_pred[objective_pos] * 100, 0))) + '% Cleavage'
        ax1.annotate(annot_text, xy=(objective_pos, cut_pred[objective_pos]), xycoords='data', xytext=(text_x, text_y), ha=ha, fontsize=10, weight="bold", color='black', textcoords='offset points', arrowprops=dict(connectionstyle="arc3,rad=-.1", headlength=8, headwidth=8, shrink=0.15, width=1.5, color='black'))

    plt.sca(ax1)

    plt.xlim((0, plot_end - plot_start))
    #plt.ylim((0, 2))
    plt.xticks([], [])
    plt.yticks([], [])
    plt.legend(handles=[l2], fontsize=12, prop=dict(weight='bold'), frameon=False)
    plt.axis('off')

    height_base = (1.0 - logo_height) / 2.

    for j in range(0, pwm.shape[0]) :
        sort_index = np.argsort(pwm[j, :])

        for ii in range(0, 4) :
            i = sort_index[ii]

            nt_prob = pwm[j, i] * conservation[j]

            nt = ''
            if i == 0 :
                nt = 'A'
            elif i == 1 :
                nt = 'C'
            elif i == 2 :
                nt = 'G'
            elif i == 3 :
                nt = 'T'

            color = None
            if sequence_template[j] != 'N' :
                color = 'black'

            if ii == 0 :
                letterAt(nt, j + 0.5, height_base, nt_prob * logo_height, ax3, color=color)
            else :
                prev_prob = np.sum(pwm[j, sort_index[:ii]] * conservation[j]) * logo_height
                letterAt(nt, j + 0.5, height_base + prev_prob, nt_prob * logo_height, ax3, color=color)

            text_x, text_y, ha = -30, -5, 'right'
            if objective_pos < 30 :
                text_x, text_y, ha = 30, -5, 'left'

            if j < len(hexamer_scores) :
                #annot_text = str(hexamer_scores[j][0]) + " = " + str(round(hexamer_scores[j][1], 2))

                annot_text = str(hexamer_scores[j][1])
                ax3.text(j + 0.25, 2.0, annot_text, size=50, rotation=90., ha="left", va="bottom", fontsize=8, weight="bold", color='black')

    plt.sca(ax3)

    plt.xlim((0, plot_end - plot_start))
    plt.ylim((0, 2.5))
    plt.xticks([], [])
    plt.yticks([], [])
    plt.axis('off')
    ax3.axhline(y=0.01 + height_base, color='black', linestyle='-', linewidth=2)

    for axis in fig.axes :
        axis.get_xaxis().set_visible(False)
        axis.get_yaxis().set_visible(False)

    plt.tight_layout()

    if save_figs :
        plt.savefig(fig_name + '.png', transparent=True, dpi=fig_dpi)
        plt.savefig(fig_name + '.svg')
        plt.savefig(fig_name + '.eps')

    plt.show()
    #plt.close()

mer6_dict = {}
i = 0
for b1 in ['A', 'C', 'G', 'T'] :
    for b2 in ['A', 'C', 'G', 'T'] :
        for b3 in ['A', 'C', 'G', 'T'] :
            for b4 in ['A', 'C', 'G', 'T'] :
                for b5 in ['A', 'C', 'G', 'T'] :
                    for b6 in ['A', 'C', 'G', 'T'] :
                        mer6_dict[b1 + b2 + b3 + b4 + b5 + b6] = i
                        i += 1

def get_hexamer_pred_both_regions(seq) :
    preds = []
    for cell_line, w, w_0 in zip(['hek', 'hela', 'mcf7', 'cho'], [hek_both_regions_w, hela_both_regions_w, mcf7_both_regions_w, cho_both_regions_w], [hek_both_regions_w_0, hela_both_regions_w_0, mcf7_both_regions_w_0, cho_both_regions_w_0]) :
        pred = w_0

        region_1 = seq[5: 40]
        for j in range(0, len(region_1) - 5) :
            pred += w[mer6_dict[region_1[j: j+6]]]

        region_2 = seq[48: 83]
        for j in range(0, len(region_2) - 5) :
            pred += w[4096 + mer6_dict[region_2[j: j+6]]]

        preds.append(1. / (1. + np.exp(-pred)))

    return np.array(preds)

def get_hexamer_diff_scores_both_regions(seq, cell_1, cell_2) :
    scores = []

    w_dict = {
        'hek' : hek_both_regions_w,
        'hela' : hela_both_regions_w,
        'mcf7' : mcf7_both_regions_w,
        'cho' : cho_both_regions_w
    }

    w_cell_1 = w_dict[cell_1]
    w_cell_2 = w_dict[cell_2]

    scores.extend([('_', ''), ('_', ''), ('_', ''), ('_', ''), ('_', '')])

    region_1 = seq[5: 40]
    for j in range(0, len(region_1) - 5) :
        hexamer_score_cell_1 = w_cell_1[mer6_dict[region_1[j: j+6]]]
        hexamer_score_cell_2 = w_cell_2[mer6_dict[region_1[j: j+6]]]

        scores.append((region_1[j: j+6], str(round(hexamer_score_cell_1 - hexamer_score_cell_2, 2))))

    scores.extend([('_', ''), ('_', ''), ('_', ''), ('_', ''), ('_', ''), ('_', ''), ('_', ''), ('_', ''), ('_', ''), ('_', ''), ('_', ''), ('_', ''), ('_', '')])

    region_2 = seq[48: 83]
    for j in range(0, len(region_2) - 5) :
        hexamer_score_cell_1 = w_cell_1[4096 + mer6_dict[region_2[j: j+6]]]
        hexamer_score_cell_2 = w_cell_2[4096 + mer6_dict[region_2[j: j+6]]]

        scores.append((region_2[j: j+6], str(round(hexamer_score_cell_1 - hexamer_score_cell_2, 2))))

    return scores

def get_hexamer_preds_both_regions(seqs) :
    preds = np.zeros((len(seqs), 4))

    for i, seq in enumerate(seqs) :
        preds[i, :] = get_hexamer_pred_both_regions(seq)

    return preds

def get_hexamer_pred(seq) :
    preds = []
    for cell_line, w, w_0 in zip(['hek', 'hela', 'mcf7', 'cho'], [hek_w, hela_w, mcf7_w, cho_w], [hek_w_0, hela_w_0, mcf7_w_0, cho_w_0]) :
        pred = w_0

        region_1 = seq[5: 40]
        for j in range(0, len(region_1) - 5) :
            pred += w[mer6_dict[region_1[j: j+6]]]

        preds.append(1. / (1. + np.exp(-pred)))

    return np.array(preds)

def get_hexamer_diff_scores(seq, cell_1, cell_2) :
    scores = []

    w_dict = {
        'hek' : hek_w,
        'hela' : hela_w,
        'mcf7' : mcf7_w,
        'cho' : cho_w
    }

    w_cell_1 = w_dict[cell_1]
    w_cell_2 = w_dict[cell_2]

    scores.extend([('_', ''), ('_', ''), ('_', ''), ('_', ''), ('_', '')])
    region_1 = seq[5: 40]
    for j in range(0, len(region_1) - 5) :
        hexamer_score_cell_1 = w_cell_1[mer6_dict[region_1[j: j+6]]]
        hexamer_score_cell_2 = w_cell_2[mer6_dict[region_1[j: j+6]]]

        scores.append((region_1[j: j+6], str(round(hexamer_score_cell_1 - hexamer_score_cell_2, 2))))

    return scores

def get_hexamer_preds(seqs) :
    preds = np.zeros((len(seqs), 4))

    for i, seq in enumerate(seqs) :
        preds[i, :] = get_hexamer_pred(seq)

    return preds

def decode_onehot_consensus(onehot) :
    seq = ''

    for i in range(onehot.shape[0]) :
        max_j = np.argmax(onehot[i, :])

        if max_j == 0 :
            seq += 'A'
        elif max_j == 1 :
            seq += 'C'
        elif max_j == 2 :
            seq += 'G'
        elif max_j == 3 :
            seq += 'T'

    return seq

def decode_onehots_consensus(onehots) :
    seqs = [
        decode_onehot_consensus(onehots[i, :, :, 0]) for i in range(onehots.shape[0])
    ]

    return seqs


##################### generated by xml-casa (v2) from pimfit.xml ####################
##################### d7d637360366fa9a556c7f929f11bf8d ##############################
from __future__ import absolute_import
from casashell.private.stack_manip import find_local as __sf__
from casashell.private.stack_manip import find_frame as _find_frame
from casatools.typecheck import validator as _pc
from casatools.coercetype import coerce as _coerce
from suncasatasks import pimfit as _pimfit_t
from collections import OrderedDict
import numpy
import sys
import os

import shutil

def static_var(varname, value):
    def decorate(func):
        setattr(func, varname, value)
        return func
    return decorate

class _pimfit:
    """
    pimfit ---- Fit one or more elliptical Gaussian components on an image region(s)

    --------- parameter descriptions ---------------------------------------------

    imagefiles   A list of the input images
    ncpu         Number of cpu cores to use
    doreg        True if use vla_prep to register the image
    ephemfile    emphemeris file generated from vla_prep.read_horizons()
    timestamps   A list of timestamps of the input images
    msinfofile   time-dependent phase center information generated from vla_prep.read_msinfo()
    box          Rectangular region(s) to select in direction plane. See "help par.box" for details. Default is to use the entire direction plane.
    region       Region selection. See "help par.region" for details. Default is to use the full image.
    chans        Channels to use. See "help par.chans" for details. Default is to use all channels.
    stokes       Stokes planes to use. See "help par.stokes" for details. Default is to use first Stokes plane.
    mask         Mask to use. See help par.mask. Default is none.
    includepix   Range of pixel values to include for fitting.
    excludepix   Range of pixel values to exclude for fitting.
    residual     Name of output residual image.
    model        Name of output model image.
    estimates    Name of file containing initial estimates of component parameters.
    logfile      Name of file to write fit results.
    append       If logfile exists, append to it if True or overwrite it if False
    newestimates File to write fit results which can be used as initial estimates for next run.
    complist     Name of output component list table.
    overwrite    Overwrite component list table if it exists?
    dooff        Also fit a zero level offset? Default is False
    offset       Initial estimate of zero-level offset. Only used if doff is True. Default is 0.0
    fixoffset    Keep the zero level offset fixed during fit? Default is False
    stretch      Stretch the mask if necessary and possible? See help par.stretch
    rms          RMS to use in calculation of uncertainties. Numeric or valid quantity (record or string). If numeric, it is given units of the input image. If quantity, units must conform to image units. If not positive, the rms of the residual image, in the region of the fit, is used.
    noisefwhm    Noise correlation beam FWHM. If numeric value, interpreted as pixel widths. If quantity (dictionary, string), it must have angular units.
    summary      File name to which to write table of fit parameters.
    RETURNS         void

    --------- examples -----------------------------------------------------------

    PARAMETER SUMMARY
    imagename        Name of the input image
    box              Rectangular region(s) to select in direction plane. See "help par.box"
    for details. Default is to use the entire direction plane.
    eg "100, 120, 200, 220, 300, 300, 400, 400" to use two boxes.
    region           Region selection. See "help par.region" for details. Default is to use
    the full image.
    chans            Channels to use. See "help par.chans" for details. Default is to use all
    channels.
    stokes           Stokes planes to use. See "help par.stokes" for details. Default is to
    use first Stokes plane.
    mask             Mask to use. See help par.mask. Default is none.
    includepix       Range of pixel values to include for fitting. Array of two numeric
    values assumed to have same units as image pixel values. Only one
    of includepix or excludepix can be specified.
    excludepix       Range of pixel values to exclude for fitting. Array of two numeric
    values assumed to have same units as image pixel values. Only one
    of includepix or excludepix can be specified.
    residual         Name of the residual image to write.
    model            Name of the model image to write.
    estimates        Name of file containing initial estimates of component parameters
    (see below for formatting details).
    logfile          Name of file to write fit results.
    append           If logfile exists, append to it (True) or overwrite it (False).
    newestimates     File to write fit results which can be used as initial estimates
    for next run.
    complist         Name of output component list table.
    overwrite        Overwrite component list table if it exists?
    dooff            Simultaneously fit a zero-level offset?
    offset           Initial estimate for the zero-level offset. Only used if dooff is True.
    fixoffset        Hold zero-level offset constant during fit? Only used if dooff is True.
    stretch          Stretch the input mask if necessary and possible. Only used if a mask is specified.
    See help par.stretch.
    rms              RMS to use in calculation of various uncertainties, assumed to have units of the input
    image. If not positve, the rms of the residual image is used.
    noisefwhm        Noise correlation beam FWHM. If numeric value, interpreted as pixel widths. If
    quantity (dictionary, string), it must have angular units.
    summary          File name to which to write table of fit parameters.

    OVERVIEW
    This application is used to fit one or more two dimensional gaussians to sources in an image as
    well as an optional zero-level offset. Fitting is limited to a single polarization
    but can be performed over several contiguous spectral channels.
    If the image has a clean beam, the report and returned dictionary will contain both the convolved
    and the deconvolved fit results.

    When dooff is False, the method returns a dictionary with three keys, 'converged', 'results',
    and 'deconvolved'. The value of 'converged' is a boolean array which indicates if the fit
    converged on a channel by channel basis. The value of 'results' is a dictionary representing
    a component list reflecting the fit results. In the case of an image containing beam information,
    the sizes and position angles in the 'results' dictionary are those of the source(s) convolved
    with the restoring beam, while the same parameters in the 'deconvolved' dictionary represent the
    source sizes deconvolved from the beam. In the case where the image does not contain a beam,
    'deconvolved' will be absent. Both the 'results' and 'deconvolved' dictionaries can
    be read into a component list tool (default tool is named cl) using the fromrecord() method
    for easier inspection using tool methods, eg

    cl.fromrecord(res['results'])

    although this currently only works if the flux density units are conformant with Jy.

    There are also values in each component subdictionary not used by cl.fromrecord() but meant to
    supply additional information. There is a 'peak' subdictionary for each component that provides the
    peak intensity of the component. It is present for both 'results' and 'deconvolved' components.
    There is also a 'sum' subdictionary for each component indicated the simple sum of pixel values in
    the the original image enclosed by the fitted ellipse. There is a 'channel' entry in the 'spectrum'
    subdictionary which provides the zero-based channel number in the input image for which the solution
    applies. In addtion, if the image has a beam(s), then there will be a 'beam' subdictionary associated
    with each component in both the 'results' and 'deconvolved' dictionaries. This subdictionary will
    have three keys: 'beamarcsec' will be a subdictionary giving the beam dimensions in arcsec,
    'beampixels' will have the value of the beam area expressed in pixels, and 'beamster' will have the
    value of the beam area epressed in steradians. Also, if the image has a beam(s), in the component level
    dictionaries will be an 'ispoint' entry with an associated boolean value describing if the component
    is consistent with a point source.

    If dooff is True, in addtion to the specified number of
    gaussians, a zero-level offset will also be fit. The initial estimate for this
    offset is specified using the offset parameter. Units are assumed to be the
    same as the image brightness units. The zero level offset can be held constant during
    the fit by specifying fixoffset=True. In the case of dooff=True, the returned
    dictionary contains two additional keys, 'zerooff' and 'zeroofferr', which are both
    dictionaries containing 'unit' and 'value' keys. The values associated with the 'value'
    keys are arrays containing the the fitted zero level offset value and its error, respectively,
    for each channel. In cases where the fit did not converge, these values are set to NaN.
    The value associated with 'unit' is just the i`mage brightness unit.

    The region can either be specified by a box(es) or a region.
    Ranges of pixel values can be included or excluded from the fit. If specified using
    the box parameter, multiple boxes can be given using the format
    box="blcx1, blcy1, trcx1, trcy1, blcx2, blcy2, trcx2, trcy2, ... , blcxN, blcyN, trcxN, trcyN"
    where N is the number of boxes. In this case, the union of the specified boxes will be used.

    If specified, the residual and/or model images for successful fits will be written.

    If an estimates file is not specified, an attempt is made to estimate
    initial parameters and fit a single Gaussian. If a multiple Gaussian fit
    is desired, the user must specify initial estimates via a text file
    (see below for details).

    The user has the option of writing the result of the fit to a log file,
    and has the option of either appending to or overwriting an existing file.

    The user has the option of writing the (convolved) parameters of a successful
    fit to a file which can be fed back to fitcomponents() as the estimates file for a
    subsequent run.

    The user has the option of writing the fit results in tabular format to a file whose
    name is specified using the summary parameter.

    If specified and positive, the value of rms is used to calculate the parameter uncertainties,
    otherwise, the rms in the selected region in the relevant channel is used for these calculations.

    The noisefwhm parameter represents the noise-correlation beam FWHM. If specified as a quantity,
    it should have angular units. If specified as a numerical value, it is set equal to that number
    of pixels. If specified and greater than or equal to the pixel size, it is used to calculate
    parameter uncertainties using the correlated noise equations (see below). If it is specified but
    less than a pixel width, the the uncorrelated noise equations (see below) are used to
    compute the parameter uncertainties. If it is not specified and the image has a restoring beam(s),
    the the correlated noise equations are used to compute parameter uncertainties using the
    geometric mean of the relevant beam major and minor axes as the noise-correlation beam FWHM. If
    noisefwhm is not specified and the image does not have a restoring beam, then the uncorrelated
    noise equations are used to compute the parameter uncertainties.

    SUPPORTED UNITS

    Currently only images with brightness units conformant with Jy/beam, Jy.km/s/beam, and K are fully
    supported for fitting. If your image has some other base brightness unit, that unit will be assumed
    to be equivalent to Jy/pixel and results will be calculated accordingly. In particular,
    the flux density (reported as Integrated Flux in the logger and associated with the "flux" key
    in the returned component subdictionary(ies)) for such a case represents the sum of pixel values.

    Note also that converting the returned results subdictionary to a component list via cl.fromrecord() currently
    only works properly if the flux density units in the results dictionary are conformant with Jy.
    If you need to be able to run cl.fromrecord() on the resulting dictionary you can first modify the
    flux density units by hand to be (some prefix)Jy and then run cl.fromrecord() on that dictionary,
    bearing in mind your unit conversion.

    If the input image has units of K, the flux density of components will be reported in units
    of [prefix]K*rad*rad, where prefix is an SI prefix used so that the numerical value is between
    1 and 1000. To convert to units of K*beam, determine the area of the appropriate beam,
    which is given by pi/(4*ln(2))*bmaj*bmin, where bmaj and bmin are the major and minor axes
    of the beam, and convert to steradians (=rad*rad). This value is included in the beam portion
    of the component subdictionary (key 'beamster'). Then divide the numerical value of the
    logged flux density by the beam area in steradians. So, for example

    begin{verbatim}
    # run on an image with K brightness units
    res = imfit(...)
    # get the I flux density in K*beam of component 0
    comp = res['results']['component0']
    flux_density_kbeam = comp['flux']['value'][0]/comp['beam']['beamster']
    end{verbatim}

    FITTING OVER MULTIPLE CHANNELS

    For fitting over multiple channels, the result of the previous successful fit is used as
    the estimate for the next channel. The number of gaussians fit cannot be varied on a channel
    by channel basis. Thus the variation of source structure should be reasonably smooth in
    frequency to produce reliable fit results.

    MASK SPECIFICATION

    Mask specification can be done using an LEL expression. For example

    mask = '"myimage">5' will use only pixels with values greater than 5.

    INCLUDING AND EXCLUDING PIXELS

    Pixels can be included or excluded from the fit based on their values
    using these parameters. Note that specifying both is not permitted and
    will cause an error. If specified, both take an array of two numeric
    values.

    ESTIMATES

    Initial estimates of fit parameters may be specified via an estimates
    text file. Each line of this file should contain a set of parameters for
    a single gaussian. Optionally, some of these parameters can be fixed during
    the fit. The format of each line is

    peak intensity, peak x-pixel value, peak y-pixel value, major axis, minor axis, position angle, fixed

    The fixed parameter is optional. The peak intensity is assumed to be in the
    same units as the image pixel values (eg Jy/beam). The peak coordinates are specified
    in pixel coordinates. The major and minor axes and the position angle are the convolved
    parameters if the image has been convolved with a clean beam and are specified as quantities.
    The fixed parameter is optional and is a string. It may contain any combination of the
    following characters 'f' (peak intensity), 'x' (peak x position), 'y' (peak y position),
    'a' (major axis), 'b' (minor axis), 'p' (position angle).

    In addition, lines in the file starting with a # are considered comments.

    An example of such a file is:

    begin{verbatim}
    # peak intensity must be in map units
    120, 150, 110, 23.5arcsec, 18.9arcsec, 120deg
    90, 60, 200, 46arcsec, 23arcsec, 140deg, fxp
    end{verbatim}

    This is a file which specifies that two gaussians are to be simultaneously fit,
    and for the second gaussian the specified peak intensity, x position, and position angle
    are to be held fixed during the fit.

    ERROR ESTIMATES

    Error estimates are based on the work of Condon 1997, PASP, 109, 166. Key assumptions made are:
    * The given model (elliptical Gaussian, or elliptical Gaussian plus constant offset) is an
    adequate representation of the data
    * An accurate estimate of the pixel noise is provided or can be derived (see above). For the
    case of correlated noise (e.g., a CLEAN map), the fit region should contain many "beams" or
    an independent value of rms should be provided.
    * The signal-to-noise ratio (SNR) or the Gaussian component is large. This is necessary because
    a Taylor series is used to linearize the problem. Condon (1997) states that the fractional
    bias in the fitted amplitude due to this assumption is of order 1/(S*S), where S is the overall
    SNR of the Gaussian with respect to the given data set (defined more precisely below). For a 5
    sigma "detection" of the Gaussian, this is a 4% effect.
    * All (or practically all) of the flux in the component being fit falls within the selected region.
    If a constant offset term is simultaneously fit and not fixed, the region of interest should be
    even larger. The derivations of the expressions summarized in this note assume an effectively
    infinite region.

    Two sets of equations are used to calculate the parameter uncertainties, based on if
    the noise is correlated or uncorrelated. The rules governing which set of equations are
    used have been described above in the description of the noisefwhm parameter.

    In the case of uncorrelated noise, the equations used are

    f(A) = f(I) = f(M) = f(m) = k*s(x)/M = k*s(y)/m = (s(p)/sqrt(2))*((M*M - m*m)/(M*m))
    = sqrt(2)/S

    where s(z) is the uncertainty associated with parameter z, f(z) = s(z)/abs(z) is the
    fractional uncertainty associated with parameter z, A is the peak intensity, I is the flux
    density, M  and m are the FWHM major and minor axes, p is the position angle of the
    component, and k = sqrt(8*ln(2)). s(x) and s(y) are the direction
    uncertainties of the component measured along the major and minor axes; the resulting
    uncertainties measured along the principle axes of the image direction coordinate are
    calculated by propagation of errors using the 2D rotation matrix which enacts the rotation through
    the position angle plus 90 degrees. S is the overall signal to noise ratio of the component,
    which, for the uncorrelated noise case is given by

    S = (A/(k*h*r))*sqrt(pi*M*m)

    where h is the pixel width of the direction coordinate and r is the rms noise (see the
    discussion above for the rules governing how the value of r is determined).

    For the correlated noise case, the same equations are used to determine the uncertainties
    as in the uncorrelated noise case, except for the uncertainty in I (see below). However,
    S is given by

    S = (A/(2*r*N)) * sqrt(M*m) * (1 + ((N*N/(M*M)))**(a/2)) * (1 + ((N*N/(m*m)))**(b/2))

    where N is the noise-correlation beam FWHM (see discussion of the noisefwhm parameter for
    rules governing how this value is determined). "**" indicates exponentiation and a and b
    depend on which uncertainty is being calculated. For sigma(A), a = b = 3/2. For M and x,
    a = 5/2 and b = 1/2. For m, y, and p, a = 1/2 and b = 5/2. f(I) is calculated in the
    correlated noise case according to

    f(I) = sqrt( f(A)*f(A) + (N*N/(M*m))*(f(M*f(M) + f(m)*f(m))) )

    Note well the following caveats:
    * Fixing Gaussian component parameters will tend to cause the parameter uncertainties reported for free
    parameters to be overestimated.
    * Fitting a zero level offset that is not fixed will tend to cause the reported parameter
    uncertainties to be slightly underestimated.
    * The parameter uncertainties will be inaccurate at low SNR (a ~10% for SNR = 3).
    * If the fitted region is not considerably larger than the largest component that is fit,
    parameter uncertainties may be mis-estimated.
    * An accurate rms noise measurement, r, for the region in question must be supplied.
    Alternatively, a sufficiently large signal-free region must be present in the selected region
    (at least about 25 noise beams in area) to auto-derive such an estimate.
    * If the image noise is not statistically independent from pixel to pixel, a reasonably accurate noise
    correlation scale, N, must be provided. If the noise correlation function is not approximately Gaussian,
    the correlation length can be estimated using

    N = sqrt(2*ln(2)/pi)* double-integral(dx dy C(x,y))/sqrt(double-integral(dx dy C(x, y) * C(x,y)))

    where C(x,y) is the associated noise-smoothing function
    * If fitted model components have significan spatial overlap, the parameter uncertainties are likely to
    be mis-estimated (i.e., correlations between the parameters of separate components are not accounted
    for).
    * If the image being analyzed is an interferometric image with poor uv sampling, the parameter
    uncertainties may be significantly underestimated.

    The deconvolved size and position angle errors are computed by taking the maximum of the absolute values of the
    differences of the best fit deconvolved value of the given parameter and the deconvolved size of the eight
    possible combinations of (FWHM major axis +/- major axis error), (FWHM minor axis +/- minor axis error),
    and (position andle +/- position angle error). If the source cannot be deconvolved from the beam (if the best
    fit convolved source size cannot be deconvolved from the beam), upper limits on the deconvolved source size
    are sometimes reported. These limits simply come from the maximum major and minor axes of the deconvolved
    gaussians taken from trying all eight of the aforementioned combinations. In the case none of these combinations
    produces a deconvolved size, no upper limit is reported.

    EXAMPLE:

    Here is how one might fit two gaussians to multiple channels of a cube using the fit
    from the previous channel as the initial estimate for the next. It also illustrates
    how one can specify a region in the associated continuum image as the region to use
    as the fit for the channel.

    begin{verbatim}
    default imfit
    imagename = "co_cube.im"
    # specify region using region from continuum
    region = "continuum.im:source.rgn"
    chans = "2~20"
    # only use pixels with positive values in the fit
    excludepix = [-1e10,0]
    # estimates file contains initial parameters for two Gaussians in channel 2
    estimates = "initial_estimates.txt"
    logfile = "co_fit.log"
    # append results to the log file for all the channels
    append = "True"
    imfit()

    """

    _info_group_ = """analysis"""
    _info_desc_ = """Fit one or more elliptical Gaussian components on an image region(s)"""

    __schema = {'imagefiles': {'type': 'cReqPathVec', 'coerce': [_coerce.to_list,_coerce.expand_pathvec]}, 'ncpu': {'type': 'cInt'}, 'doreg': {'type': 'cBool'}, 'ephemfile': {'type': 'cStr', 'coerce': _coerce.to_str}, 'timestamps': {'type': 'cStrVec', 'coerce': [_coerce.to_list,_coerce.to_strvec]}, 'msinfofile': {'type': 'cStr', 'coerce': _coerce.to_str}, 'box': {'type': 'cStr', 'coerce': _coerce.to_str}, 'region': {'type': 'cVariant', 'coerce': [_coerce.to_variant]}, 'chans': {'type': 'cVariant', 'coerce': [_coerce.to_variant]}, 'stokes': {'type': 'cStr', 'coerce': _coerce.to_str}, 'mask': {'type': 'cStr', 'coerce': _coerce.to_str}, 'includepix': {'type': 'cIntVec', 'coerce': [_coerce.to_list,_coerce.to_intvec]}, 'excludepix': {'type': 'cIntVec', 'coerce': [_coerce.to_list,_coerce.to_intvec]}, 'residual': {'type': 'cStr', 'coerce': _coerce.to_str}, 'model': {'type': 'cStr', 'coerce': _coerce.to_str}, 'estimates': {'type': 'cStr', 'coerce': _coerce.to_str}, 'logfile': {'type': 'cStr', 'coerce': _coerce.to_str}, 'append': {'type': 'cBool'}, 'newestimates': {'type': 'cStr', 'coerce': _coerce.to_str}, 'complist': {'type': 'cStr', 'coerce': _coerce.to_str}, 'overwrite': {'type': 'cBool'}, 'dooff': {'type': 'cBool'}, 'offset': {'type': 'cFloat', 'coerce': _coerce.to_float}, 'fixoffset': {'type': 'cBool'}, 'stretch': {'type': 'cBool'}, 'rms': {'anyof': [{'type': 'cInt'}, {'type': 'cFloat', 'coerce': _coerce.to_float}, {'type': 'cDict'}, {'type': 'cStr', 'coerce': _coerce.to_str}]}, 'noisefwhm': {'anyof': [{'type': 'cInt'}, {'type': 'cFloat', 'coerce': _coerce.to_float}, {'type': 'cDict'}, {'type': 'cStr', 'coerce': _coerce.to_str}]}, 'summary': {'type': 'cStr', 'coerce': _coerce.to_str}}

    def __init__(self):
        self.__stdout = None
        self.__stderr = None
        self.__root_frame_ = None

    def __globals_(self):
        if self.__root_frame_ is None:
            self.__root_frame_ = _find_frame( )
            assert self.__root_frame_ is not None, "could not find CASAshell global frame"
        return self.__root_frame_

    def __to_string_(self,value):
        if type(value) is str:
            return "'%s'" % value
        else:
            return str(value)

    def __validate_(self,doc,schema):
        return _pc.validate(doc,schema)

    def __do_inp_output(self,param_prefix,description_str,formatting_chars):
        out = self.__stdout or sys.stdout
        description = description_str.split( )
        prefix_width = 23 + 13 + 4
        output = [ ]
        addon = ''
        first_addon = True
        while len(description) > 0:
            ## starting a new line.....................................................................
            if len(output) == 0:
                ## for first line add parameter information............................................
                if len(param_prefix)-formatting_chars > prefix_width - 1:
                    output.append(param_prefix)
                    continue
                addon = param_prefix + ' #'
                first_addon = True
                addon_formatting = formatting_chars
            else:
                ## for subsequent lines space over prefix width........................................
                addon = (' ' * prefix_width) + '#'
                first_addon = False
                addon_formatting = 0
            ## if first word of description puts us over the screen width, bail........................
            if len(addon + description[0]) - addon_formatting + 1 > self.term_width:
                ## if we're doing the first line make sure it's output.................................
                if first_addon: output.append(addon)
                break
            while len(description) > 0:
                ## if the next description word puts us over break for the next line...................
                if len(addon + description[0]) - addon_formatting + 1 > self.term_width: break
                addon = addon + ' ' + description[0]
                description.pop(0)
            output.append(addon)
        out.write('\n'.join(output) + '\n')

    #--------- return nonsubparam values ----------------------------------------------

    def __noisefwhm_dflt( self, glb ):
        return ''

    def __noisefwhm( self, glb ):
        if 'noisefwhm' in glb: return glb['noisefwhm']
        return ''

    def __includepix_dflt( self, glb ):
        return [  ]

    def __includepix( self, glb ):
        if 'includepix' in glb: return glb['includepix']
        return [  ]

    def __rms_dflt( self, glb ):
        return int(0)

    def __rms( self, glb ):
        if 'rms' in glb: return glb['rms']
        return int(0)

    def __estimates_dflt( self, glb ):
        return ''

    def __estimates( self, glb ):
        if 'estimates' in glb: return glb['estimates']
        return ''

    def __summary_dflt( self, glb ):
        return ''

    def __summary( self, glb ):
        if 'summary' in glb: return glb['summary']
        return ''

    def __newestimates_dflt( self, glb ):
        return ''

    def __newestimates( self, glb ):
        if 'newestimates' in glb: return glb['newestimates']
        return ''

    def __model_dflt( self, glb ):
        return ''

    def __model( self, glb ):
        if 'model' in glb: return glb['model']
        return ''

    def __logfile_dflt( self, glb ):
        return ''

    def __logfile( self, glb ):
        if 'logfile' in glb: return glb['logfile']
        return ''

    def __dooff_dflt( self, glb ):
        return False

    def __dooff( self, glb ):
        if 'dooff' in glb: return glb['dooff']
        return False

    def __mask_dflt( self, glb ):
        return ''

    def __mask( self, glb ):
        if 'mask' in glb: return glb['mask']
        return ''

    def __residual_dflt( self, glb ):
        return ''

    def __residual( self, glb ):
        if 'residual' in glb: return glb['residual']
        return ''

    def __ncpu_dflt( self, glb ):
        return int(8)

    def __ncpu( self, glb ):
        if 'ncpu' in glb: return glb['ncpu']
        return int(8)

    def __stokes_dflt( self, glb ):
        return ''

    def __stokes( self, glb ):
        if 'stokes' in glb: return glb['stokes']
        return ''

    def __region_dflt( self, glb ):
        return ''

    def __region( self, glb ):
        if 'region' in glb: return glb['region']
        return ''

    def __excludepix_dflt( self, glb ):
        return [  ]

    def __excludepix( self, glb ):
        if 'excludepix' in glb: return glb['excludepix']
        return [  ]

    def __chans_dflt( self, glb ):
        return ''

    def __chans( self, glb ):
        if 'chans' in glb: return glb['chans']
        return ''

    def __imagefiles_dflt( self, glb ):
        return [  ]

    def __imagefiles( self, glb ):
        if 'imagefiles' in glb: return glb['imagefiles']
        return [  ]

    def __complist_dflt( self, glb ):
        return ''

    def __complist( self, glb ):
        if 'complist' in glb: return glb['complist']
        return ''

    def __box_dflt( self, glb ):
        return ''

    def __box( self, glb ):
        if 'box' in glb: return glb['box']
        return ''

    def __doreg_dflt( self, glb ):
        return False

    def __doreg( self, glb ):
        if 'doreg' in glb: return glb['doreg']
        return False

    #--------- return inp/go default --------------------------------------------------
    def __timestamps_dflt( self, glb ):
        if self.__doreg( glb ) == bool(True): return []
        return None
    def __stretch_dflt( self, glb ):
        if self.__mask( glb ) != "": return bool(False)
        return None
    def __offset_dflt( self, glb ):
        if self.__dooff( glb ) != bool(False): return float(0.0)
        return None
    def __msinfofile_dflt( self, glb ):
        if self.__doreg( glb ) == bool(True): return ""
        return None
    def __ephemfile_dflt( self, glb ):
        if self.__doreg( glb ) == bool(True): return ""
        return None
    def __overwrite_dflt( self, glb ):
        if self.__complist( glb ) != "": return bool(False)
        return None
    def __fixoffset_dflt( self, glb ):
        if self.__dooff( glb ) != bool(False): return bool(False)
        return None
    def __append_dflt( self, glb ):
        if self.__logfile( glb ) != "": return bool(True)
        return None

    #--------- return subparam values -------------------------------------------------
    def __ephemfile( self, glb ):
        if 'ephemfile' in glb: return glb['ephemfile']
        dflt = self.__ephemfile_dflt( glb )
        if dflt is not None: return dflt
        return ''
    def __timestamps( self, glb ):
        if 'timestamps' in glb: return glb['timestamps']
        dflt = self.__timestamps_dflt( glb )
        if dflt is not None: return dflt
        return [  ]
    def __msinfofile( self, glb ):
        if 'msinfofile' in glb: return glb['msinfofile']
        dflt = self.__msinfofile_dflt( glb )
        if dflt is not None: return dflt
        return ''
    def __append( self, glb ):
        if 'append' in glb: return glb['append']
        dflt = self.__append_dflt( glb )
        if dflt is not None: return dflt
        return True
    def __overwrite( self, glb ):
        if 'overwrite' in glb: return glb['overwrite']
        dflt = self.__overwrite_dflt( glb )
        if dflt is not None: return dflt
        return False
    def __offset( self, glb ):
        if 'offset' in glb: return glb['offset']
        dflt = self.__offset_dflt( glb )
        if dflt is not None: return dflt
        return float(0.0)
    def __fixoffset( self, glb ):
        if 'fixoffset' in glb: return glb['fixoffset']
        dflt = self.__fixoffset_dflt( glb )
        if dflt is not None: return dflt
        return False
    def __stretch( self, glb ):
        if 'stretch' in glb: return glb['stretch']
        dflt = self.__stretch_dflt( glb )
        if dflt is not None: return dflt
        return False

    #--------- subparam inp output ----------------------------------------------------
    def __imagefiles_inp(self):
        description = ''
        value = self.__imagefiles( self.__globals_( ) )
        (pre,post) = ('','') if self.__validate_({'imagefiles': value},{'imagefiles': self.__schema['imagefiles']}) else ('\x1B[91m','\x1B[0m')
        self.__do_inp_output('%-13.13s = %s%-23s%s' % ('imagefiles',pre,self.__to_string_(value),post),description,0+len(pre)+len(post))
    def __ncpu_inp(self):
        description = ''
        value = self.__ncpu( self.__globals_( ) )
        (pre,post) = ('','') if self.__validate_({'ncpu': value},{'ncpu': self.__schema['ncpu']}) else ('\x1B[91m','\x1B[0m')
        self.__do_inp_output('%-13.13s = %s%-23s%s' % ('ncpu',pre,self.__to_string_(value),post),description,0+len(pre)+len(post))
    def __doreg_inp(self):
        description = ''
        value = self.__doreg( self.__globals_( ) )
        (pre,post) = ('','') if self.__validate_({'doreg': value},{'doreg': self.__schema['doreg']}) else ('\x1B[91m','\x1B[0m')
        self.__do_inp_output('\x1B[1m\x1B[47m%-13.13s =\x1B[0m %s%-23s%s' % ('doreg',pre,self.__to_string_(value),post),description,13+len(pre)+len(post))
    def __ephemfile_inp(self):
        if self.__ephemfile_dflt( self.__globals_( ) ) is not None:
             description = ''
             value = self.__ephemfile( self.__globals_( ) )
             (pre,post) = ('','') if self.__validate_({'ephemfile': value},{'ephemfile': self.__schema['ephemfile']}) else ('\x1B[91m','\x1B[0m')
             self.__do_inp_output('   \x1B[92m%-10.10s =\x1B[0m %s%-23s%s' % ('ephemfile',pre,self.__to_string_(value),post),description,9+len(pre)+len(post))
    def __timestamps_inp(self):
        if self.__timestamps_dflt( self.__globals_( ) ) is not None:
             description = ''
             value = self.__timestamps( self.__globals_( ) )
             (pre,post) = ('','') if self.__validate_({'timestamps': value},{'timestamps': self.__schema['timestamps']}) else ('\x1B[91m','\x1B[0m')
             self.__do_inp_output('   \x1B[92m%-10.10s =\x1B[0m %s%-23s%s' % ('timestamps',pre,self.__to_string_(value),post),description,9+len(pre)+len(post))
    def __msinfofile_inp(self):
        if self.__msinfofile_dflt( self.__globals_( ) ) is not None:
             description = ''
             value = self.__msinfofile( self.__globals_( ) )
             (pre,post) = ('','') if self.__validate_({'msinfofile': value},{'msinfofile': self.__schema['msinfofile']}) else ('\x1B[91m','\x1B[0m')
             self.__do_inp_output('   \x1B[92m%-10.10s =\x1B[0m %s%-23s%s' % ('msinfofile',pre,self.__to_string_(value),post),description,9+len(pre)+len(post))
    def __box_inp(self):
        description = ''
        value = self.__box( self.__globals_( ) )
        (pre,post) = ('','') if self.__validate_({'box': value},{'box': self.__schema['box']}) else ('\x1B[91m','\x1B[0m')
        self.__do_inp_output('%-13.13s = %s%-23s%s' % ('box',pre,self.__to_string_(value),post),description,0+len(pre)+len(post))
    def __region_inp(self):
        description = ''
        value = self.__region( self.__globals_( ) )
        (pre,post) = ('','') if self.__validate_({'region': value},{'region': self.__schema['region']}) else ('\x1B[91m','\x1B[0m')
        self.__do_inp_output('%-13.13s = %s%-23s%s' % ('region',pre,self.__to_string_(value),post),description,0+len(pre)+len(post))
    def __chans_inp(self):
        description = ''
        value = self.__chans( self.__globals_( ) )
        (pre,post) = ('','') if self.__validate_({'chans': value},{'chans': self.__schema['chans']}) else ('\x1B[91m','\x1B[0m')
        self.__do_inp_output('%-13.13s = %s%-23s%s' % ('chans',pre,self.__to_string_(value),post),description,0+len(pre)+len(post))
    def __stokes_inp(self):
        description = ''
        value = self.__stokes( self.__globals_( ) )
        (pre,post) = ('','') if self.__validate_({'stokes': value},{'stokes': self.__schema['stokes']}) else ('\x1B[91m','\x1B[0m')
        self.__do_inp_output('%-13.13s = %s%-23s%s' % ('stokes',pre,self.__to_string_(value),post),description,0+len(pre)+len(post))
    def __mask_inp(self):
        description = ''
        value = self.__mask( self.__globals_( ) )
        (pre,post) = ('','') if self.__validate_({'mask': value},{'mask': self.__schema['mask']}) else ('\x1B[91m','\x1B[0m')
        self.__do_inp_output('\x1B[1m\x1B[47m%-13.13s =\x1B[0m %s%-23s%s' % ('mask',pre,self.__to_string_(value),post),description,13+len(pre)+len(post))
    def __includepix_inp(self):
        description = ''
        value = self.__includepix( self.__globals_( ) )
        (pre,post) = ('','') if self.__validate_({'includepix': value},{'includepix': self.__schema['includepix']}) else ('\x1B[91m','\x1B[0m')
        self.__do_inp_output('%-13.13s = %s%-23s%s' % ('includepix',pre,self.__to_string_(value),post),description,0+len(pre)+len(post))
    def __excludepix_inp(self):
        description = ''
        value = self.__excludepix( self.__globals_( ) )
        (pre,post) = ('','') if self.__validate_({'excludepix': value},{'excludepix': self.__schema['excludepix']}) else ('\x1B[91m','\x1B[0m')
        self.__do_inp_output('%-13.13s = %s%-23s%s' % ('excludepix',pre,self.__to_string_(value),post),description,0+len(pre)+len(post))
    def __residual_inp(self):
        description = ''
        value = self.__residual( self.__globals_( ) )
        (pre,post) = ('','') if self.__validate_({'residual': value},{'residual': self.__schema['residual']}) else ('\x1B[91m','\x1B[0m')
        self.__do_inp_output('%-13.13s = %s%-23s%s' % ('residual',pre,self.__to_string_(value),post),description,0+len(pre)+len(post))
    def __model_inp(self):
        description = ''
        value = self.__model( self.__globals_( ) )
        (pre,post) = ('','') if self.__validate_({'model': value},{'model': self.__schema['model']}) else ('\x1B[91m','\x1B[0m')
        self.__do_inp_output('%-13.13s = %s%-23s%s' % ('model',pre,self.__to_string_(value),post),description,0+len(pre)+len(post))
    def __estimates_inp(self):
        description = ''
        value = self.__estimates( self.__globals_( ) )
        (pre,post) = ('','') if self.__validate_({'estimates': value},{'estimates': self.__schema['estimates']}) else ('\x1B[91m','\x1B[0m')
        self.__do_inp_output('%-13.13s = %s%-23s%s' % ('estimates',pre,self.__to_string_(value),post),description,0+len(pre)+len(post))
    def __logfile_inp(self):
        description = ''
        value = self.__logfile( self.__globals_( ) )
        (pre,post) = ('','') if self.__validate_({'logfile': value},{'logfile': self.__schema['logfile']}) else ('\x1B[91m','\x1B[0m')
        self.__do_inp_output('\x1B[1m\x1B[47m%-13.13s =\x1B[0m %s%-23s%s' % ('logfile',pre,self.__to_string_(value),post),description,13+len(pre)+len(post))
    def __append_inp(self):
        if self.__append_dflt( self.__globals_( ) ) is not None:
             description = ''
             value = self.__append( self.__globals_( ) )
             (pre,post) = ('','') if self.__validate_({'append': value},{'append': self.__schema['append']}) else ('\x1B[91m','\x1B[0m')
             self.__do_inp_output('   \x1B[92m%-10.10s =\x1B[0m %s%-23s%s' % ('append',pre,self.__to_string_(value),post),description,9+len(pre)+len(post))
    def __newestimates_inp(self):
        description = ''
        value = self.__newestimates( self.__globals_( ) )
        (pre,post) = ('','') if self.__validate_({'newestimates': value},{'newestimates': self.__schema['newestimates']}) else ('\x1B[91m','\x1B[0m')
        self.__do_inp_output('%-13.13s = %s%-23s%s' % ('newestimates',pre,self.__to_string_(value),post),description,0+len(pre)+len(post))
    def __complist_inp(self):
        description = ''
        value = self.__complist( self.__globals_( ) )
        (pre,post) = ('','') if self.__validate_({'complist': value},{'complist': self.__schema['complist']}) else ('\x1B[91m','\x1B[0m')
        self.__do_inp_output('\x1B[1m\x1B[47m%-13.13s =\x1B[0m %s%-23s%s' % ('complist',pre,self.__to_string_(value),post),description,13+len(pre)+len(post))
    def __overwrite_inp(self):
        if self.__overwrite_dflt( self.__globals_( ) ) is not None:
             description = ''
             value = self.__overwrite( self.__globals_( ) )
             (pre,post) = ('','') if self.__validate_({'overwrite': value},{'overwrite': self.__schema['overwrite']}) else ('\x1B[91m','\x1B[0m')
             self.__do_inp_output('   \x1B[92m%-10.10s =\x1B[0m %s%-23s%s' % ('overwrite',pre,self.__to_string_(value),post),description,9+len(pre)+len(post))
    def __dooff_inp(self):
        description = ''
        value = self.__dooff( self.__globals_( ) )
        (pre,post) = ('','') if self.__validate_({'dooff': value},{'dooff': self.__schema['dooff']}) else ('\x1B[91m','\x1B[0m')
        self.__do_inp_output('\x1B[1m\x1B[47m%-13.13s =\x1B[0m %s%-23s%s' % ('dooff',pre,self.__to_string_(value),post),description,13+len(pre)+len(post))
    def __offset_inp(self):
        if self.__offset_dflt( self.__globals_( ) ) is not None:
             description = ''
             value = self.__offset( self.__globals_( ) )
             (pre,post) = ('','') if self.__validate_({'offset': value},{'offset': self.__schema['offset']}) else ('\x1B[91m','\x1B[0m')
             self.__do_inp_output('   \x1B[92m%-10.10s =\x1B[0m %s%-23s%s' % ('offset',pre,self.__to_string_(value),post),description,9+len(pre)+len(post))
    def __fixoffset_inp(self):
        if self.__fixoffset_dflt( self.__globals_( ) ) is not None:
             description = ''
             value = self.__fixoffset( self.__globals_( ) )
             (pre,post) = ('','') if self.__validate_({'fixoffset': value},{'fixoffset': self.__schema['fixoffset']}) else ('\x1B[91m','\x1B[0m')
             self.__do_inp_output('   \x1B[92m%-10.10s =\x1B[0m %s%-23s%s' % ('fixoffset',pre,self.__to_string_(value),post),description,9+len(pre)+len(post))
    def __stretch_inp(self):
        if self.__stretch_dflt( self.__globals_( ) ) is not None:
             description = ''
             value = self.__stretch( self.__globals_( ) )
             (pre,post) = ('','') if self.__validate_({'stretch': value},{'stretch': self.__schema['stretch']}) else ('\x1B[91m','\x1B[0m')
             self.__do_inp_output('   \x1B[92m%-10.10s =\x1B[0m %s%-23s%s' % ('stretch',pre,self.__to_string_(value),post),description,9+len(pre)+len(post))
    def __rms_inp(self):
        description = ''
        value = self.__rms( self.__globals_( ) )
        (pre,post) = ('','') if self.__validate_({'rms': value},{'rms': self.__schema['rms']}) else ('\x1B[91m','\x1B[0m')
        self.__do_inp_output('%-13.13s = %s%-23s%s' % ('rms',pre,self.__to_string_(value),post),description,0+len(pre)+len(post))
    def __noisefwhm_inp(self):
        description = ''
        value = self.__noisefwhm( self.__globals_( ) )
        (pre,post) = ('','') if self.__validate_({'noisefwhm': value},{'noisefwhm': self.__schema['noisefwhm']}) else ('\x1B[91m','\x1B[0m')
        self.__do_inp_output('%-13.13s = %s%-23s%s' % ('noisefwhm',pre,self.__to_string_(value),post),description,0+len(pre)+len(post))
    def __summary_inp(self):
        description = ''
        value = self.__summary( self.__globals_( ) )
        (pre,post) = ('','') if self.__validate_({'summary': value},{'summary': self.__schema['summary']}) else ('\x1B[91m','\x1B[0m')
        self.__do_inp_output('%-13.13s = %s%-23s%s' % ('summary',pre,self.__to_string_(value),post),description,0+len(pre)+len(post))

    #--------- global default implementation-------------------------------------------
    @static_var('state', __sf__('casa_inp_go_state'))
    def set_global_defaults(self):
        self.set_global_defaults.state['last'] = self
        glb = self.__globals_( )
        if 'stokes' in glb: del glb['stokes']
        if 'complist' in glb: del glb['complist']
        if 'residual' in glb: del glb['residual']
        if 'doreg' in glb: del glb['doreg']
        if 'mask' in glb: del glb['mask']
        if 'model' in glb: del glb['model']
        if 'timestamps' in glb: del glb['timestamps']
        if 'estimates' in glb: del glb['estimates']
        if 'stretch' in glb: del glb['stretch']
        if 'imagefiles' in glb: del glb['imagefiles']
        if 'noisefwhm' in glb: del glb['noisefwhm']
        if 'logfile' in glb: del glb['logfile']
        if 'dooff' in glb: del glb['dooff']
        if 'rms' in glb: del glb['rms']
        if 'offset' in glb: del glb['offset']
        if 'msinfofile' in glb: del glb['msinfofile']
        if 'ephemfile' in glb: del glb['ephemfile']
        if 'ncpu' in glb: del glb['ncpu']
        if 'chans' in glb: del glb['chans']
        if 'region' in glb: del glb['region']
        if 'newestimates' in glb: del glb['newestimates']
        if 'box' in glb: del glb['box']
        if 'overwrite' in glb: del glb['overwrite']
        if 'fixoffset' in glb: del glb['fixoffset']
        if 'excludepix' in glb: del glb['excludepix']
        if 'summary' in glb: del glb['summary']
        if 'includepix' in glb: del glb['includepix']
        if 'append' in glb: del glb['append']

    #--------- inp function -----------------------------------------------------------
    def inp(self):
        print("# pimfit -- %s" % self._info_desc_)
        self.term_width, self.term_height = shutil.get_terminal_size(fallback=(80, 24))
        self.__imagefiles_inp( )
        self.__ncpu_inp( )
        self.__doreg_inp( )
        self.__ephemfile_inp( )
        self.__timestamps_inp( )
        self.__msinfofile_inp( )
        self.__box_inp( )
        self.__region_inp( )
        self.__chans_inp( )
        self.__stokes_inp( )
        self.__mask_inp( )
        self.__includepix_inp( )
        self.__excludepix_inp( )
        self.__residual_inp( )
        self.__model_inp( )
        self.__estimates_inp( )
        self.__logfile_inp( )
        self.__append_inp( )
        self.__newestimates_inp( )
        self.__complist_inp( )
        self.__overwrite_inp( )
        self.__dooff_inp( )
        self.__offset_inp( )
        self.__fixoffset_inp( )
        self.__stretch_inp( )
        self.__rms_inp( )
        self.__noisefwhm_inp( )
        self.__summary_inp( )

    #--------- tget function ----------------------------------------------------------
    @static_var('state', __sf__('casa_inp_go_state'))
    def tget(self,file=None):
        from casashell.private.stack_manip import find_frame
        from runpy import run_path
        filename = None
        if file is None:
            if os.path.isfile("pimfit.last"):
                filename = "pimfit.last"
        elif isinstance(file, str):
            if os.path.isfile(file):
                filename = file
        if filename is not None:
            glob = find_frame( )
            newglob = run_path( filename, init_globals={ } )
            for i in newglob:
                glob[i] = newglob[i]
            self.tget.state['last'] = self
        else:
            print("could not find last file, setting defaults instead...")
            self.set_global_defaults( )

    def __call__( self, imagefiles=None, ncpu=None, doreg=None, ephemfile=None, timestamps=None, msinfofile=None, box=None, region=None, chans=None, stokes=None, mask=None, includepix=None, excludepix=None, residual=None, model=None, estimates=None, logfile=None, append=None, newestimates=None, complist=None, overwrite=None, dooff=None, offset=None, fixoffset=None, stretch=None, rms=None, noisefwhm=None, summary=None ):
        def noobj(s):
           if s.startswith('<') and s.endswith('>'):
               return "None"
           else:
               return s
        _prefile = os.path.realpath('pimfit.pre')
        _postfile = os.path.realpath('pimfit.last')
        _return_result_ = None
        _arguments = [imagefiles,ncpu,doreg,ephemfile,timestamps,msinfofile,box,region,chans,stokes,mask,includepix,excludepix,residual,model,estimates,logfile,append,newestimates,complist,overwrite,dooff,offset,fixoffset,stretch,rms,noisefwhm,summary]
        _invocation_parameters = OrderedDict( )
        if any(map(lambda x: x is not None,_arguments)):
            # invoke python style
            # set the non sub-parameters that are not None
            local_global = { }
            if imagefiles is not None: local_global['imagefiles'] = imagefiles
            if ncpu is not None: local_global['ncpu'] = ncpu
            if doreg is not None: local_global['doreg'] = doreg
            if box is not None: local_global['box'] = box
            if region is not None: local_global['region'] = region
            if chans is not None: local_global['chans'] = chans
            if stokes is not None: local_global['stokes'] = stokes
            if mask is not None: local_global['mask'] = mask
            if includepix is not None: local_global['includepix'] = includepix
            if excludepix is not None: local_global['excludepix'] = excludepix
            if residual is not None: local_global['residual'] = residual
            if model is not None: local_global['model'] = model
            if estimates is not None: local_global['estimates'] = estimates
            if logfile is not None: local_global['logfile'] = logfile
            if newestimates is not None: local_global['newestimates'] = newestimates
            if complist is not None: local_global['complist'] = complist
            if dooff is not None: local_global['dooff'] = dooff
            if rms is not None: local_global['rms'] = rms
            if noisefwhm is not None: local_global['noisefwhm'] = noisefwhm
            if summary is not None: local_global['summary'] = summary

            # the invocation parameters for the non-subparameters can now be set - this picks up those defaults
            _invocation_parameters['imagefiles'] = self.__imagefiles( local_global )
            _invocation_parameters['ncpu'] = self.__ncpu( local_global )
            _invocation_parameters['doreg'] = self.__doreg( local_global )
            _invocation_parameters['box'] = self.__box( local_global )
            _invocation_parameters['region'] = self.__region( local_global )
            _invocation_parameters['chans'] = self.__chans( local_global )
            _invocation_parameters['stokes'] = self.__stokes( local_global )
            _invocation_parameters['mask'] = self.__mask( local_global )
            _invocation_parameters['includepix'] = self.__includepix( local_global )
            _invocation_parameters['excludepix'] = self.__excludepix( local_global )
            _invocation_parameters['residual'] = self.__residual( local_global )
            _invocation_parameters['model'] = self.__model( local_global )
            _invocation_parameters['estimates'] = self.__estimates( local_global )
            _invocation_parameters['logfile'] = self.__logfile( local_global )
            _invocation_parameters['newestimates'] = self.__newestimates( local_global )
            _invocation_parameters['complist'] = self.__complist( local_global )
            _invocation_parameters['dooff'] = self.__dooff( local_global )
            _invocation_parameters['rms'] = self.__rms( local_global )
            _invocation_parameters['noisefwhm'] = self.__noisefwhm( local_global )
            _invocation_parameters['summary'] = self.__summary( local_global )

            # the sub-parameters can then be set. Use the supplied value if not None, else the function, which gets the appropriate default
            _invocation_parameters['ephemfile'] = self.__ephemfile( _invocation_parameters ) if ephemfile is None else ephemfile
            _invocation_parameters['timestamps'] = self.__timestamps( _invocation_parameters ) if timestamps is None else timestamps
            _invocation_parameters['msinfofile'] = self.__msinfofile( _invocation_parameters ) if msinfofile is None else msinfofile
            _invocation_parameters['append'] = self.__append( _invocation_parameters ) if append is None else append
            _invocation_parameters['overwrite'] = self.__overwrite( _invocation_parameters ) if overwrite is None else overwrite
            _invocation_parameters['offset'] = self.__offset( _invocation_parameters ) if offset is None else offset
            _invocation_parameters['fixoffset'] = self.__fixoffset( _invocation_parameters ) if fixoffset is None else fixoffset
            _invocation_parameters['stretch'] = self.__stretch( _invocation_parameters ) if stretch is None else stretch

        else:
            # invoke with inp/go semantics
            _invocation_parameters['imagefiles'] = self.__imagefiles( self.__globals_( ) )
            _invocation_parameters['ncpu'] = self.__ncpu( self.__globals_( ) )
            _invocation_parameters['doreg'] = self.__doreg( self.__globals_( ) )
            _invocation_parameters['ephemfile'] = self.__ephemfile( self.__globals_( ) )
            _invocation_parameters['timestamps'] = self.__timestamps( self.__globals_( ) )
            _invocation_parameters['msinfofile'] = self.__msinfofile( self.__globals_( ) )
            _invocation_parameters['box'] = self.__box( self.__globals_( ) )
            _invocation_parameters['region'] = self.__region( self.__globals_( ) )
            _invocation_parameters['chans'] = self.__chans( self.__globals_( ) )
            _invocation_parameters['stokes'] = self.__stokes( self.__globals_( ) )
            _invocation_parameters['mask'] = self.__mask( self.__globals_( ) )
            _invocation_parameters['includepix'] = self.__includepix( self.__globals_( ) )
            _invocation_parameters['excludepix'] = self.__excludepix( self.__globals_( ) )
            _invocation_parameters['residual'] = self.__residual( self.__globals_( ) )
            _invocation_parameters['model'] = self.__model( self.__globals_( ) )
            _invocation_parameters['estimates'] = self.__estimates( self.__globals_( ) )
            _invocation_parameters['logfile'] = self.__logfile( self.__globals_( ) )
            _invocation_parameters['append'] = self.__append( self.__globals_( ) )
            _invocation_parameters['newestimates'] = self.__newestimates( self.__globals_( ) )
            _invocation_parameters['complist'] = self.__complist( self.__globals_( ) )
            _invocation_parameters['overwrite'] = self.__overwrite( self.__globals_( ) )
            _invocation_parameters['dooff'] = self.__dooff( self.__globals_( ) )
            _invocation_parameters['offset'] = self.__offset( self.__globals_( ) )
            _invocation_parameters['fixoffset'] = self.__fixoffset( self.__globals_( ) )
            _invocation_parameters['stretch'] = self.__stretch( self.__globals_( ) )
            _invocation_parameters['rms'] = self.__rms( self.__globals_( ) )
            _invocation_parameters['noisefwhm'] = self.__noisefwhm( self.__globals_( ) )
            _invocation_parameters['summary'] = self.__summary( self.__globals_( ) )
        try:
            with open(_prefile,'w') as _f:
                for _i in _invocation_parameters:
                    _f.write("%-12s = %s\n" % (_i,noobj(repr(_invocation_parameters[_i]))))
                _f.write("#pimfit( ")
                count = 0
                for _i in _invocation_parameters:
                    _f.write("%s=%s" % (_i,noobj(repr(_invocation_parameters[_i]))))
                    count += 1
                    if count < len(_invocation_parameters): _f.write(",")
                _f.write(" )\n")
        except: pass
        try:
            _return_result_ = _pimfit_t( _invocation_parameters['imagefiles'],_invocation_parameters['ncpu'],_invocation_parameters['doreg'],_invocation_parameters['ephemfile'],_invocation_parameters['timestamps'],_invocation_parameters['msinfofile'],_invocation_parameters['box'],_invocation_parameters['region'],_invocation_parameters['chans'],_invocation_parameters['stokes'],_invocation_parameters['mask'],_invocation_parameters['includepix'],_invocation_parameters['excludepix'],_invocation_parameters['residual'],_invocation_parameters['model'],_invocation_parameters['estimates'],_invocation_parameters['logfile'],_invocation_parameters['append'],_invocation_parameters['newestimates'],_invocation_parameters['complist'],_invocation_parameters['overwrite'],_invocation_parameters['dooff'],_invocation_parameters['offset'],_invocation_parameters['fixoffset'],_invocation_parameters['stretch'],_invocation_parameters['rms'],_invocation_parameters['noisefwhm'],_invocation_parameters['summary'] )
        except Exception as e:
            from traceback import format_exc
            from casatasks import casalog
            casalog.origin('pimfit')
            casalog.post("Exception Reported: Error in pimfit: %s" % str(e),'SEVERE')
            casalog.post(format_exc( ))
            _return_result_ = False
        try:
            os.rename(_prefile,_postfile)
        except: pass
        return _return_result_

pimfit = _pimfit( )