#!/usr/bin/env python
from __future__ import print_function

import sys, os
import re
import traceback
import itertools
import warnings
import functools
VERBOSE=False

zip_longest = itertools.zip_longest
base_string_type = str
get_input = input

def is_string(x):
	return isinstance(x, base_string_type)

def version_file(val=None):
	v = "VERSION"
	if os.path.exists(v):
		if val is None:
			with open(v) as f:
				return f.read().strip()
		else:
			with open(v, 'w') as f:
				f.write(val)
			return True
version_file.desc = "VERSION"

def json_file(filename):
	def _replacer(val=None):
		return replace(filename, re.compile("""(?P<pre>["']?version["']?\s*:\s*['"])(?P<version>[^'"]*)"""), val)
	_replacer.desc=filename
	return _replacer

package_json = json_file("package.json")
bower_json = json_file("bower.json")

def replace(filename, regex, val):
	if not os.path.exists(filename):
		if VERBOSE:
			print("Skipping %s (file doesn't exist)" % (filename,), file=sys.stderr)
		return None
	with open(filename) as f:
		lines = f.read()
	match = re.search(regex, lines)
	if VERBOSE and not match:
		print("No match found in %s" % (filename,),  file=sys.stderr)
	if val is None:
		return match.group('version') if match else None
	elif match:
		with open(filename, 'w') as f:
			f.write(re.sub(regex, r"\g<pre>%s" % (val,), lines))
		return True

def setup_py(val=None):
	return replace("setup.py", re.compile("""(?P<pre>version\s*=\s*u?['"])(?P<version>[^'"]*)"""), val)
setup_py.desc = "setup.py"

version_strategies = [version_file, setup_py, package_json, bower_json]
def version_types():
	versions = []
	for strat in version_strategies:
		value = _apply_strategy(strat)
		if value:
			versions.append(Version.parse(value, desc=strat.desc, expand_symbolic=True))
	return versions

def set_version(new_version):
	do = lambda s: _apply_strategy(s, new_version)
	results = map(do, version_strategies)
	successes = filter(bool, results)
	return list(successes)

def _apply_strategy(strategy, new_version=None):
	try:
		return strategy(new_version)
	except Exception as e:
		print("[ error: %s  (%s)]" % (e,strategy.desc),  file=sys.stderr)
		if VERBOSE:
			traceback.print_exc(file=sys.stderr)

#TODO: replace with rich equality version?
def zip_cmp(pairs):
	for pair in pairs:
		a,b = pair
		if a == b: continue
		if a < b: return -1
		if a > b: return 1
	return 0

@functools.total_ordering
class Version(object):
	@classmethod
	def guess(cls):
		return version_types()[0]

	@classmethod
	def parse(cls, number, desc=None, coerce=False, expand_symbolic=False):
		"""
		>>> Version.parse('2.0.1a5')
		Version('2.0.1-a5')

		>>> Version.parse('2.0.1a5', coerce=True)
		Version('2.0.1-pre-pre5')

		>>> Version.parse('2.0.1-a5', coerce=True)
		Version('2.0.1-pre-pre5')

		>>> Version.parse('2.0.1b3', coerce=True)
		Version('2.0.1-pre-post3')

		>>> Version.parse('2.0.1b3.0', coerce=True)
		Version('2.0.1-pre-post3.0')

		>>> Version.parse('2.0.1rc3', coerce=True)
		Version('2.0.1-rc3')

		>>> Version.parse('2.0.1-c3', coerce=True)
		Version('2.0.1-rc3')

		>>> Version.parse('2.0.1.pre.3', coerce=True)
		Version('2.0.1-pre-3')

		>>> Version.parse('2.0.1.pre.3.1', coerce=True)
		Version('2.0.1-pre-3.1')

		>>> Version.parse('2.0.1.pre', coerce=True)
		Version('2.0.1-pre')

		>>> import time
		>>> str(Version.parse('date', expand_symbolic=True)).startswith('0.' + time.strftime('%Y'))
		True
		"""
		if expand_symbolic:
			if number.lower() == 'date':
				import time
				return cls.parse("0." + time.strftime("%Y%m%d.%H.%M"), desc=desc)
		if coerce:
			number = number.lower()
			# combine lonely suffixes into their surrounding numbers
			number = re.sub(r'(\d+\.)?([a-z]+)(\.\d+)?', lambda match: match.group(0).replace(".", "-"), number)

		components = list(map(lambda s: VersionComponent.parse(s, coerce=coerce), number.split('.')))
		return cls(components, desc=desc)

	def __init__(self, components, desc=None):
		assert not is_string(components), "use Version.parse()"
		self.number = '.'.join(map(str,components)) # XXX REMOVE
		self.components = components
		self.desc = desc

	def __eq__(self, other):
		return self.__cmp__(other) == 0

	def __lt__(self, other):
		return self.__cmp__(other) < 0

	def __cmp__(self, other):
		"""
		>>> sort = lambda *strs: list(map(str, sorted([Version.parse(x) for x in strs])))
		>>> sort('0.1','0.10','1.0')
		['0.1', '0.10', '1.0']
		>>> sort('1', '1-1', '1-pre','1-rc','1-rc1','1-post')
		['1-pre', '1-rc', '1-rc1', '1', '1-1', '1-post']
		>>> sort('1-post-pre', '1-post')
		['1-post-pre', '1-post']
		>>> Version.parse('0.1') == Version.parse('0.1.0')
		True
		>>> Version.parse('0.1') == Version.parse('0.1-0')
		True
		>>> Version.parse('0.1-1') == Version.parse('0.1-whatever1')
		False
		"""
		filler = VersionComponent(0)
		return zip_cmp(zip_longest(self.components, other.components, fillvalue=filler))

	def next(self):
		"""
		Increment the version number *minimally*, according to the zero-install
		version numbers spec (http://0install.net/interface-spec.html#versions):

		>>> Version.parse('1.0').next()
		Version('1.0-post')

		>>> Version.parse('1.0-pre').next()
		Version('1.0-pre1')
		>>> Version.parse('1.0-pre1').next()
		Version('1.0-pre2')

		>>> Version.parse('1.0-post1-pre').next()
		Version('1.0-post1-pre1')
		>>> Version.parse('1.0-post1-pre1').next()
		Version('1.0-post1-pre2')

		>>> Version.parse('1.0-rc').next()
		Version('1.0-rc1')
		>>> Version.parse('1.0-rc1').next()
		Version('1.0-rc2')

		>>> Version.parse('1.0-post').next()
		Version('1.0-post1')
		>>> Version.parse('1.0-post1').next()
		Version('1.0-post2')
		>>> Version.parse('1.0-post07').next()
		Version('1.0-post8')
		>>> Version.parse('1.0-post9').next()
		Version('1.0-post10')

		# arbitrary suffixes are supported, if present:
		>>> Version.parse('1.0-foo').next()
		Version('1.0-foo1')
		"""

		last = self.components[-1]
		components = self.components[:-1] + [last.next()]
		return Version(components)

	def increment(self, levels=1):
		"""
		Increment the version component at `levels` least-significant
		position (1 is the least significant):

		>>> Version.parse('1.2.3').increment()
		Version('1.2.4')
		>>> Version.parse('1.2.3').increment(2)
		Version('1.3.0')
		>>> Version.parse('1.2.3').increment(3)
		Version('2.0.0')

		-pre, -rc and -post suffixes are handled also:

		>>> Version.parse('0.1.3-pre').increment()
		Version('0.1.3')
		>>> Version.parse('0.1.3-post').increment()
		Version('0.1.4')
		>>> Version.parse('0.1.3-post').increment(2)
		Version('0.2.0')
		>>> Version.parse('0.1.3-rc').increment()
		Version('0.1.3')

		"""
		before, middle, after = rsplit_list(self.components, levels)
		middle = middle.increment()
		after = [VersionComponent(0) for part in after]
		all_parts = before + [middle] + after
		return Version(components=all_parts)

	def suffix(self, suf):
		version, old_suf = split_suffix(self.number)
		return Version.parse("%s-%s" % (version, suf))

	def __str__(self):
		return str(self.number)

	def describe(self):
		return "%-8s (%s)" % (self.desc, self.number)

	def __repr__(self):
		return "Version(%r)" % (self.number,)

	def __nonzero__(self):
		return self.number is not None

	def __hash__(self):
		"""

		>>> list(sorted(set(map(Version.parse, ("0.1", "0.2", "0.1")))))
		[Version('0.1'), Version('0.2')]
		"""
		return hash(self.number)

def prompt(msg):
	if sys.stdin.isatty():
		return get_input(msg).strip().lower() in ('y','yes','')
	else:
		return True

def main(opts, input=None):
	"""

	>>> # hacky stuff to mock out functionality
	>>> import version
	>>> def set_version(new):
	...     print(":: new %s" % (new,))
	...     return [True]
	>>> def version_types():
	...     return [Version.parse('0.1.2', 'fake')]
	>>> version.version_types = version_types
	>>> version.set_version = set_version
	>>> version.prompt = lambda *a: True
	>>> class Object(object):
	... 	def __init__(self, **k):
	... 		[setattr(self, k, v) for k, v in k.items()]
	... 	def __getattr__(self, k): return None

	>>> version.main(Object(suffix='post'))
	fake     (0.1.2)
	:: new 0.1.2-post
	changed version in 1 files.

	>>> version.main(Object(suffix='post'), '+')
	fake     (0.1.2)
	:: new 0.1.3-post
	changed version in 1 files.

	>>> version.main(Object(suffix='pre'), '++')
	fake     (0.1.2)
	:: new 0.2.0-pre
	changed version in 1 files.

	>>> version.main(Object(raw=True))
	0.1.2
	"""
	versions = version_types()
	if opts.raw:
		if input:
			v = get_version(input, versions)
		else:
			v = versions[0]
		print(v)
		return
	else:
		print("\n".join([version.describe() for version in versions]))
	if input or opts.suffix:
		new_version = get_version(input, versions)
		if opts.suffix:
			new_version = new_version.suffix(opts.suffix)

		ok = prompt("\nchange version to %s? " % (new_version.number,))
		if not ok:
			sys.exit(0)
		changed = set_version(new_version.number)
		print("changed version in %s files." % (len(changed),))

def get_version(input, current_versions):
	"""
	>>> get_version('1234', [])
	Version('1234')
	>>> get_version('+', [Version.parse('0.1.2')])
	Version('0.1.3')
	>>> get_version('+', [Version.parse('0.1.9')])
	Version('0.1.10')
	>>> get_version('+', [Version.parse('0.1')])
	Version('0.2')

	>>> get_version('++', [Version.parse('0.1.2')])
	Version('0.2.0')
	>>> get_version('++', [Version.parse('0.1')])
	Version('1.0')

	>>> get_version(None, [Version.parse('0.1')])
	Version('0.1')

	>>> get_version('.', [Version.parse('0.1')])
	Version('0.1-post')

	"""
	if input is None:
		return current_versions[0]
	if all([char == '+' for char in input]):
		return current_versions[0].increment(len(input))
	elif input == '=':
		return current_versions[0]
	elif input == '.':
		return current_versions[0].next()
	else:
		return Version.parse(input, expand_symbolic=True)

def rsplit_list(parts, idx):
	"""splits a version at idx from the lest-significant portion (starting at 1):
	>>> rsplit_list([0,1,2], 1)
	([0, 1], 2, [])
	>>> rsplit_list([0,1,2], 2)
	([0], 1, [2])
	>>> rsplit_list([0,1,2], 3)
	([], 0, [1, 2])

	>>> rsplit_list([0,1], 1)
	([0], 1, [])
	>>> rsplit_list([0,1], 2)
	([], 0, [1])

	>>> rsplit_list([0,1], 3)
	([], 0, [1])
	"""
	middle = max(0, len(parts) - idx)
	more_significant = parts[:middle]
	less_significant = parts[middle+1:]
	return (more_significant, parts[middle], less_significant)

def split(version, idx):
	"""splits a version at idx from the lest-significant portion (starting at 1):

	>>> split('0.1.2', 1)
	(['0', '1'], '2', [])
	>>> split('0.1.2', 2)
	(['0'], '1', ['2'])
	>>> split('0.1.2', 3)
	([], '0', ['1', '2'])

	>>> split('0.1', 1)
	(['0'], '1', [])
	>>> split('0.1', 2)
	([], '0', ['1'])

	>>> split('0.1', 3)
	([], '0', ['1'])
	"""
	parts = version.split(".")
	middle = max(0, len(parts) - idx)
	more_significant = parts[:middle]
	less_significant = parts[middle+1:]
	return (more_significant, parts[middle], less_significant)

def split_suffix(part):
	"""
	Split a component into non-suffix and suffix component.
	If no suffix present, return (part, None)

	>>> split_suffix("1-pre")
	('1', 'pre')
	>>> split_suffix("2")
	('2', None)
	"""
	if not '-' in part:
		return (part, None)
	return tuple(part.rsplit('-', 1))

sentinel=object()
def take_re(pattern, val, default=sentinel):
	"""
	>>> take_re('f.', 'foop')
	('fo', 'op')

	>>> take_re(re.compile('f.'), 'ofoop')
	Traceback (most recent call last):
	ValueError: Value 'ofoop' does not match regex 'f.'

	>>> take_re('f.', 'ofoop', None)
	(None, 'ofoop')
	"""
	match = re.match(pattern, val)
	if not match:
		if default is sentinel:
			raise ValueError("Value %r does not match regex %r" % (val, getattr(pattern, 'pattern', pattern)))
		return (default, val)
	found = match.group(0)
	return (found, val[len(found):])

def _replace_suffix_aliases(s):
	alpha, number = take_re(_alphas_re, s, '')
	if alpha == 'a':
		return ['pre', 'pre' + number]
	if alpha == 'b':
		return ['pre', 'post' + number]
	if alpha == 'c':
		return ['rc' + number]
	# everything else, we leave as-is
	return [s]

_digits_re = re.compile('\d+')
_alphas_re = re.compile('[a-z]+')

@functools.total_ordering
class VersionComponent(object):
	"""
	>>> VersionComponent.parse("1").value
	1
	>>> VersionComponent.parse("1-pre2").suffixes
	[pre2]
	>>> VersionComponent.parse("1-pre2").suffixes[0].rank
	2
	>>> str(VersionComponent.parse("10-pre2"))
	'10-pre2'

	>>> str(VersionComponent.parse("b2"))
	'0-b2'

	alpha an beta both need to map to "pre-something". This
	isn't pretty, but should preserve ordering in all cases:

	>>> str(VersionComponent.parse("b2", coerce=True))
	'0-pre-post2'

	>>> str(VersionComponent.parse("a2", coerce=True))
	'0-pre-pre2'

	>>> str(VersionComponent.parse("1a2", coerce=True))
	'1-pre-pre2'

	>>> str(VersionComponent.parse("1_a2", coerce=True))
	'1-pre-pre2'

	>>> str(VersionComponent.parse("1-c2", coerce=True))
	'1-rc2'

	>>> str(VersionComponent.parse("rc2", coerce=True))
	'0-rc2'

	>>> str(VersionComponent.parse("p2", coerce=True))
	'0-p2'
	"""
	@classmethod
	def parse(cls, val, coerce=False):
		try:
			seps = '-_' if coerce else '-'
			value, suffixes = take_re(_digits_re, val, '')
			suffixes = suffixes.lstrip(seps)
			suffixes = re.split('[%s]' % (seps,), suffixes)
			if coerce:
				suffixes = itertools.chain(*map(_replace_suffix_aliases, suffixes))
			suffixes = filter(None, suffixes)
			suffixes = list(map(Suffix.parse, suffixes))
			value = int(value) if value else 0
			return cls(value, suffixes)
		except (ValueError) as e:
			raise ValueError("Can't parse version component %s: %s" % (val, e))

	def __init__(self, value, suffixes=[]):
		self.value = value
		self.suffixes = suffixes

	def __str__(self):
		return '-'.join(map(str, [self.value] + self.suffixes))

	def __repr__(self):
		return "<VersionComponent %s>" % (self,)

	def __eq__(self, other):
		return self.__cmp__(other) == 0

	def __lt__(self, other):
		return self.__cmp__(other) < 0

	def __cmp__(self, other):
		my_parts = [self.value] + self.suffixes
		other_parts = [other.value] + other.suffixes
		return zip_cmp(zip_longest(my_parts, other_parts, fillvalue = Suffix(None)))

	def next(self):
		if not self.suffixes:
			new_suffixes = [Suffix.parse(None).next()]
		else:
			new_suffixes = self.suffixes[:-1] + [self.suffixes[-1].next()]
		return type(self)(self.value, new_suffixes)

	def increment(self):
		value = self.value + 1
		if self.suffixes and self.suffixes[0] < Suffix():
			# -pre or -rc increment by simply removing the suffix
			value = self.value
		return VersionComponent(value)

KNOWN_SUFFIXES = ['pre', 'rc', None, 'post']
@functools.total_ordering
class Suffix(object):
	"""
	>>> list(map(str, sorted([
	... Suffix.parse('pre'),
	... Suffix.parse('post'),
	... Suffix.parse('rc'),
	... Suffix.parse('pre1'),
	... Suffix.parse('post1'),
	... Suffix.parse('rc3'),
	... Suffix.parse('pre2'),
	... Suffix.parse(None),
	... ])))
	['pre', 'pre1', 'pre2', 'rc', 'rc3', '', 'post', 'post1']
	>>> bool(Suffix.parse('pre'))
	True
	>>> bool(Suffix.parse(None))
	False
	"""
	def __init__(self, name=None, rank=0):
		self.name = name
		self.rank = rank

	@classmethod
	def parse(cls, suffix):
		if suffix is None:
			return cls()
		else:
			name, rank = cls._split(suffix)
			return cls(name, rank)

	def __nonzero__(self): # py2
		return self.__bool__()

	def __bool__(self): # py3
		return self.name is not None

	def __repr__(self):
		return ''.join([str(part) for part in (self.name, self.rank) if part])

	def next(self):
		"""
		>>> Suffix.parse('rc').next()
		rc1
		>>> Suffix.parse('rc9').next()
		rc10
		>>> Suffix.parse(None).next()
		post
		"""
		if not self:
			return Suffix('post', 0)
		else:
			return Suffix(self.name, self.rank + 1)

	@classmethod
	def _split(self, suffix):
		"""
		>>> Suffix._split('pre')
		('pre', 0)
		>>> Suffix._split('rc5')
		('rc', 5)
		>>> Suffix._split('rc11')
		('rc', 11)
		"""
		text, digits = take_re(_alphas_re, suffix, None)
		return (text, int(digits or '0'))

	@property
	def _cmp_val(self):
		return (self._name_ord(), self.rank)

	def __eq__(self, other):
		"""
		>>> Suffix(None) == Suffix('',0)
		True
		"""
		return self._cmp_val == other._cmp_val

	def __lt__(self, other):
		return self._cmp_val < other._cmp_val

	def _name_ord(self):
		try:
			return KNOWN_SUFFIXES.index(self.name or None)
		except ValueError:
			return len(KNOWN_SUFFIXES)

def _main():
	import optparse
	p = optparse.OptionParser(usage="%prog [OPTIONS] [version]")
	p.add_option('-v', '--verbose', action='store_true', help="print more debugging info", default=False)
	p.add_option('-r', '--raw', action='store_true', help="print a single version string and nothing else")
	p.add_option('--pre',  action='store_const', const='pre', dest='suffix', help="set -pre suffix", default=None)
	p.add_option('--rc',   action='store_const', const='rc',  dest='suffix', help="set -rc suffix")
	p.add_option('--post', action='store_const', const='post',dest='suffix', help="set -post suffix")

	opts, args = p.parse_args()
	VERBOSE = opts.verbose

	try:
		main(opts, *args)
	except (KeyboardInterrupt, EOFError):
		print("")
		sys.exit(1)
	except Exception as e:
		print(e,  file=sys.stderr)
		if VERBOSE: raise
		sys.exit(1)

if __name__ == '__main__':
	_main()


from typing import Tuple

import numpy as np

from ..qnms import final_mass_spin, freq_damping
from ..utils import CC, GG, MPC, SOLAR_MASS
from . import MemoryGenerator

class MWM(MemoryGenerator):
    def __init__(
        self,
        q: float,
        total_mass: float = 60,
        distance: float = 400,
        name: str = "MWM",
        times: np.ndarray = None,
    ):
        super(MWM, self).__init__(name=name, h_lm=dict(), times=times)
        self.name = name
        if q > 1:
            q = 1 / q
        self.q = q
        self.MTot = total_mass
        self.distance = distance
        self.m1 = self.MTot / (1 + self.q)
        self.m2 = self.m1 * self.q

        if times is None:
            times = np.linspace(-900, 100, 10001) / self.t_to_geo
        self.times = times

    def time_domain_memory(
        self, inc: float, phase: float, times: np.ndarray = None, rm: float = 3
    ) -> Tuple[dict, np.ndarray]:
        """
        Calculates the plus and cross polarisations for the
        minimal waveform model memory waveform:
        eqns (5) and (9) from Favata (2009. ApJL 159)

        TODO: Implement spherical harmonic decomposition?

        Parameters
        ----------
        inc: float
            Binary inclination angle
        phase: float
            Binary phase at coalescence
        times: array, optional
            Time array on which the memory is calculated
        rm: float, optional
            Radius at which the matching occurs in solar masses

        Returns
        -------
        h_mem: dict
            Plus and cross polarisations of the memory waveform
        times: np.array
            Time array on which the memory is calculated

        Paul Lasky
        """
        if times is None:
            times = self.times

        time_geo = times * CC

        mass_solar_to_geometric = SOLAR_MASS * GG / CC**2
        m1_geo = self.m1 * mass_solar_to_geometric
        m2_geo = self.m2 * mass_solar_to_geometric

        dist_geo = self.distance * MPC

        # total mass
        MM = m1_geo + m2_geo

        # symmetric mass ratio
        eta = self.m1 * self.m2 / (self.m1 + self.m2) ** 2

        # this is the orbital separation at the matching radius --
        # see Favata (2009) before eqn (8).
        # the default value for this is given as rm = 3 MM.
        rm *= MM

        # calculate dimensionless mass and spin of the final black hole
        # from the Buonanno et al. (2007) fits
        Mf_geo, jj = final_mass_spin(m1_geo, m2_geo)

        # calculate the QNM frequencies and damping times
        # from the fits in Table VIII of Berti et al. (2006)
        omega, tau = np.array([freq_damping(Mf_geo, jj, nn=nn) for nn in range(3)]).T

        sigma = 1j * omega + 1 / tau
        sigma_plus_sigma_star = np.add.outer(sigma, sigma.conj())

        # set the matching time to be at t = 0
        tm = 0
        TT = time_geo - tm

        # some quantity defined after equation (7) of Favata
        trr = 5 * MM * rm**4 / (256 * eta * MM**4)

        # calculate the A_{ell m n} matching coefficients.  Note that
        # I've solved a matrix equation that solved for the three coefficients
        # from three equations
        xi = 2 * np.sqrt(2 * np.pi / 5) * eta * MM * rm**2
        chi = -2 * 1j * np.sqrt(MM / rm**3)

        amplitude = (
            xi
            * chi**2
            * np.array(
                [
                    (sigma[(ii + 1) % 3] + chi)
                    * (sigma[(ii + 2) % 3] + chi)
                    / (sigma[ii % 3] - sigma[(ii + 1) % 3])
                    / (sigma[ii % 3] - sigma[(ii + 2) % 3])
                    for ii in range(3)
                ]
            )
        )

        # Calculate the coefficients in the summed term of equation (9)
        # from Favata (2009) this is a double sum, with each variable going
        # from n = 0 to 2; therefore 9 terms
        coeffs = (
            np.outer(sigma * amplitude, sigma.conj() * amplitude.conj())
            / sigma_plus_sigma_star
        )

        # radial separation
        rr = rm * (1 - TT / trr) ** (1 / 4)

        # set up strain
        h_MWM = np.zeros(len(TT))

        # calculate strain for TT < 0.
        post_merger = TT > 0
        h_MWM[~post_merger] = 8 * np.pi * MM / rr[~post_merger]

        TT = TT[post_merger]

        # calculate strain for TT > 0.
        terms = coeffs * (
            1 - np.exp(-TT[:, np.newaxis, np.newaxis] * sigma_plus_sigma_star)
        )

        alt_sum_terms = terms.sum(axis=(1, 2))

        h_MWM[post_merger] = 8 * np.pi * MM / rm + alt_sum_terms / (eta * MM)

        # calculate the plus polarisation of GWs: eqn. (5) from Favata (2009)
        h_plus_coeff = (
            0.77
            * eta
            * MM
            / (384 * np.pi)
            * np.sin(inc) ** 2
            * (17 + np.cos(inc) ** 2)
            / dist_geo
        )
        h_mem = dict(plus=h_plus_coeff * h_MWM, cross=np.zeros_like(h_MWM))
        h_mem["plus"] -= h_mem["plus"][0]

        return h_mem, times


#! /usr/local/bin/stackless2.6

"""Functions for monkey-patching Python libraries to use Syncless."""

__author__ = 'pts@fazekas.hu (Peter Szabo)'

import sys
import types

# TODO(pts): Have a look at gevent, Eventlet, Concurrence (or others) for
# patching everything.

fake_create_connection = None

def get_fake_create_connection():
  global fake_create_connection
  if fake_create_connection is None:
    import socket
    import coio
    fake_create_connection_globals = {
        'getaddrinfo': coio.partial_getaddrinfo,
        'socket': coio.nbsocket,
        'SOCK_STREAM': socket.SOCK_STREAM,
        '_GLOBAL_DEFAULT_TIMEOUT': socket._GLOBAL_DEFAULT_TIMEOUT,
        'error': socket.error,
    }
    fake_create_connection = types.FunctionType(
      socket.create_connection.func_code, fake_create_connection_globals,
      None, socket.create_connection.func_defaults)
    fake_create_connection.__doc__ = (
        """Non-blocking drop-in replacement for socket.create_connection.""")
  return fake_create_connection

def _populate_socket_module_with_coio(socket_module):
  """Populate a socket module with coio non-blocking functions and classes."""
  from syncless import coio
  # There is no need to afraid that ssl.SSLSocket would pick up coio.nbsocket
  # as its base class, because when `coio' is loaded above, it loads `ssl' as
  # well, so loading `ssl' happens before the following assignment.
  socket_module.socket = coio.nbsocket
  # TODO(pts): Maybe make this a class?
  socket_module._realsocket = coio.new_realsocket
  if not hasattr(socket_module, '_socket'):
     # Create new module.
    socket_module._socket = type(socket_module)('fake_coio_c_socket')
  socket_module._socket.socket = coio.new_realsocket
  socket_module.gethostbyname = coio.gethostbyname
  socket_module.gethostbyname_ex = coio.gethostbyname_ex
  socket_module.gethostbyaddr = coio.gethostbyaddr
  socket_module.getfqdn = coio.getfqdn
  # TODO(pts): Better indicate NotImplementedError
  socket_module.getaddrinfo = None
  socket_module.getnameinfo = None
  socket_module.create_connection = get_fake_create_connection()
  socket_module.socketpair = coio.socketpair
  socket_module.ssl = coio.sslwrap_simple
  return socket_module

fake_coio_socket_module = None
"""None or a fake socket module containing constants and some coio values."""

def get_fake_coio_socket_module():
  global fake_coio_socket_module
  if fake_coio_socket_module is None:
    import socket
    fake_coio_socket_module = type(socket)('fake_coio_socket')
    for name in dir(socket):
      if (name[0] in 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' and
          isinstance(getattr(socket, name), int) or
          name == 'error'):
        setattr(fake_coio_socket_module, name, getattr(socket, name))
      if name.startswith('RAND_'):
        # RAND_Add, RAND_egd and RAND_status from the SSL module.
        setattr(fake_coio_socket_module, name, getattr(socket, name))
    _populate_socket_module_with_coio(fake_coio_socket_module)
  return fake_coio_socket_module

fake_coio_select_module = None
"""None or a fake select module containing select()."""

def get_fake_coio_select_module():
  global fake_coio_select_module
  if fake_coio_select_module is None:
    from syncless import coio
    fake_coio_select_module = type(coio)('fake_coio_select')
    fake_coio_select_module.select = coio.select
  return fake_coio_select_module

def patch_socket():
  """Monkey-patch the socket module for non-blocking I/O."""
  import socket
  _populate_socket_module_with_coio(socket)

def patch_ssl():
  """Monkey-patch the standard ssl module for non-blocking I/O."""
  import ssl
  from syncless import coio
  ssl.SSLSocket = coio.nbsslsocket
  # There is no need to patch ssl.wrap_socket since ssl.SSLSocket is already
  # patched.
  #ssl.wrap_socket = coio.ssl_wrap_socket

def patch_geventmysql():
  """Patch gevent-MySQL at 2010-08-02.

  It is OK and harmless to call this function more than once.

  It is recommended that this function is called before `import geventmysql',
  so the real gevent wouldn't get loaded, and this patch would work without
  gevent even being installed.

  TODO(pts): Make it geventmysql with real gevent and Syncless in the same
  process.
  """
  import sys
  from syncless import coio

  if ('geventmysql' in sys.modules and
      sys.modules['geventmysql'].gevent.socket.socket is coio.nbsocket):
    return  # Already patched.

  import socket

  class Timeout(Exception):
    """Unused timeout class for Syncless geventmysql."""
    pass

  def create_connection(address):
    # socket.create_connection supports timeout=..., which is not needed by
    # geventmysql, so we don't support it here.
    if isinstance(address, str) and address[0] == '/':
      family = socket.AF_UNIX
    elif ':' in address[0]:
      # TODO(pts): Better support IPv6 connections, especially as DNS hosts.
      family = socket.AF_INET6
    else:
      family = socket.AF_INET
    sock = coio.nbsocket(family, socket.SOCK_STREAM)
    sock.connect(address)
    return sock

  gevent_module = type(sys)('fake_gevent')
  gevent_module.socket = type(sys)('fake_gevent_socket')
  gevent_module.socket.create_connection = create_connection
  gevent_module.socket.socket = coio.nbsocket
  gevent_module.GreenletExit = TaskletExit
  # This exception is needed, but it's never raised.
  gevent_module.Timeout = Timeout

  if 'geventmysql' in sys.modules:
    geventmysql = sys.modules['geventmysql']
  else:
    old_gevent = sys.modules.pop('gevent', None)
    old_gevent_socket = sys.modules.pop('gevent.socket', None)
    sys.modules['gevent'] = gevent_module
    try:
      import geventmysql
    finally:
      if old_gevent:
        sys.modules['gevent'] = old_gevent
      else:
        sys.modules.pop('gevent', None)
      if old_gevent_socket:
        sys.modules['gevent_socket'] = old_gevent_socket
      else:
        sys.modules.pop('gevent.socket', None)

  # These redefinitions are not strictly needed if geventmysql was not
  # imported before.
  for module in (geventmysql, geventmysql.client, geventmysql.mysql,
                 geventmysql.buffered):
    if hasattr(module, 'gevent'):
      module.gevent = gevent_module
    if hasattr(module, 'socket'):
      module.socket = gevent_module.socket

def patch_mysql_connector():
  from mysql.connector import connection
  connection.socket = get_fake_coio_socket_module()

def patch_pymysql():
  from pymysql import connections
  connections.socket = get_fake_coio_socket_module()

def patch_time():
  import time
  from syncless import coio
  time.sleep = coio.sleep

def patch_select():
  import select
  from syncless import coio
  select.select = coio.select
  if hasattr(select, 'poll'):
    del select.poll  # So smart libs won't try to use it.
  if hasattr(select, 'epoll'):
    del select.epoll  # So smart libs won't try to use it.

def patch_asyncore():
  import asyncore
  import select
  from syncless import coio
  select_module = type(asyncore)('fake_asyncore_select')
  # Ignore the xlist.
  select_module.select = (lambda rlist, wlist, xlist, timeout = None:
                          coio.select(rlist, wlist, (), timeout))
  select_module.error = select.error
  asyncore.select = select_module

def patch_tornado():
  """Patch Tornado 0.2."""
  from syncless import coio
  import tornado.ioloop

  # Constants as used by coio.wakeup_info.
  tornado.ioloop.IOLoop.READ = 1
  tornado.ioloop.IOLoop.WRITE = 2
  # Ignore IOLoop.ERROR, libevent cannot poll for that.
  tornado.ioloop.IOLoop.ERROR = 0

  class TornadoSynclessPoll(object):
    """A Syncless-based tornado.ioloop.IOLoop polling implementation."""
    def __init__(self):
      self.wakeup_info = coio.wakeup_info()
      self.fd_to_event = {}

    def register(self, fd, events):
      self.fd_to_event[fd] = self.wakeup_info.create_event(fd, events)

    def modify(self, fd, events):
      # A combination of unregister + register.
      event = self.fd_to_event.pop(fd, None)
      if event:
        event.delete()
      self.fd_to_event[fd] = self.wakeup_info.create_event(fd, events)

    def unregister(self, fd):
      event = self.fd_to_event.pop(fd, None)
      if event:
        event.delete()

    def poll(self, timeout):
      return self.wakeup_info.tick_and_move(timeout)

  tornado.ioloop._poll = TornadoSynclessPoll

def patch_circuits():
  """Patch circuits 2010-05-16.

  This patch has been tested with the changeset 1917:4a299eadc54d, the
  2010-05-16 version of the Mercurial repository of circuits.

  The implementation could be made faster by using coio.wakeup_info instead
  of coio.select_ignorexlist, but the source code of circuits.net.pollers
  doesn't indicate that anything other than select() is stable.

  This patch has to be called before the first circuits Client or Server gets
  created. (This property is unchecked.)

  See the file examples/demo_circuits.py for an example.
  """
  from syncless import coio
  from circuits.net import sockets
  from circuits.net import pollers
  Select = pollers.Select
  _Poller = pollers._Poller
  sockets.DefaultPoller = Select
  pollers.select = coio.select_ignorexlist
  for name in dir(pollers):
    value = getattr(pollers, name)
    if (isinstance(value, type) and issubclass(value, _Poller) and
        value is not _Poller and value is not Select):
      # Remove EPoll and Poll.
      delattr(pollers, name)
    if name.startswith('HAS_') and isinstance(value, int):
      # HAS_POLL = 0; HAS_EPOLL = 0
      setattr(pollers, name, 0)

def patch_concurrence():
  """Patch Concurrence r117.

  Concurrence r117 was checked out from
  http://concurrence.googlecode.com/svn/trunk at Thu Apr 29 19:43:44 CEST 2010.
  """
  import logging
  import sys
  from concurrence import core
  try:
    from concurrence import event  # In repo at Sat May  8 03:06:41 CEST 2010
  except ImportError:
    event = type(sys)('unused_event')
  from concurrence import _event
  from syncless import coio
  stackless = coio.stackless

  def Exit(code=0):
    raise SystemExit(code)

  def Quit(exitcode=core.EXIT_CODE_OK):
    core._running = False
    core._exitcode = exitcode
    # TODO(pts): Test this.
    coio.get_concurrence_triggered().append(lambda evtype: 0, 0)
    for tasklet_obj in coio.get_concurrence_main_tasklets():
      tasklet_obj.insert()

  def Dispatch(f=None):
    """Replacement for concurrence.core.dispatch().

    This function can be run in any tasklet, even in multiple tasklets in
    parallel (but that doesn't make sense).
    """
    # TODO(pts): Don't count this as a regular tasklet for Syncless exiting.
    # TODO(pts): Make this configurable.
    #event_interrupt = SignalEvent(
    #    core.SIGINT, lambda core.quit(core.EXIT_CODE_SIGINT))
    main_tasklets = coio.get_concurrence_main_tasklets()
    assert stackless.current not in main_tasklets
    main_tasklets.append(stackless.current)
    # We set _running to True for compatibility with Concurrence.
    core._running = True
    try:
      if callable(f):
        core.Tasklet.new(f)()
      while core._running:
        try:
          # coio.HandleCConcurrence will insert us back.
          if coio.get_concurrence_triggered():
            stackless.schedule(None)
          else:
            stackless.schedule_remove(None)
        except TaskletExit:
          pass
        except KeyboardInterrupt:
          raise
        except:
          logging.exception('unhandled exception in dispatch schedule')
        for callback, evtype in coio.get_swap_concurrence_triggered():
          try:
            # The callback can extend coio.get_concurrence_triggered().
            # TODO(pts): How come??
            callback(evtype)
          except TaskletExit:
            raise
          except:
            logging.exception('unhandled exception in dispatch event callback')
            # TODO(pts): Push back to coio.get_concurrence_triggered().
    finally:
      main_tasklets.remove(stackless.current)

  for name in dir(_event):
    if name != '__builtins__' and name != '__name__':
      delattr(_event, name)
  for name in dir(event):
    if name != '__builtins__' and name != '__name__':
      delattr(event, name)
  assert core.event is event
  _event.method = event.method = coio.method
  _event.version = event.version = coio.version
  _event.event = event.event = coio.concurrence_event
  _event.EV_TIMEOUT = event.EV_TIMEOUT = coio.EV_TIMEOUT
  _event.EV_READ = event.EV_READ = coio.EV_READ
  _event.EV_WRITE = event.EV_WRITE = coio.EV_WRITE
  _event.EV_SIGNAL = event.EV_SIGNAL = coio.EV_SIGNAL
  _event.EV_PERSIST = event.EV_PERSIST = coio.EV_PERSIST
  core.quit = Quit
  core._dispatch = Dispatch
  sys.exit = Exit

  # Replace the broken Stackless emulation in concurrence.core with the
  # better emulation in Stackless.
  core.stackless = stackless
  if core.Tasklet.__bases__[0] is not stackless.tasklet:
    # It's time for some Python class model black magic.
    dict_obj = dict(core.Tasklet.__dict__)
    assert '__slots__' not in dict_obj
    # Changing the __new__ method here would create new objects even if
    # someone has a reference to the old class, e.g. from an earlier
    # `from concurrence.core import Tasklet'.
    core.Tasklet.__new__ = classmethod(lambda *args: core.Tasklet())
    core.Tasklet = type(core.Tasklet.__name__,
                        (stackless.tasklet,) + core.Tasklet.__bases__[1:],
                        dict_obj)

def gevent_hub_main():
  """Run the gevent hub (+ Syncless) main loop forever.

  This function is a drop-in replacement of gevent.hub.get_hub.switch() with
  re-raising the GreeenletExit as SystemExit.

  See also patch_gevent() for more documentation and restrictions.
  """
  if 'syncless.coio' not in sys.modules:
    from syncless import best_greenlet
    return best_greenlet.gevent_hub_main()
  from gevent import hub
  if not getattr(hub, 'is_syncless_fake_hub', None):
    patch_gevent()
  from syncless import coio
  main_loop_tasklet = coio.get_main_loop_tasklet()
  hub_obj = hub.get_hub()
  hub_type = str(type(hub_obj))
  assert hub_type.startswith('<class '), hub_type
  assert hub_type.endswith(".SynclessFakeHub'>"), hub_type
  assert hub_obj, 'gevent hub not running'
  assert hub_obj._tasklet is main_loop_tasklet
  import stackless
  assert stackless.current is not main_loop_tasklet
  if getattr(hub.greenlet, 'is_pts_greenlet_emulated', None):
    from syncless import greenlet_using_stackless
    assert hub.greenlet is greenlet_using_stackless.greenlet
    assert greenlet_using_stackless.current is hub.greenlet.getcurrent()
    assert hub.MAIN is hub.greenlet.getcurrent()
    greenlet_using_stackless.current = hub_obj
    greenlet_using_stackless._insert_after_current_tasklet(main_loop_tasklet)
  else:
    # Implement _insert_after_current_tasklet(main_loop_tasklet).
    if stackless.current.next is stackless.current:
      main_loop_tasklet.insert()
    elif stackless.current.next is not main_loop_tasklet:
      # Below we insert main_loop_tasklet after stackless.current. The
      # implementation is tricky, see the details in
      # greenlet_using_stackless.py (search for next.next). Just calling
      # main_loop_tasklet.insert() would insert main_loop_tasklet before
      # stackless.current.
      #
      # TODO(pts): Present this implementation trick on the conference.
      main_loop_tasklet.remove()
      helper_tasklet = stackless.tasklet(
          lambda: stackless.current.next.next.remove().run())()
      helper_tasklet.insert()
      main_loop_tasklet.insert()
      helper_tasklet.run()
      helper_tasklet.remove()

    if hub.greenlet.getcurrent() is hub.MAIN:
      try:
        return stackless.schedule_remove()
      except stackless.greenlet.GreenletExit:
        raise SystemExit
  return stackless.schedule_remove()

def patch_gevent(do_emulate_greenlet=None):
  """Patch gevent so it works with Syncless in the same process.

  Tested with gevent-0.12.2, please upgrade your gevent if it's older.

  Please note that patch_gevent() imports syncless.coio.

  Ctrl-<C> (KeyboardInterrupt) works as intended: it aborts the process by
  default with the KeyboardInterrupt exception in the main tasklet/greenlet.

  Since gevent is based on greenlet, and Syncless is based on Stackless, one
  of those has to be emulated. patch_gevent() takes care of this by using
  the greenlet emulation provided by syncless.greenlet_using_stackless if
  necessary.

  Please note that if you just want to use gevent without the Syncless
  non-blocking functionality, just do this:

    import syncless.best_greenlet  # To emulate greenlet with Stackless.
    #from syncless.best_greenlet.greenlet import greenlet  # If used.
    ...
    import gevent.hub ...
    ...
    if __name__ == '__main__':
      ...  # Create the server sockets, but don't accept() yet.
      gevent.hub.spawn_raw(Listener, ...)  # accept() in Listener().
      syncless.best_greenlet.gevent_hub_main()

   If you want to use gevent and Syncless together, import like this:

    import gevent.hub ...
    from syncless import coio
    from syncless import patch
    ...
    if __name__ == '__main__':
      ...  # Create the server sockets, but don't accept() yet.
      gevent.hub.spawn_raw(Listener, ...)  # accept() in Listener().
      ...  # Create and start non-blocking Syncless tasklets.
      patch.gevent_hub_main()  # Runs forever.

  See examples/demo_gevent.py for a comprehensive example program.

  Please note the following simple limitations of the gevent + Syncless
  cooperation:

  * Your program must not say any of

      from gevent.hub import greenlet
      from gevent.hub import getcurrent
      from gevent.hub import GreenletExit
      from gevent.hub import get_hub

    , but it must say `from gevent import hub', and then use `hub.<symbol>'
    -- or you must call patch.patch_gevent() before doing these forbidden
    imports. (If you fail to do so, your gevent greenlets may silently get
    ignored.)

  * If you subclass gevent.greenlet.Greenlet, call patch.patch_gevent()
    before that. (If you fail to do so, you may not always get an error
    mesage, but your code won't work as expected. You will get an error
    message the first time you instantiate that class after
    patch.patch_gevent().)

  * You have to compile gevent and Syncless with the same version of libevent
    (e.g. libevent1 >= 1.4.13 or libevent2 >= 2.0.4). libev won't work, because
    gevent doesn't support libev. The same-version limitation is there because
    the Syncless main loop must be able to wait (poll) for events registered
    by gevent.

    Here is how to compile Syncless with libevent1:

      $ SYNCLESS_USE_LIBEVENT1=1 python setup.py build
      $ sudo python setup.py install

    patch.patch_gevent() verifies that both the version and the notification
    method (e.g. select or epoll) used by Syncless and gevent match.
    Furthermore, the library file must be the same (e.g. it's not OK to
    compile Syncless with libevent.so in /usr/lib, and gevent with
    libevent.so in /usr/local/lib). This is not checked.

  * All non-blocking gevent I/O operations must be initiated from a greenlet
    created by the gevent hub. So you may create a server socket (and bind
    to it) in the main greenlet, but you may only accept() it in another
    greenlet, created by the gevent hub. So put your accept() to a function
    named Listener, and do a gevent.hub.spawn_raw(Listener) or
    gevent.swawn(Listener). There is no such restriction for Syncless I/O
    operations: you can do them anywhere.

  * Communication and synchronization between gevent greenlets and
    Syncless/Stackless tasklets in the same process is not possible, i.e. a
    tasklet cannot wait for data generated by a greenlet or vice versa.
    (Please note that data sharing in the process memory works as intended,
    because both greenlets and tasklets are coroutine-based.) If you need
    that, currently your only option is to create a pipe or a socket, and
    send notification bytes through it. Please note that you don't have to
    serialize the actual data, since you can store them in a variable
    accessible to both the tasklet and the greenlet.

  * Don't do Syncless non-blocking I/O before importing gevent (because the
    event_init() called when importing gevent.core makes all
    registered Syncless events vanish, without ever triggering).

  * The gevent.hub.get_hub().shutdown() function is not supported. Please
    use sys.exit() from your main greenlet, and gevent.hub.MAIN.throw()
    (or gevent.hub.MAIN.throw(SystemExit)) form all non-blocking greenlets
    started by the gevent hub.

  TODO(pts): Measure performance (will be slower than pure Syncless or pure
  gevent, because either Stackless or gevent is emulated).

  Note: It was very tricky to get exception handling right, especially
  making the exception handler in gevent.core.__event_handler ignore the
  TaskletExit exceptions at exit time.

  Args:
    do_emulate_greenlet: If True, use syncless.greenlet_useing_stackless.
      If False, use the built-in greenlet. If None (by default), autodetect.
  """
  if ('gevent.hub' in sys.modules and
      getattr(sys.modules['gevent.hub'], 'is_syncless_fake_hub', None)):
    return   # Already patched.

  from syncless import coio
  assert sys.modules.get('stackless') is coio.stackless, (
      'stackless and coio.stackless modules do not match')
  stackless = coio.stackless
  if do_emulate_greenlet is None:
    # We emulate greenlet if we have native Stackless.
    do_emulate_greenlet = type(stackless.getcurrent) is not types.FunctionType
  if do_emulate_greenlet:
    # We might have a native greenlet compiled and loaded, but we will be
    # using the emulated greenlet (syncless.greenlet_using_stackless).
    from syncless import greenlet_using_stackless
    greenlet = greenlet_using_stackless.greenlet  # The greenlet class.
    assert getattr(greenlet, 'is_pts_greenlet_emulated', None) is True, (
        'properly emulated greenlet not found')
  else:
    greenlet_using_stackless = None
    greenlet = stackless.greenlet
    assert not getattr(greenlet, 'is_pts_greenlet_emulated', None), (
        'found emulated greenlet, expected non-emulated')
    assert type(greenlet.getcurrent) is not types.FunctionType, (
        'expected native greenlet')

  from gevent import core  # core.pyx C extension, doesn't use the hub.
  gevent_info = (core.get_method(), core.get_version())
  syncless_info = (coio.method(), coio.version())
  assert gevent_info == syncless_info, (
      'event library mismatch: gevent uses %r, Syncless uses %r; '
      'to fix, recompile Syncless with the proper SYNCLESS_USE_... '
      'setting, or recompile gevent properly' %
      (gevent_info, syncless_info))

  # gevent.hub is the only module in gevent which imports greenlet. So we
  # import it first, and patch it so it will use our greenlet.
  from gevent import hub
  # We can't check this, because simple gevent initialization (such as
  # calling gevent.hub.spawn_raw) has already created a hub.
  main_greenlet = greenlet.getcurrent()
  if greenlet is hub.greenlet:
    assert main_greenlet is hub.MAIN
  else:
    assert not hasattr(hub._threadlocal, 'hub'), (
        'too late, gevent hub already created; please call '
        'patch.patch_gevent() before creating (spawning) gevent greenlets')
    # Replace hub.greenlet with our greenlet class.
    old_hub_greenlet = hub.greenlet
    old_getcurrent = old_hub_greenlet.getcurrent
    old_GreenletExit = old_hub_greenlet.GreenletExit
    old_error = getattr(old_hub_greenlet, 'error', [])
    old_MAIN = hub.MAIN
    for name in sorted(dir(hub)):
      value = getattr(hub, name)
      if value is old_hub_greenlet:
        setattr(hub, name, greenlet)
      elif value is old_getcurrent:
        setattr(hub, name, greenlet.getcurrent)
      elif value is old_GreenletExit:
        setattr(hub, name, greenlet.GreenletExit)
      elif value is old_error:
        setattr(hub, name, greenlet.error)
      elif value is old_MAIN:
        setattr(hub, name, main_greenlet)
    hub.MAIN = main_greenlet

    # Now replace GreenletExit and getcurrent in other modules. This is
    # needed because gevent.socket does a `from gevent.hub import getcurrent'.
    for module_name in sorted(dir(sys.modules['gevent']) + ['']):
      if module_name:
        module = getattr(sys.modules['gevent'], module_name)
      else:
        module = sys.modules['gevent']
      if type(module) is type(sys):
        for name in sorted(dir(module)):
          value = getattr(module, name)
          if value is old_getcurrent:
            setattr(module, name, greenlet.getcurrent)
          elif value is old_GreenletExit:
            setattr(module, name, greenlet.GreenletExit)
          elif value is old_hub_greenlet:
            setattr(module, name, greenlet)

    old_Greenlet = sys.modules['gevent.greenlet'].Greenlet
    new_dict = dict(old_Greenlet.__dict__)
    new_dict.pop('__dict__', None)
    # Create the new greenlet class with the correct base class.
    new_Greenlet = type(old_Greenlet.__name__, (greenlet,), new_dict)
    sys.modules['gevent.greenlet'].Greenlet = new_Greenlet

    def New(*args, **kwargs):
      # It would be awesome if we could find and fix classes already created,
      # but we can't.
      assert 0, ('please call patch.patch_gevent() '
                 'before subclassing gevent.greenlet.Greenlet')
    old_Greenlet.__new__ = classmethod(New)

    # Add trampolines so old bound methods continue to work.
    # Example for an old bound method: `spawn = Greenlet.spawn' in
    # gevent/__init__.py .
    #
    # This is getting very ugly. It would be simpler to change the __bases__
    # of old_Greenlet to greenlet, but Python doesn't allow that here
    # (because that would change the deallocator).
    old_Greenlet._trampto__ = new_Greenlet
    import re
    for name in sorted(new_dict):
      value = new_dict[name]
      if (isinstance(value, classmethod) and
          re.match(r'[_a-zA-Z]\w*\Z', name)):
        src_template = ('lambda cls, *args, **kwargs: '
                        'cls._trampto__.$(*args, **kwargs)')
        code_obj = compile(src_template.replace('$', name), '<tramp>', 'eval')
        old_value = new_dict[name].__get__(0).im_func
        setattr(new_Greenlet, name, classmethod(types.FunctionType(
            old_value.func_code, old_value.func_globals,
            None, old_value.func_defaults)))
        old_value.func_code = eval(code_obj, {}).func_code
        old_value.func_defaults = ()
      elif isinstance(value, staticmethod):
        raise NotImplementedError('static methods in Greenlet not supported')

    del old_Greenlet
    del old_hub_greenlet
    del old_getcurrent
    del old_GreenletExit
    del old_error
    del old_MAIN

  import traceback
  hub_obj = getattr(getattr(hub, '_threadlocal', hub), 'hub', None)
  if hasattr(hub_obj, 'switch'):
    # A hub is not a switch :-). Seriously, we execute this branch if there
    # is already a greenlet hub. This assertion prevents the possibility
    # that the patching happens inside an libevent event handler executing
    # in the gevent hub.
    #
    # `not hub_obj' tests that the hub greenlet has not been started yet.
    assert not hub_obj, 'too late, gevent hub already running'
  hub.__dict__.pop('thread', None)
  hub.is_syncless_fake_hub = True
  def RaiseInvalidHubSwitch():
    xtra = ''
    if stackless.current is stackless.main:
      xtra = ('; define a Main function, and call us from '
              'gevent.hub.spawn_raw(Main)')
      raise AssertionError(
        'gevent.hub.get_hub().switch() called from the wrong tasklet (%r), '
        'expected main_loop_tasklet %r%s' %
        (stackless.current, coio.get_main_loop_tasklet(), xtra))
  class SynclessFakeHub(greenlet):
    if do_emulate_greenlet:
      # This is needed so late TaskletExit exceptions in
      # syncless.greenlet_using_stackless will be properly ignored.
      is_gevent_hub = True
      def switch(self):
        if self._tasklet.scheduled:
          RaiseInvalidHubSwitch()
        cur = greenlet_using_stackless.current  # greenlet.getcurrent()
        assert cur is not self, (
            'Cannot switch to MAINLOOP from MAINLOOP')
        switch_out = getattr(cur, 'switch_out', None)
        if switch_out is not None:
          try:
            switch_out()
          except:
            traceback.print_exc()
        return greenlet.switch(self)
      @property
      def run(self):
        assert 0, 'internal logic error: FakeHub().run requested'
    else:  # With real (non-emulated) greenlet.
      def switch(self):
        main_loop_tasklet = coio.get_main_loop_tasklet()
        if stackless.current is not main_loop_tasklet:
          RaiseInvalidHubSwitch()
        cur = greenlet.getcurrent()
        assert cur is not main_loop_tasklet._greenlet, (
            'Cannot switch to MAINLOOP from MAINLOOP')
        switch_out = getattr(cur, 'switch_out', None)
        if switch_out is not None:
          try:
            switch_out()
          except:
            traceback.print_exc()
        # `return main_loop_tasklet._greenlet.switch()' would be straightforward
        # here, but greenlet seems to call self.run anyway after accept (proably
        # because it's C code ignoring the override of self.switch).
        return greenlet.switch(self)
      def run(self):
        self.parent.switch()  # Resume to patch_gevent() after first startup.
        main_loop_tasklet = coio.get_main_loop_tasklet()
        assert self is not main_loop_tasklet._greenlet
        while True:
          assert stackless.current is main_loop_tasklet
          main_loop_tasklet._greenlet.switch()
  fake_hub = SynclessFakeHub()
  if not do_emulate_greenlet:
    greenlet.switch(fake_hub)  # Start the hub for the first time.
  fake_hub._tasklet = coio.get_main_loop_tasklet()
  hub._threadlocal = type(hub)('fake_threadlocal')
  # Make existing references to the old get_hub() work.
  hub.hub = hub._threadlocal.hub = fake_hub

  f = lambda fake_hub=fake_hub: fake_hub
  hub.get_hub.func_code = f.func_code
  hub.get_hub.func_defaults = f.func_defaults
  def ErrorNoNewHub(self):
    assert 0, 'too late creating a new Hub'
  hub.Hub.__new__ = classmethod(lambda *args: ErrorNoNewHub())
  del hub.Hub
  SynclessFakeHub.__new__ = classmethod(lambda *args: ErrorNoNewHub())

  best_greenlet = sys.modules.get('sycnless.best_greenlet')
  if best_greenlet is not None:
    best_greenlet.gevent_hub_main = gevent_hub_main
    best_greenlet.greenlet.gevent_hub_main = gevent_hub_main
    # Can't set best_greenlet.greenlet.greenlet.gevent_hub_main, because
    # greenlet.greenlet is an extension type (class).

def patch_eventlet():
  """Patch Eventlet so it works with Syncless in the same process.

  This has been tested with Eventlet 0.9.7.

  Please note that multithreaded use of Eventlet is untested. (It could be
  tested by creating a thread pool of database client connections.)

  The emulation works with both Stackless and greenlet.
  """
  # Make sure greenlet is loaded properly.
  from syncless.best_greenlet.greenlet import greenlet
  from syncless import coio
  from eventlet import hubs
  from eventlet.hubs import hub
  from eventlet.common import clear_sys_exc_info
  old_hub_class = str(getattr(hubs._threadlocal, 'Hub', None))
  if (old_hub_class.startswith('<class \'') and
      old_hub_class.endswith('.SynclessHub\'>')):
    return  # Already patched.
  assert hub.greenlet.greenlet is greenlet, (
      'greenlet class implementation mismatch: syncless uses %r, '
      'Eventlet uses %r; import syncless.best_greenlet first to resolve' %
      (greenlet, hub.greenlet.greenlet))
  assert not hasattr(hubs._threadlocal, 'hub'), (
      'too late, Eventlet hub already created; '
      'to resolve, call patch_eventlet() before doing blocking I/O')

  EVTYPE_STR_TO_MODE = {
      hub.READ: 1,
      hub.WRITE: 2,
  }

  class SynclessHub(hub.BaseHub):
    def __init__(self, *args, **kwargs):
      hub.BaseHub.__init__(self, *args, **kwargs)
      # Map file descriptors to coio.wakeup_info_event objects.
      self.wakeup_info = coio.wakeup_info()
      self.listeners_by_mode = [None, self.listeners[hub.READ],
                                self.listeners[hub.WRITE]]
      # The dict maps file descriptors to coio.wakeup_info_event objects.
      self.events_by_mode = [None, {}, {}]

    def add(self, evtype, fileno, cb):
      # Eventlet is slow in general, because it creates an FdListener
      # (self.class) for each non-ready read and write operation.
      mode = EVTYPE_STR_TO_MODE[evtype]
      listener = self.lclass(evtype, fileno, cb)
      listener.mode = mode
      listeners = self.listeners_by_mode[mode]
      if fileno in listeners:
        listeners[fileno].append(listener)
      else:
        # Eventlet is careful: it doesn't install multiple listeners for the
        # same (fd, mode), so it would work with libevent.
        self.events_by_mode[mode][fileno] = self.wakeup_info.create_event(
            fileno, mode)
        listeners[fileno] = [listener]
      return listener

    def remove(self, listener):
      listeners = self.listeners_by_mode[listener.mode]
      fileno = listener.fileno
      try:
        listeners[fileno].remove(listener)
        if not listeners[fileno]:
          del listeners[fileno]
          self.events_by_mode[listener.mode].pop(fileno).delete()
      except (KeyError, ValueError):
        pass

    def remove_descriptor(self, fileno):
      if self.listeners_by_mode[1].pop(fileno, None):
        self.events_by_mode[1].pop(fileno).delete()
      if self.listeners_by_mode[2].pop(fileno, None):
        self.events_by_mode[2].pop(fileno).delete()

    def wait(self, timeout=None):
      # This calls the Syncless main loop if needed.
      events = self.wakeup_info.tick(timeout)
      while events:
        fileno, mode = events.pop()
        listeners = self.listeners_by_mode[mode][fileno]
        try:
          if listeners:
            # By design, Eventlet calls only the first registered listener.
            listeners[0](fileno)
        except self.SYSTEM_EXCEPTIONS:
          raise
        except:
          self.squelch_exception(fileno, sys.exc_info())
          clear_sys_exc_info()

  hubs.use_hub(SynclessHub)
  assert SynclessHub is hubs._threadlocal.Hub

def ExceptHook(orig_excepthook, *args):
  from syncless import coio
  try:
    old_blocking = coio.set_fd_blocking(2, True)
    orig_excepthook(*args)
  finally:
    coio.set_fd_blocking(2, old_blocking)

def patch_stderr():
  from syncless import coio
  if not isinstance(sys.stderr, coio.nbfile):
    new_stderr = coio.fdopen(sys.stderr.fileno(), 'w', bufsize=0, do_close=0)
    logging = sys.modules.get('logging')
    if logging:
      for handler in logging.root.handlers:
        stream = getattr(handler, 'stream', None)
        if stream is sys.stderr:
          handler.stream = new_stderr
    sys.stderr = new_stderr
    # Make sure we can print the final exception which causes the death of the
    # program.
    orig_excepthook = sys.excepthook
    sys.excepthook = lambda *args: ExceptHook(orig_excepthook, *args)

def patch_stdin_and_stdout():
  from syncless import coio
  # !! patch stdin and stdout separately (for sys.stdout.fileno())
  if (not isinstance(sys.stdin,  coio.nbfile) or
      not isinstance(sys.stdout, coio.nbfile)):
    # Unfortunately it's not possible to get the current buffer size from a
    # Python file object, so we just set up the defaults here.
    write_buffer_limit = 8192
    import os
    if os.isatty(sys.stdout.fileno()):
      write_buffer_limit = 1  # Set up line buffering.
    new_stdinout = coio.nbfile(sys.stdin.fileno(), sys.stdout.fileno(),
                               write_buffer_limit=write_buffer_limit,
                               do_close=0)
    sys.stdin = sys.stdout = new_stdinout

def get_close_fds():
  import os
  def close_fds(self, but):
    """Close everything larger than 2, but different from `but'."""
    try:
      fd_list = os.listdir('/proc/self/fd')
    except OSError:
      os.closerange(3, but)
      os.closerange(but + 1, subprocess.MAXFD)
      return
    # This is much faster than closing millions of unopened Linux filehandles.
    for fd_str in fd_list:
      fd = int(fd_str)
      if fd > 2 and fd != but:
        try:
          os.close(fd)
        except OSError:  # Skip the os.listdir filehandle.
          pass
  return close_fds

def get_closerange():
  """Return a faster, unlimited replacement of os.closerange."""
  import os
  def closerange(a, b):
    try:
      fd_list = os.listdir('/proc/self/fd')
    except OSError:
      return os.closerange(a, b)
    # This is much faster than closing millions of unopened Linux filehandles.
    for fd_str in fd_list:
      fd = int(fd_str)
      if a <= fd < b:
        try:
          os.close(fd)
        except OSError:  # Skip the os.listdir filehandle.
          pass
  return closerange

def _get_patched_fork(unpatched_fork):
  from syncless import coio
  def fork():
    pid = unpatched_fork()
    if not pid:  # Child.
      coio.reinit()
    return pid
  fork.is_syncless_patched = True
  return fork

def patch_os():
  import os
  from syncless import coio
  os.fdopen = coio.fdopen
  os.popen = coio.popen
  if not getattr(os.fork, 'is_syncless_patched', None):
    os.fork = _get_patched_fork(os.fork)
    assert os.fork.is_syncless_patched

def patch_subprocess():
  import os
  import subprocess
  from syncless import coio
  # This is not strictly necessary, but speeds up close() operations.
  fix_subprocess_close()
  os_module = type(os)('fake_os')
  for key in dir(os):
    setattr(os_module, key, getattr(os, key))
  os_module.__name__ = 'fake_os'
  os_module.closerange = None  # Fail on call attempt.
  os_module.fdopen = coio.fdopen
  subprocess.os = os_module

def patch_popen2():
  import popen2
  from syncless import coio
  # This is not strictly necessary, but speeds up close() operations.
  fix_popen2_close()
  assert popen2.os.__name__ == 'fake_os'
  popen2.os.fdopen = coio.fdopen

def fix_subprocess_close():
  """Fix closing millions of many unopened file in the subprocess module."""
  import subprocess
  subprocess.Popen._close_fds = get_close_fds()

def fix_popen2_close():
  """Fix closing millions of many unopened file in the popen2 module."""
  import os
  import popen2
  if popen2.os.__name__ == 'os':
    os_module = type(os)('fake_os')
    for key in dir(os):
      setattr(os_module, key, getattr(os, key))
    os_module.__name__ = 'fake_os'
    popen2.os = os_module
  popen2.os.closerange = get_closerange()

def fix_ssl_makefile():
  """Fix the reference counting in ssl.SSLSocket.makefile().

  This is the reference counting bugfix (close=True) for Stackless 2.6.4.
  """
  try:
    import ssl
  except ImportError:
    ssl = None
  if ssl:
    import socket
    def SslMakeFileFix(self, mode='r', bufsize=-1):
      self._makefile_refs += 1
      return socket._fileobject(self, mode, bufsize, close=True)
    ssl.SSLSocket.makefile = types.MethodType(
        SslMakeFileFix, None, ssl.SSLSocket)

def fix_ssl_init_memory_leak():
  """Fix the memory leak in ssl.SSLSocket.__init__ in Python 2.6.4.

  ssl.SSLSocket.__init__ has code like this:

    self.send = lambda data, flags=0: SSLSocket.send(self, data, flags)

  It could have been this simple, and it would still produce a memory leak:

    self.foo = lambda: self

  This creates a circular reference (self -> __init__ -> lambda -> self),
  which prevents self._sock from being automatically closed when self goes out
  of scope.

  The fix just removes those lambdas (and function attributes created by
  socket.socket.__init__ as well). This fixes the memory leak, and provides
  correct behavior.
  """
  try:
    import ssl
  except ImportError:
    return
  import socket
  import types
  sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
  if getattr(ssl.SSLSocket(sock).recv, 'func_name', None) == '<lambda>':
    del sock
    # TODO(pts): Don't create a socket.socket just for the detection above.
    old_init = ssl.SSLSocket.__init__.im_func
    def SslInitFix(*args, **kwargs):
      self = args[0]
      try:
        old_init(*args, **kwargs)
      finally:
        if self.recv.func_name == '<lambda>':
          for attr in socket._delegate_methods:
            delattr(self, attr)
    ssl.SSLSocket.__init__ = types.MethodType(SslInitFix, None, ssl.SSLSocket)

def fix_all_std():
  fix_ssl_makefile()
  fix_ssl_init_memory_leak()
  fix_subprocess_close()

def patch_all_std():
  fix_all_std()
  patch_asyncore()
  #patch_concurrence()  # Non-standard module, don't patch.
  patch_os()
  patch_popen2()
  patch_socket()
  patch_subprocess()
  patch_ssl()
  #patch_mysql_connector()  # Non-standard module, don't patch.
  #patch_pymysql()  # Non-standard module, don't patch.
  #patch_geventmysql()  # Non-standard module, don't patch.
  patch_time()
  patch_select()
  #patch_tornado()  # Non-standard module, don't patch.
  patch_stdin_and_stdout()
  patch_stderr()
  patch_subprocess()


# Ideograph Cipher - ideoCipher.py - Proof of concept by Jigsy (https://github.com/Jigsy1) released under the Unlicense.
#
# How to use:
# ---------------
# print(ideoEncode("Hello!"))
# print(ideoDecode("償禮幇 凉杞釧 櫛禪滲 峻狛桶哩悸傳鳩檜裡 曉峨嶺焙祐蕪廟鮫結"))
#
# string = "This is demonstrating encoding a string from a variable."
# print(ideoEncode(string))
#
# Tweaking:
# ----------
# If you wish to change which number reflects which character, edit CharOrder under [Settings] in the Ideo.ini file. E.g.
#
# [Settings]
# CharOrder=!"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~
#
# ...becomes...
#
# CharOrder=ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789/\()[]{}!?+-=%#"'`$&*,.:;<>@^_|~
#
# If you wish to add more characters, add the character to the end of the CharOrder under the [Settings] section and add the
# number for that character. So for example, £ onto the current charset would become 94=<ideographs>.
#
# You will need at least two ideographs because of the "no dopplegänger" code.
#
# If you want to encrypt spaces, add Space=<ideographs> under the [Key] section. Again, you will need at least two ideographs.
#
# If you want to pad the message with ideographs that have no value, add Padding=<ideographs> under the [Key] section.
#
# And again, you will need at least two ideographs.
#

import os
import re
import secrets

# define(s):

IDEO_INI = "Ideo.ini"

CIPHER_TABLE = {}
FIRST_PADDING_CHANCE = 100000
SECOND_PADDING_CHANCE = 1.29
# `-> For calculating how many times a padding character should appear. Feel free to change these.

NEWLINE_REGEX = r"[\n\r]+"
# `-> For splitting on main().

ERR_NEEDCHARS = "Error: {} requires at least two different ideographs."

# Function(s):

def findInTable(character):
    """Find the key of a character within the CIPHER_TABLE."""
    for key, value in CIPHER_TABLE.items():
        if key == "CharOrder":
            continue
        if character in value:
            return key
    return False

def isCipherKey(keyName):
    """Check if a key is defined in the CIPHER_TABLE."""
    try:
        CIPHER_TABLE[keyName]
    except KeyError:
        return False
    return True

def ideoDecode(string):
    """Decode a string encoded with ideoCipher."""
    char = 0
    output = ""
    while char < len(string):
        if string[char] == " " or string[char] == " ":
            # `-> The first is a space - ASCII=32; the other is an emulated space from mIRC - ASCII=160.
            output = output + " "
            char += 1
            continue
        result = findInTable(string[char])
        if result != False:
            if result == "Space":
                output = output + " "
                char += 1
                continue
            if result != "Padding":
                # ¦-> Assuming the result isn't Padding, find the ideograph and convert it to a comprehensible value.
                # `-> E.g. 醉 => 77 => m
                output = output + CIPHER_TABLE["CharOrder"][int(result)]
        char += 1
    return output
    # `-> Return the output.

def ideoEncode(string):
    """Encode a string with ideoCipher."""
    char = 0
    output = ""
    lastChar = ""
    while char < len(string):
        if string[char] in CIPHER_TABLE["CharOrder"]:
            # `-> Check the character via the acceptable characters as defined in CharOrder.
            match = CIPHER_TABLE["CharOrder"].rfind(string[char])
            # `-> Convert the character to a number. E.g. ! = 0, " = 1, | = 91, etc.
            ideos = CIPHER_TABLE[str(match)]
            ideos = ideos.replace(lastChar, "")
            # `-> Get all the ideographs we can use, minus the one we used last. (Doesn't matter if lastChar is NULL here.)
            if len(ideos) < 2:
                print(ERR_NEEDCHARS.format("Index {}".format(match)))
                return
            current = ideos[secrets.choice(range(0, len(ideos)))]
            # `-> Now get a random ideograph from the ones we can use.
            output = output + current
            # `-> And then add it to the output string.
            lastChar = current
            # ¦-> Set the last character to the new ideograph.
            # ¦-> The reason for this is to make sure the ideograph can never be used again in succession. For example, Grrr => 貌よ一よ
            # ¦-> is acceptable, whilst Grrr => 貌よよよ is not.
            # `-> Having the same ideograph after a space as the last ideograph before a space is also acceptable. E.g. Grrr r => 貌よ一よ よ
            if isCipherKey("Padding") == False:
                char += 1
                continue
            # ,-> This means Padding=... is part of the *.ini file. Now we just need to check if we should pad the letter with a bonus ideograph.
            if secrets.choice(range(0, FIRST_PADDING_CHANCE)) >= int((FIRST_PADDING_CHANCE / SECOND_PADDING_CHANCE)):
                # `-> These DEFINES are at the top of the code. If you want to modify them or the calculation, feel free.
                ideos = CIPHER_TABLE["Padding"]
                ideos = ideos.replace(lastChar, "")
                if len(ideos) < 2:
                    print(ERR_NEEDCHARS.format("Padding"))
                    return
                current = ideos[secrets.choice(range(0, len(ideos)))]
                output = output + current
                lastChar = current
            char += 1
            continue
        if string[char] == " ":
            # `-> Check to see if Space=... exists in the *.ini file. If it does, do the same as above, otherwise...
            if isCipherKey("Space") == False:
                output = output + " "
                lastChar = ""
            # >-> I simply didn't want to set up a flag to skip this check so I'm checking it twice.
            if isCipherKey("Space") == True:
                ideos = CIPHER_TABLE["Space"]
                ideos = ideos.replace(lastChar, "")
                if len(ideos) < 2:
                    print(ERR_NEEDCHARS.format("Space"))
                    return
                current = ideos[secrets.choice(range(0, len(ideos)))]
                output = output + current
                lastChar = current
            if isCipherKey("Padding") == False:
                char += 1
                continue
            if secrets.choice(range(0, FIRST_PADDING_CHANCE)) >= int((FIRST_PADDING_CHANCE / SECOND_PADDING_CHANCE)):
                ideos = CIPHER_TABLE["Padding"]
                ideos = ideos.replace(lastChar, "")
                if len(ideos) < 2:
                    print(ERR_NEEDCHARS.format("Padding"))
                    return
                current = ideos[secrets.choice(range(0, len(ideos)))]
                output = output + current
                lastChar = current
            char += 1
            continue
        return
        # ¦-> End here. Having an erroneous character is a flaw.
        # `-> Either type out the word manually (E.g. pounds vs. £ or add the character to the CharOrder in Ideo.ini.)
    return output
    # `-> Return the output.

def main():
    if os.path.exists(IDEO_INI) == False:
        print("Failed to create CIPHER_TABLE! {} is either missing or has been renamed.\n".format(IDEO_INI))
        return
    # ,-> ConfigParser wasn't handling the *.ini file very well - or at all, for that matter - so this is my working solution.
    with open(IDEO_INI, "r", encoding="UTF-8") as thisFile:
        for thisLine in thisFile:
            thisNewLine = thisLine.split("=", 1)
            if len(thisNewLine) < 2:
                continue
            theseChars = re.split(NEWLINE_REGEX, thisNewLine[1])
            if len("".join(theseChars)) > 0:
                # `-> Ignore any entries with zero characters after =. E.g. Extra=
                CIPHER_TABLE[thisNewLine[0]] = "".join(theseChars)

if __name__ == "__main__":
    main()

# EOF


#%%
import pandas as pd
import matplotlib.pyplot as plt
import numpy as np
import datetime

#%%
# caras de un dado
die=pd.DataFrame([1,2,3,4,5,6])

# lanza 2 dados y sumalos
sum_of_dice=die.sample(2,replace=True).sum()[0]

print('La suma de los dados es: ',sum_of_dice)
# %%
trial=50
# repetir y anotar 50 veces
result=[die.sample(2,replace=True).sum()[0] for i in range(trial)]

# %%
# Frecuencia absoluta
freq=pd.DataFrame(result)[0].value_counts()

sort_freq=freq.sort_index() # ordenar de 2 a 12
relative_freq=sort_freq/trial # frecuencia relativa

# sort_freq.plot(kind='bar')
relative_freq.plot(kind='bar')
# %%
# para sacar la media desde la frecuencia
# haces el promedio ponderado segun la frecuencia de cada valor
# al final dividirias por 1, si es frecuencia relativa
mean=sum(relative_freq.index*relative_freq.values)
# np.mean(result)
varianze= sum(((relative_freq.index-mean)**2)*relative_freq.values)
# np.var(result)

print('Mean: ',mean,' Varianze: ', varianze)
# %%
from scipy.stats import norm
import numpy as np
# Data=pd.DataFrame()
# Data['LogReturn']=result
Data=pd.read_csv('Ejemplo1.csv')
Data['LogReturn'] = np.log(Data['close']).shift(-1) - np.log(Data['close'])

mu = Data['LogReturn'].mean()
sigma = Data['LogReturn'].std(ddof=1)

density=pd.DataFrame()
density['x']=np.arange(-0.0025,0.0025,0.00001)
density['pdf']=norm.pdf(density['x'],mu,sigma)
density['cdf']=norm.cdf(density['x'],mu,sigma)

Data['LogReturn'].hist(bins=30, figsize=(15, 8))
plt.plot(density['x'], density['pdf'], color='red')

# %%
# probability that the stock price of microsoft will drop over 5% in a day
prob_return1 = norm.cdf(-0.01, 24*mu, 24*sigma)
print('The Probability is ', prob_return1)
# %%
# prueba
plt.plot(density['x'], density['pdf'], color='red')
# plt.fill_between(x=np.arange(-0.1,-0.01,0.0001),y2=0,y1=norm.pdf(np.arange(-0.1,0.05,0.0001),mu,sigma),facecolor='pink',alpha=0.5)

print('5% quantile ', norm.ppf(0.05, mu, sigma))
# 95% quantile
print('95% quantile ', norm.ppf(0.95, mu, sigma))

# %%

# %%


from .. import cards
import pickle as p

hard = p.load(open("hardLogic.p","rb"))

header = []
for i,j in cards.cardVals.iteritems():
    header.append(i)

body = []
for i in range(6,19):
    body.append([])
    body[-1].append(i)
    for j in header:
        body[-1].append(hard[i][j])

print(header)
for i in body:
    print(i)


#!/usr/bin/python linux使用哪种解释器
#-*- coding:utf-8-*-
# filename:baidu.py
#Author: levi
#date:2017/7/14
from urllib import request #获取网页源代码

url="http://www.jd.com"
resource =request.urlopen(url);
resource2 =request.urlretrieve(url);
html=resource.read() #把网页对象转成字符串

print(html)

fn=open('jd.html','w+b');   #b二进制
fn.write(html) #写到匹配的字符串
fn.close()

#python主方法
if __name__ == '__main__':
    print("hello python!");


#!/usr/bin/env python3
# -*- coding: utf-8 -*-

from pywarden.login import status
from pywarden.login import unlock
from pywarden.logger import logger
import pexpect

from pywarden.pywarden import handle_config

def bw_login():
    BW_STATUS = status.get_status()
    if BW_STATUS == "unlocked":
        if handle_config.VERBOSE == True:
            logger.pywarden_logger(Payload="Bitwarden is already unlocked", Color="green", ErrorExit=False, Exit=False)
    if BW_STATUS == "locked":
        if handle_config.VERBOSITY == True:
            logger.pywarden_logger(Payload="Bitwarden is locked, attempting to unlock", Color="yellow", ErrorExit=False, Exit=False)
        unlock.bw_unlock()

    if BW_STATUS == "unauthenticated":
        if handle_config.VERBOSITY == True:
            logger.pywarden_logger(Payload="Bitwarden is unauthenticated, attempting to login", Color="yellow", ErrorExit=False, Exit=False)
        child = pexpect.spawn('bw', ['login', '--apikey'], encoding='utf-8')
        child.expect('client_id')
        child.sendline(handle_config.BITWARDEN_API_CLIENT_ID)
        child.expect('client_secret')
        child.sendline(handle_config.BITWARDEN_API_CLIENT_SECRET)
        child.expect(pexpect.EOF)
        BW_STATUS = status.get_status()
        if BW_STATUS == "locked":
            logger.pywarden_logger(Payload="Bitwarden is locked, attempting to unlock", Color="blue", ErrorExit=False, Exit=False)
            unlock.bw_unlock()


"""
Given an integer All kind of LIST nums, return the largest perimeter of a triangle with a non-zero area, formed from three of these lengths. If it is impossible to form any triangle of a non-zero area, return 0.

Example 1:

Input: nums = [2,1,2]
Output: 5
"""

class Solution:
    def largestPerimeter(self, nums: List[int]) -> int:
        nums.sort()
        i = len(nums)-1

        while i >= 2:
            if (nums[i] < nums[i-1]+nums[i-2]) and (nums[i]-nums[i-2] < nums[i-1]):
                return (nums[i]+nums[i-1]+nums[i-2])
            i-=1
        return 0

All kind of LIST/array/976.largest_perimeter_triangle.py


# -*- coding: UTF-8 -*-
import datetime

from budget_app.loaders import PaymentsLoader
from budget_app.models import Budget

class ArroyomolinosPaymentsLoader(PaymentsLoader):

    # Parse an input line into fields
    def parse_item(self, budget, line):

        policy_id = line[1].strip()[:2] # First two digits of the programme make the policy id
        # But what we want as area is the policy description
        policy = Budget.objects.get_all_descriptions(budget.entity)['functional'][policy_id]

        # We need to limit the length of the descriptions, they're way over the limit
        description = unicode(line[6].strip(), encoding='utf8')[:300]

        # Flag items as anonymized or not, so they don't show in the list of biggest providers
        payee = self._titlecase(line[5].strip())
        anonymized = payee.startswith('Este concepto recoge')

        return {
            'area': policy,
            'programme': None,
            'fc_code': None,  # We don't try (yet) to have foreign keys to existing records
            'ec_code': None,
            'date': datetime.datetime.strptime(line[3].strip(), "%d/%m/%Y"),
            'payee': payee,
            'anonymized': anonymized,
            'description': description,
            # The Excel files are inconsistent, in2csv renders different number formats (?!)
            'amount': self._read_english_number(line[7]) if budget.year==2016 else self._read_spanish_number(line[7])
        }


import openpyxl
import pandas as pd
import streamlit as st
import plotly.express as px
from PIL import Image
import os

st.set_page_config(page_title='SDD Təmir normativləri')
st.header('Təmir materialları 2022-2023')
st.subheader('Təmir materialların dövriyyəsi')

BASE_DIR = os.path.dirname(os.path.abspath(__file__))
excel_file = os.path.join(BASE_DIR, 'Material.xlsx')
image_path = os.path.join(BASE_DIR, 'Train.jpg')
sheet_name = 'Summary'
sheet_name_Norms = 'Normativ'

# Şəkil əlavə etmək
image = Image.open(image_path)
st.image(image,
         caption = "Azərbaycan Dəmir Yolları",
        width = 500)

# Normativlər sütunu
df_Normativ = pd.read_excel(excel_file,
                   sheet_name = sheet_name_Norms,
                   usecols = 'B:K',
                   header = 0)

# Normativ sutununda unique adlari chixarir
Norms = df_Normativ['Təmir Növü'].unique().tolist()
per_loco = df_Normativ['per loco'].unique().tolist()

# 1-39 geder say uzre secim verirem
count_per_loco = st.slider('Bir lokomotivə təmir növündən asılı olaraq lazım olan material sayı: ',
                        min_value = min(per_loco),
                        max_value = max(per_loco),
                        value = (min(per_loco), max(per_loco)))

Normativs_selection = st.multiselect("Təmir növü:",
                                     Norms,
                                     default= Norms)

#  --- Group DATAFRAME based on selection
mask = (df_Normativ['Təmir Növü'].isin(Normativs_selection)) & (df_Normativ['per loco'].between(*count_per_loco))
# isin(Norms))
number_of_result = df_Normativ[mask].shape[0]
st.markdown(f'*Available Results: {number_of_result}*')

# --- Group DATAFRAME  after selection
df_grouped = df_Normativ[mask].groupby(by=['Malın Adı ']).sum()[['per loco']]
df_grouped = df_grouped.rename(columns={'per loco': 'Miqdar'})
df_grouped = df_grouped.reset_index()

bar_chart = px.bar(df_grouped,
                   x = 'Malın Adı ',
                   y = 'Miqdar',
                   text = 'Miqdar',
                   color_discrete_sequence= ['#F63366']*len(df_grouped),
template = "plotly_white")
st.plotly_chart(bar_chart)

# --- Vüsal analiz
df_vusal = pd.read_excel(excel_file,
                                sheet_name=sheet_name,
                                usecols = 'B:L',
                                header=1)

# --- table vüsal cağırır
st.dataframe(df_vusal)

# --- Planlı təmir
df = pd.read_excel(excel_file,
                   sheet_name = sheet_name,
                   usecols = 'O:P',
                   header = 2)

# --- Piechart formasına salmaq (planlı)
pie_chart = px.pie(df,
                   title = 'Planlı təmir',
                   values = 'Tezlik',
                   names= 'Material')

# --- piechart cağırır (planlı)
st.plotly_chart(pie_chart)

#  --- Plandan kənar təmir
df_plandan_kənar = pd.read_excel(excel_file,
                                sheet_name=sheet_name,
                                usecols = 'R:S',
                                header=2)

# --- Piechart formasına salmaq (plandan kənar)
pie_chart_1 = px.pie(df_plandan_kənar,
                   title = 'Plandan kənar təmir',
                   values = 'Tezlik ',
                   names= 'Material ')
# --- piechart cağırır (plandan kənar)
st.plotly_chart(pie_chart_1)

st.dataframe(df_Normativ)


from enum import Enum
from typing import Tuple, List

import torch

from transformers import (
    AutoTokenizer,
    AutoModelForQuestionAnswering,
    AutoConfig,
    PreTrainedModel,
)
from .data_utils import (
    QASample,
    SquadExample,
    QAInputFeatures,
    RawResult,
    read_squad_example,
    convert_qa_example_to_features,
    parse_prediction,
)

class ModelType(Enum):
    """
    Different pretrained models that can be loaded from transformers lib
    """

    BERT_BASE_UNCASED = "bert-base-uncased"
    BERT_BASE_UNCASED_SQUAD = "csarron/bert-base-uncased-squad-v1"
    T5_BASE_QA_QG = "valhalla/t5-base-qa-qg-hl"

"""
    Model loader class for loading pretained transformer models from the transformer lib or custom transformers.
"""

class QAModel:
    """
    Model loader class for loading pretained transformer models from the transformer lib or custom transformers.
    Supports Bert models and Seq2Seq Models (tested on T5ForConditionalGeneration)
    """

    def __init__(
        self,
        head: PreTrainedModel,
        model_type: ModelType = None,
        custom: bool = False,
        model_path=None,
        cache_dir: str = "../cache",
        device: str = "cpu",
    ):
        self.model_type = model_type
        self.model_path = model_path
        self.cache_dir = cache_dir
        self.device = device
        self.head = head

        self._model = self.load_model(custom)
        self._tokenizer = self.load_tokenizer()

    def load_model(self, custom: bool):
        """
        Loads model from path or string tag @ModelType
        """
        config = AutoConfig.from_pretrained(
            self.model_type.value, output_hidden_states=True, cache_dir=self.cache_dir
        )
        if custom:
            pretrained_weights = torch.load(
                self.model_path, map_location=torch.device(self.device)
            )
            model = self.head.from_pretrained(
                self.model_path,
                state_dict=pretrained_weights,
                config=config,
                cache_dir=self.cache_dir,
            )
        else:
            model = self.head.from_pretrained(
                self.model_type.value, config=config, cache_dir=self.cache_dir
            )
        return model

    def load_tokenizer(self):
        return AutoTokenizer.from_pretrained(self.model_type.value)

    def predict(self, sample: QASample):
        """
        Runs prediction on given QA Sample with the loaded model.
        Returns the prediction, hidden states and the input tokens
        """
        squad_formatted_sample: SquadExample = read_squad_example(sample)
        input_features: QAInputFeatures = self.__tokenize(squad_formatted_sample)

        with torch.no_grad():
            inputs = {
                "input_ids": input_features.input_ids,
                "attention_mask": input_features.input_mask,
                "token_type_ids": input_features.segment_ids,
            }

            """
            inputs = {'input_ids': input_features.input_ids,
                      'attention_mask': input_features.input_mask,
                      'decoder_input_ids': input_features.decoder_ids
                      }
            """

            output: Tuple = self._model(**inputs)
            # print(output, output[0][0].shape, output[1][0].shape)
            prediction, hidden_states = self.__parse_model_output(
                output, squad_formatted_sample, input_features
            )

            return prediction, hidden_states, input_features

    def __tokenize(self, input_sample: SquadExample) -> QAInputFeatures:
        """
        Converts QA sample into features for the loaded model
        """
        features = convert_qa_example_to_features(
            example=input_sample,
            tokenizer=self._tokenizer,
            max_seq_length=384,
            doc_stride=128,
            max_query_length=64,
            is_training=False,
        )

        # print(features)

        features.input_ids = torch.tensor([features.input_ids], dtype=torch.long)
        features.decoder_ids = torch.tensor([features.decoder_ids], dtype=torch.long)
        features.input_mask = torch.tensor([features.input_mask], dtype=torch.long)
        features.segment_ids = torch.tensor([features.segment_ids], dtype=torch.long)
        features.cls_index = torch.tensor([features.cls_index], dtype=torch.long)
        features.p_mask = torch.tensor([features.p_mask], dtype=torch.float)

        return features

    def __parse_model_output(
        self, output: Tuple, sample: SquadExample, features: QAInputFeatures
    ) -> Tuple:
        """
        Parse best prediction and hidden states from model output
        """

        def to_list(tensor):
            return tensor.detach().cpu().tolist()

        result: RawResult = RawResult(
            unique_id=1,
            start_logits=to_list(output[0][0]),
            end_logits=to_list(output[1][0]),
        )

        nbest_predictions: List = parse_prediction(sample, features, result)

        return nbest_predictions[0], output[2]


import pymongo
import random
import numpy as np
from datetime import datetime,timedelta
client = pymongo.MongoClient('localhost',27017)
db =  client.btc_auto

class HistoryClient():

    def __init__(self):
        pass
    def get_max_guid(self):
        try:
            res = db.coins.find_one(sort=[('guid',pymongo.DESCENDING)])["guid"]
        except TypeError:
            res = 1
        return res

    def get_max_end_time(self):
        res = db.coins.find_one(sort=[('data.k.T',pymongo.DESCENDING)])["data"]["k"]["T"]
        res = datetime.fromtimestamp(res/1000)
        return res

    def get_min_end_time(self):
        res = db.coins.find_one(sort=[("data.k.T",pymongo.ASCENDING)])['data']['k']['T']
        res = datetime.fromtimestamp(res/1000)
        return res

    def get_data(self,start_time=None,interval_mins=1,total_days=1):
        rows = []
        intervals_in_day = 1440/interval_mins
        num_total_mins = total_days * intervals_in_day * interval_mins
        if start_time is None:
            max_start = (self.get_max_end_time() - timedelta(minutes=num_total_mins)).timestamp()*1000
            min_start = self.get_min_end_time().timestamp()*1000
            start_time = random.randint(min_start,max_start)
            start_time = (datetime.now() - timedelta(days=5)).timestamp()*1000
            start_time = random.normalvariate(start_time,8.64e+7)
        print("interval_mins: {}".format(interval_mins))
        print("interval_in_day: {}".format(intervals_in_day))
        print("start_time: {}".format(datetime.fromtimestamp(start_time/1000)))
        coins_cursor = db.coins.find({"stream_name":"btcusdt@kline_1m"
                                              ,"data.k.T":{"$gt":start_time}})

        print("coins: {}".format(coins_cursor.count()))

        for i,row in enumerate(list(coins_cursor)):
            if i < num_total_mins and i % interval_mins==0:
                rows.append(float(row['data']['k']['o']))
            elif i>num_total_mins:
                break

        return rows

    def to_pct_change(self,array):
        array = list(map(float,array))
        pct_array = []
        for i in range(len(array)):
            if i == 0:
                pct = 0.0
            else:
                pct = ((array[i] - array[i-1])/array[i-1])*100
            pct_array.append(pct)
        return pct_array

if __name__ ==  '__main__':
    h = HistoryClient()
    rows = h.get_data(interval_mins=20)
    pct_rows = h.to_pct_change(rows)
    print(rows[:40])
    print(pct_rows[:40])


import json
import amfm_decompy.pYAAPT as pYAAPT
import amfm_decompy.basic_tools as basic
from multiprocessing import Pool
import os
import sys

lang_id = sys.argv[1]

# Conform to model - this is for HuBERT

def analyze(audio):
    '''
    Run pYAAPT, and return None if error
    '''
    try:
        return list(pYAAPT.yaapt(basic.SignalObj(audio), **{'frame_length' : 25.0, 'frame_space' : 20.0}).values) # Conform parameters to model - this is for HuBERT
    except:
        return None

def make_data(piece):
    '''
    Make data for a specific language.

    When there is no more dev or test data, it will just skip it.
    '''
    try:
        res = {audio_name: analyze(info['wav_path']) for audio_name, info in piece.items() if info['label'] == lang_id}
        return res
    except:
        return {}

data = json.load(open('data.json', 'r'))

cores = int(os.environ['SLURM_JOB_NUM_NODES']) * int(os.environ['SLURM_CPUS_PER_TASK'])
with Pool(cores) as pool:
    output = pool.map(make_data, data.values())
    data['train'] = output[0]
    data['valid'] = output[1]
    data['test'] = output[2]

open(f'data/data_pitch{sys.argv[1]}.json','w').write(json.dumps(data))


# SW Expert Academy 0210_문제풀이2 - 1946

import sys
sys.stdin = open('input.txt', 'r')

T = int(input())
for t in range(1, T + 1):
    N = int(input())
    lst = [input().split() for _ in range(N)]
    sum_val = ''
    for i in range(N):
        sum_val += lst[i][0] * int(lst[i][1])
    print(f'#{t}')

    cnt = 0
    for idx in range(len(sum_val)):
        print(sum_val[idx], end='')
        cnt += 1
        if cnt == 10:
            print()
            cnt = 0
    print()


from rest_framework import serializers

from apps.alerts.models import AlertGroup, ResolutionNote
from common.api_helpers.custom_fields import OrganizationFilteredPrimaryKeyRelatedField, UserIdField
from common.api_helpers.exceptions import BadRequest
from common.api_helpers.mixins import EagerLoadingMixin

class ResolutionNoteSerializer(EagerLoadingMixin, serializers.ModelSerializer):
    id = serializers.CharField(read_only=True, source="public_primary_key")
    alert_group_id = OrganizationFilteredPrimaryKeyRelatedField(
        queryset=AlertGroup.objects,
        source="alert_group",
        filter_field="channel__organization",
    )
    text = serializers.CharField(allow_null=False, source="message_text")
    source = serializers.CharField(read_only=True, source="get_source_display")
    author = UserIdField(read_only=True)

    class Meta:
        model = ResolutionNote
        fields = [
            "id",
            "alert_group_id",
            "author",
            "source",
            "created_at",
            "text",
        ]
        read_only_fields = [
            "created_at",
        ]

    SELECT_RELATED = ["alert_group", "resolution_note_slack_message", "author"]

    def create(self, validated_data):
        validated_data["author"] = self.context["request"].user
        validated_data["source"] = ResolutionNote.Source.WEB
        return super().create(validated_data)

    def to_representation(self, instance):
        result = super().to_representation(instance)
        result["text"] = instance.text
        return result

class ResolutionNoteUpdateSerializer(ResolutionNoteSerializer):
    alert_group_id = serializers.CharField(read_only=True, source="alert_group.public_primary_key")

    def update(self, instance, validated_data):
        if instance.source != ResolutionNote.Source.WEB:
            raise BadRequest(detail="Cannot update message with this source type")
        return super().update(instance, validated_data)


import sqlite3

connect=sqlite3.connect('baza_danych')
connect.row_factory=sqlite3.Row

c=connect.cursor()

c.executescript("""
        DROP TABLE IF EXISTS ksiazka;
        CREATE TABLE IF NOT EXISTS ksiazka (
                id INTEGER PRIMARY KEY ASC,
                tytul varchar(250) NOT NULL,
                autor varchar(250) NOT NULL,
                rok varchar(250) NOT NULL
        )""")

ksiazki = (
        (None, 'Rok 1984', 'George Orwell', '1949')
)
c.execute('INSERT INTO ksiazka VALUES(?, ?, ?, ?);', ksiazki)

def addRow():
        tytul = input('Podaj tytuł książki: ')
        autor = input('Podaj imię i nazwisko autora: ')
        rok = input('Podaj rok wydania: ')
        c.execute('INSERT INTO ksiazka VALUES(NULL, ?, ?, ?);', (tytul, autor, rok))
        connect.commit()

def deleteRow():
        x = input("Podaj ID książki, którą chcesz usunąć. ")
        while True:
                print()
                y = input(f"Czy na pewno chcesz usunąć książkę o id {x} ? t/n ")
                if y == 't':
                        c.execute('DELETE FROM ksiazka WHERE id=?', x)
                        break
                elif y == 'n':
                        break
                else:
                        print("Błąd")

def editRow():
        x = int(input('Który wiersz chcesz edytować? Podaj jego ID. '))
        while True:
                print()
                y = int(input("Który element wiersza chcesz edytować?\n1. Tytuł\n2. Autor\n3. Rok wydania\n0. Koniec edycji\n"))
                if y == 1:
                        tytul = input('Wprowadź nowy tytuł: ')
                        c.execute("UPDATE ksiazka SET tytul=? WHERE id=?", (tytul, x,))
                elif y == 2:
                        autor = input('Wprowadź nowe imię i nazwisko autora: ')
                        c.execute('UPDATE ksiazka SET autor=? WHERE id=?', (autor, x,))
                elif y == 3:
                        rok = input('Wprowadź nowy rok wydania: ')
                        c.execute('UPDATE ksiazka SET rok=? WHERE id=?', (rok, x,))
                elif y == 0:
                        break
                else:
                        print("Błąd")

def showTable():
        c.execute("""
        SELECT * FROM ksiazka;
        """)
        ksiazki = c.fetchall()
        for ksiazka in ksiazki:
                print(ksiazka['id'], ksiazka['tytul'], ksiazka['autor'], ksiazka['rok'])

print("Wybierz opcję, którą ma wykonać program: \n1. Dodać wiersz do tabeli\n2. Usunąć wiersz z tabeli\n3. Wyświetlić dane\n4. Edytować wiersz\n0. Wyjście")
print()
option = int(input("Wybierz opcję od 0 do 4: "))
print()
while option != 0:
        if option == 1:
                addRow()
        elif option == 2:
                deleteRow()
        elif option == 3:
                showTable()
        elif option == 4:
                editRow()
        else:
                print()
                print("Błąd")

        print()
        print("Wybierz opcję, którą ma wykonać program: \n1. Dodać wiersz do tabeli\n2. Usunąć wiersz z tabeli\n3. Wyświetlić dane\n4. Edytować wiersz\n0. Wyjście")
        print()
        option = int(input("Wybierz opcję od 0 do 4: "))
        print()

c.close()
connect.close()