# Path: covest/constants.py
# MAX_EXP = 200
# GRID_DEPTH = 3
# INF = float('inf')
# VERBOSE = True
# PLOT_LOG_SCALE = True
# USE_BIGFLOAT = False
# STEP = 1.1
# OPTIMIZATION_METHOD = 'L-BFGS-B'
# INITIAL_GRID_COUNT = 20
# INITIAL_GRID_STEP = 3
# DEFAULT_ERR_SCALE = 1
# DEFAULT_K = 21
# DEFAULT_READ_LENGTH = 100
# DEFAULT_REPEAT_MODEL = 0
# DEFAULT_MIN_SINGLECOPY_RATIO = 0.3
# AUTO_SAMPLE_TARGET_COVERAGE = 12
# AUTO_TRIM_PRECISION = 6
# NOISE_THRESHOLD = 10**-6
# MAX_ERRORS = 8
# MAX_NOTRIM = 25
#     DEFAULT_THREAD_COUNT = cpu_count()
#     DEFAULT_THREAD_COUNT = 2
# 
# Path: covest/utils.py
# def safe_log(x):
#     if x is None or x <= 0:
#         return -constants.INF
#     return log(x)
# 
# def fix_zero(x, val=1):
#     if x == 0:
#         return val
#     else:
#         return x


import inspect
import itertools
import multiprocessing
import sys
import matplotlib.pyplot as plt
from covest_poisson import truncated_poisson as tr_poisson
from functools import lru_cache
from math import exp, fsum
from scipy.misc import comb
from covest import constants
from covest.utils import safe_log, fix_zero
        ]

    def compute_probabilities(self, c, err, *_):
        # read to kmer coverage
        ck = self.correct_c(c)
        # lambda for kmers with s errors
        l_s = self._get_lambda_s(ck, err)
        # expected probability of kmers with s errors and coverage >= 1
        n_s = [self.comb[s] * (1.0 - exp(-l_s[s])) for s in range(self.max_error)]
        sum_n_s = fix_zero(sum(n_s[t] for t in range(self.max_error)))
        # portion of kmers with s errors
        a_s = [n_s[s] / sum_n_s for s in range(self.max_error)]
        # probability that unique kmer has coverage j (j > 0)
        p_j = {
            j: sum(
                a_s[s] * tr_poisson(l_s[s], j) for s in range(self.max_error)
            )
            for j in self.hist
        }
        return p_j

    def compute_loglikelihood(self, *args):
        args = self.fit_to_bounds(args)
        p_j = self.compute_probabilities(*args)
        sp_j = min(1, fsum(p_j.values()))
        tail = self.tail * safe_log(1 - sp_j) if sp_j < 1 else 0
        return float(sum(
            h * safe_log(p_j[j]) for j, h in self.hist.items() if h
        )) + tail


# Path: covest/constants.py
# MAX_EXP = 200
# GRID_DEPTH = 3
# INF = float('inf')
# VERBOSE = True
# PLOT_LOG_SCALE = True
# USE_BIGFLOAT = False
# STEP = 1.1
# OPTIMIZATION_METHOD = 'L-BFGS-B'
# INITIAL_GRID_COUNT = 20
# INITIAL_GRID_STEP = 3
# DEFAULT_ERR_SCALE = 1
# DEFAULT_K = 21
# DEFAULT_READ_LENGTH = 100
# DEFAULT_REPEAT_MODEL = 0
# DEFAULT_MIN_SINGLECOPY_RATIO = 0.3
# AUTO_SAMPLE_TARGET_COVERAGE = 12
# AUTO_TRIM_PRECISION = 6
# NOISE_THRESHOLD = 10**-6
# MAX_ERRORS = 8
# MAX_NOTRIM = 25
#     DEFAULT_THREAD_COUNT = cpu_count()
#     DEFAULT_THREAD_COUNT = 2
# 
# Path: covest/utils.py
# def safe_log(x):
#     if x is None or x <= 0:
#         return -constants.INF
#     return log(x)
# 
# def fix_zero(x, val=1):
#     if x == 0:
#         return val
#     else:
#         return x


# Path: covest/models.py
import inspect
import itertools
import multiprocessing
import sys
import matplotlib.pyplot as plt
from covest_poisson import truncated_poisson as tr_poisson
from functools import lru_cache
from math import exp, fsum
from scipy.misc import comb
from covest import constants
from covest.utils import safe_log, fix_zero

        ]

    def compute_probabilities(self, c, err, *_):
        # read to kmer coverage
        ck = self.correct_c(c)
        # lambda for kmers with s errors
        l_s = self._get_lambda_s(ck, err)
        # expected probability of kmers with s errors and coverage >= 1
        n_s = [self.comb[s] * (1.0 - exp(-l_s[s])) for s in range(self.max_error)]
        sum_n_s = fix_zero(sum(n_s[t] for t in range(self.max_error)))
        # portion of kmers with s errors
        a_s = [n_s[s] / sum_n_s for s in range(self.max_error)]
        # probability that unique kmer has coverage j (j > 0)
        p_j = {
            j: sum(
                a_s[s] * tr_poisson(l_s[s], j) for s in range(self.max_error)
            )
            for j in self.hist
        }
        return p_j

    def compute_loglikelihood(self, *args):
        args = self.fit_to_bounds(args)
        p_j = self.compute_probabilities(*args)
        sp_j = min(1, fsum(p_j.values()))
        tail = self.tail * safe_log(1 - sp_j) if sp_j < 1 else 0
        return float(sum(
            h * safe_log(p_j[j]) for j, h in self.hist.items() if h
        )) + tail


    def compute_loglikelihood_multi(self, args_list, thread_count=constants.DEFAULT_THREAD_COUNT):